From bc71b14aa8ebf22f4a49b406508485f7a963eb2f Mon Sep 17 00:00:00 2001
From: djh <1810493+djh82@users.noreply.github.com>
Date: Mon, 5 Jul 2021 18:25:42 +0100
Subject: feat: adds minor java improvements
---
CHANGES.txt | 2 +
SCons/Scanner/Java.py | 101 ++++++++++++++++++++++++++++
SCons/Scanner/JavaTests.py | 160 +++++++++++++++++++++++++++++++++++++++++++++
SCons/Tool/__init__.py | 8 ++-
SCons/Tool/javac.py | 1 +
SCons/Tool/javac.xml | 9 ---
SCons/Tool/javacTests.py | 5 ++
7 files changed, 275 insertions(+), 11 deletions(-)
create mode 100644 SCons/Scanner/Java.py
create mode 100644 SCons/Scanner/JavaTests.py
diff --git a/CHANGES.txt b/CHANGES.txt
index c73f345..343d547 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -37,6 +37,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- Fix Issue #3906 - `IMPLICIT_COMMAND_DEPENDENCIES` was not properly disabled when
set to any string value (For example ['none','false','no','off'])
Also previously 'All' wouldn't have the desired affect.
+ - Add JavaScanner to include JAVACLASSPATH as a dependency when using the Java tool.
+ - Fix incorrect Java classpath generation when a NodeList is used as part of any JAVA*PATH variables.
From Ivan Kravets:
- Provide a custom argument escape function for `TempFileMunge` using a new
diff --git a/SCons/Scanner/Java.py b/SCons/Scanner/Java.py
new file mode 100644
index 0000000..4c08b10
--- /dev/null
+++ b/SCons/Scanner/Java.py
@@ -0,0 +1,101 @@
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+
+import SCons.Node
+import SCons.Node.FS
+import SCons.Scanner
+import SCons.Util
+
+
+def _subst_libs(env, libs):
+ """
+ Substitute environment variables and split into list.
+ """
+ if SCons.Util.is_String(libs):
+ libs = env.subst(libs)
+ if SCons.Util.is_String(libs):
+ libs = libs.split()
+ elif SCons.Util.is_Sequence(libs):
+ _libs = []
+ for lib in libs:
+ _libs += _subst_libs(env, lib)
+ libs = _libs
+ else:
+ # libs is an object (Node, for example)
+ libs = [libs]
+ return libs
+
+
+def _collect_classes(list, dirname, files):
+ for fname in files:
+ if os.path.splitext(fname)[1] == ".class":
+ list.append(os.path.join(str(dirname), fname))
+
+
+def scan(node, env, libpath=()):
+ """Scan for files on the JAVACLASSPATH.
+
+ The classpath can contain:
+ - Explicit paths to JAR/Zip files
+ - Wildcards (*)
+ - Directories which contain classes in an unnamed package
+ - Parent directories of the root package for classes in a named package
+
+ Class path entries that are neither directories nor archives (.zip or JAR files) nor the asterisk (*) wildcard character are ignored.
+ """
+ classpath = env.get('JAVACLASSPATH', [])
+ classpath = _subst_libs(env, classpath)
+
+ result = []
+ for path in classpath:
+ if SCons.Util.is_String(path) and "*" in path:
+ libs = env.Glob(path)
+ else:
+ libs = [path]
+
+ for lib in libs:
+ if os.path.isdir(str(lib)):
+ # grab the in-memory nodes
+ env.Dir(lib).walk(_collect_classes, result)
+ # now the on-disk ones
+ for root, dirs, files in os.walk(str(lib)):
+ _collect_classes(result, root, files)
+ else:
+ result.append(lib)
+
+ return list(filter(lambda x: os.path.splitext(str(x))[1] in [".class", ".zip", ".jar"], result))
+
+
+def JavaScanner():
+ return SCons.Scanner.Base(scan, 'JavaScanner',
+ skeys=['.java'])
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/SCons/Scanner/JavaTests.py b/SCons/Scanner/JavaTests.py
new file mode 100644
index 0000000..9fb39ce
--- /dev/null
+++ b/SCons/Scanner/JavaTests.py
@@ -0,0 +1,160 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import unittest
+import collections
+import os
+
+import TestCmd
+
+import SCons.Scanner.Java
+import SCons.Node.FS
+import SCons.Warnings
+
+
+test = TestCmd.TestCmd(workdir = '')
+test.subdir('com')
+
+files = [
+ 'bootclasspath.jar',
+ 'classpath.jar',
+ 'Test.class',
+ 'com/Test.class'
+]
+
+for fname in files:
+ test.write(fname, "\n")
+
+
+class DummyEnvironment(collections.UserDict):
+ def __init__(self,**kw):
+ collections.UserDict.__init__(self)
+ self.data.update(kw)
+ self.fs = SCons.Node.FS.FS(test.workpath(''))
+ self['ENV'] = {}
+
+ def Dictionary(self, *args):
+ return self.data
+
+ def subst(self, strSubst, target=None, source=None, conv=None):
+ if strSubst[0] == '$':
+ return self.data[strSubst[1:]]
+ return strSubst
+
+ def subst_path(self, path, target=None, source=None, conv=None):
+ if not isinstance(path, list):
+ path = [path]
+ return list(map(self.subst, path))
+
+ def has_key(self, key):
+ return key in self.Dictionary()
+
+ def get_calculator(self):
+ return None
+
+ def get_factory(self, factory):
+ return factory or self.fs.File
+
+ def Dir(self, filename):
+ return self.fs.Dir(filename)
+
+ def File(self, filename):
+ return self.fs.File(filename)
+
+ def Glob(self, path):
+ return self.fs.Glob(path)
+
+
+class DummyNode:
+ def __init__(self, name):
+ self.name = name
+
+ def rexists(self):
+ return 1
+
+ def __str__(self):
+ return self.name
+
+
+global my_normpath
+my_normpath = os.path.normpath
+
+if os.path.normcase('foo') == os.path.normcase('FOO'):
+ my_normpath = os.path.normcase
+
+def deps_match(self, deps, headers):
+ scanned = sorted(map(my_normpath, list(map(str, deps))))
+ expect = sorted(map(my_normpath, headers))
+ self.assertTrue(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
+
+
+class JavaScannerEmptyClasspath(unittest.TestCase):
+ def runTest(self):
+ path = []
+ env = DummyEnvironment(JAVASUFFIXES=['.java'],
+ JAVACLASSPATH=path)
+ s = SCons.Scanner.Java.JavaScanner()
+ deps = s(DummyNode('dummy'), env)
+ expected = []
+ deps_match(self, deps, expected)
+
+
+class JavaScannerClasspath(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(JAVASUFFIXES=['.java'],
+ JAVACLASSPATH=[test.workpath('classpath.jar')])
+ s = SCons.Scanner.Java.JavaScanner()
+ deps = s(DummyNode('dummy'), env)
+ expected = ['classpath.jar']
+ deps_match(self, deps, expected)
+
+
+class JavaScannerWildcardClasspath(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(JAVASUFFIXES=['.java'],
+ JAVACLASSPATH=[test.workpath('*')])
+ s = SCons.Scanner.Java.JavaScanner()
+ deps = s(DummyNode('dummy'), env)
+ expected = ['bootclasspath.jar', 'classpath.jar', 'Test.class']
+ deps_match(self, deps, expected)
+
+
+class JavaScannerDirClasspath(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(JAVASUFFIXES=['.java'],
+ JAVACLASSPATH=[test.workpath()])
+ s = SCons.Scanner.Java.JavaScanner()
+ deps = s(DummyNode('dummy'), env)
+ expected = ['Test.class', 'com/Test.class']
+ deps_match(self, deps, expected)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/SCons/Tool/__init__.py b/SCons/Tool/__init__.py
index e78e953..df2e935 100644
--- a/SCons/Tool/__init__.py
+++ b/SCons/Tool/__init__.py
@@ -48,6 +48,7 @@ import SCons.Node.FS
import SCons.Scanner
import SCons.Scanner.C
import SCons.Scanner.D
+import SCons.Scanner.Java
import SCons.Scanner.LaTeX
import SCons.Scanner.Prog
import SCons.Scanner.SWIG
@@ -57,6 +58,7 @@ DefaultToolpath = []
CScanner = SCons.Scanner.C.CScanner()
DScanner = SCons.Scanner.D.DScanner()
+JavaScanner = SCons.Scanner.Java.JavaScanner()
LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner()
PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner()
ProgramScanner = SCons.Scanner.Prog.ProgramScanner()
@@ -521,7 +523,8 @@ def CreateJavaClassFileBuilder(env):
src_suffix='$JAVASUFFIX',
src_builder=['JavaFile'],
target_factory=fs.Entry,
- source_factory=fs.File)
+ source_factory=fs.File,
+ target_scanner=JavaScanner)
env['BUILDERS']['JavaClassFile'] = java_class_file
return java_class_file
@@ -535,7 +538,8 @@ def CreateJavaClassDirBuilder(env):
java_class_dir = SCons.Builder.Builder(action=javac_com,
emitter={},
target_factory=fs.Dir,
- source_factory=fs.Dir)
+ source_factory=fs.Dir,
+ target_scanner=JavaScanner)
env['BUILDERS']['JavaClassDir'] = java_class_dir
return java_class_dir
diff --git a/SCons/Tool/javac.py b/SCons/Tool/javac.py
index fd007eb..98009f9 100644
--- a/SCons/Tool/javac.py
+++ b/SCons/Tool/javac.py
@@ -157,6 +157,7 @@ class pathopt:
default = [default]
path = path + default
if path:
+ path = SCons.Util.flatten(path)
return [self.opt, os.pathsep.join(map(str, path))]
else:
return []
diff --git a/SCons/Tool/javac.xml b/SCons/Tool/javac.xml
index 3ec7fea..eeafb15 100644
--- a/SCons/Tool/javac.xml
+++ b/SCons/Tool/javac.xml
@@ -215,15 +215,6 @@ env = Environment(JAVACCOMSTR="Compiling class files $TARGETS from $SOURCES")
;
on Windows).
-
-
- Note that this currently just adds the specified
- directory via the option.
- &SCons; does not currently search the
- &cv-JAVACLASSPATH; directories for dependency
- .class
- files.
-
diff --git a/SCons/Tool/javacTests.py b/SCons/Tool/javacTests.py
index ff0b8d4..08633d5 100644
--- a/SCons/Tool/javacTests.py
+++ b/SCons/Tool/javacTests.py
@@ -97,6 +97,11 @@ class pathoptTestCase(unittest.TestCase):
['-foopath', '/foo'],
'/foo',
'')
+
+ def test_list_within_list(self):
+ self.assert_pathopt(['-foopath', os.pathsep.join(['/foo','/bar'])],
+ ['/foo', ['/bar']])
+
if __name__ == "__main__":
unittest.main()
--
cgit v0.12
From d1c74bc17b36e4ce4f163a70ff64100b03e9c6f3 Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Fri, 28 Jan 2022 06:10:02 -0700
Subject: Man: mention $$ a subst escape [skip appveyor]
Some rewordings elsewhere in Variable Substitution section -
mainly to a variable that's a function.
Signed-off-by: Mats Wichmann
---
doc/man/scons.xml | 56 ++++++++++++++++++++++++++++++++-----------------------
1 file changed, 33 insertions(+), 23 deletions(-)
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index fc2e24d..eb91d88 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -6414,18 +6414,25 @@ env.Command('marker', 'input_file', action=[MyBuildAction, Touch('$TARGET')])
&scons;
performs variable substitution on the string that makes up
the action part of the builder.
-Variables to be interpolated are indicated in the
-string with a leading
-$, to distinguish them from plain text
+Variables or other text to be substituted are indicated in the
+string by a leading $,
+to distinguish them from plain text
which is not to be substituted.
-The name may be surrounded by curly braces
-(${})
-to separate the name from surrounding characters if necessary.
-Curly braces are required when you use
+The substitutable text may be surrounded by curly braces
+to separate it from surrounding characters if necessary
+(for example ${FOO}BAR).
+To avoid substituting a substring that looks like a variable name,
+escape it with an additional $,
+(for example, $$FOO will be left in the
+final string as $FOO).
+
+
+The curly brace notation is required when you use
Python list subscripting/slicing notation on a variable
to select one or more items from a list,
or access a variable's special attributes,
-or use Python expression substitution.
+or when you use Python expression substitution
+(see below for descriptions of these).
@@ -6670,9 +6677,10 @@ echo Last build occurred . > $TARGET
While &consvars; are normally directly substituted,
if a variable refers to a &consvar;
-whose value is a &Python; function,
-that function is called during substitution.
-Such a function must accept four arguments:
+whose value is a callable &Python; object (a function
+or a class with a __call__ method),
+that object is called during substitution.
+The callable must accept four arguments:
target,
source,
env and
@@ -6681,19 +6689,21 @@ Such a function must accept four arguments:
target is a list of target nodes,
env is the &consenv; to use for context,
and for_signature is
-a Boolean value that tells the function
+a boolean value that tells the callable
if it is being called for the purpose of generating a build signature.
Since the build signature is used for rebuild determination,
-the function should omit variable elements
-that do not affect whether a rebuild should be triggered
-(see $(
-and $)
-above) if for_signature is true.
+variable elements that do not affect whether
+a rebuild should be triggered
+should be omitted from the returned string
+if for_signature is true.
+See $(
+and $) above
+for the syntax.
&SCons; will insert whatever
-the called function returns
+the callable returns
into the expanded string:
@@ -6712,11 +6722,11 @@ will be exactly as it was set: "$FOO baz".
You can use this feature to pass arguments to a
-Python function by creating a callable class
-that stores one or more arguments in an object,
-and then uses them when the
-__call__()
-method is called.
+callable variable by creating a callable class
+that stores passed arguments in the instance,
+and then uses them
+(in the __call__ method)
+when the instance is called.
Note that in this case,
the entire variable expansion must
be enclosed by curly braces
--
cgit v0.12
From 34b62e00d8f0d73c8229d82ce3805f0f72f8380f Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Sat, 29 Jan 2022 09:20:37 -0700
Subject: man: drop some extra wording [ci skip]
An extra stanza was added "or other text" that didn't
really make sense in context, remove it again.
Signed-off-by: Mats Wichmann
---
doc/man/scons.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index eb91d88..13a06a4 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -6414,7 +6414,7 @@ env.Command('marker', 'input_file', action=[MyBuildAction, Touch('$TARGET')])
&scons;
performs variable substitution on the string that makes up
the action part of the builder.
-Variables or other text to be substituted are indicated in the
+Variables to be substituted are indicated in the
string by a leading $,
to distinguish them from plain text
which is not to be substituted.
--
cgit v0.12
From fa23e2ff1f4f4086c9edad2a508d6d6fa281ecb1 Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Wed, 9 Feb 2022 09:39:20 -0700
Subject: Updates to User Guide: 6/Dependencies
Removed most references to md5 and tried to make the terminology
used a bit more consistent.
Added some markup - refernced functions now hyperlinked on
first mention of each section.
Signed-off-by: Mats Wichmann
---
doc/user/depends.xml | 128 ++++++++++++++++++++++++++-------------------------
1 file changed, 65 insertions(+), 63 deletions(-)
diff --git a/doc/user/depends.xml b/doc/user/depends.xml
index 90b446c..f8959a3 100644
--- a/doc/user/depends.xml
+++ b/doc/user/depends.xml
@@ -109,26 +109,26 @@ int main() { printf("Hello, world!\n"); }
things when an input file changes,
so that the built software is up to date.
By default,
- &SCons; keeps track of this through an
- MD5 &signature;, or checksum, of the contents of each file,
+ &SCons; keeps track of this through a
+ content signature,
+ or hash, of the contents of each file,
although you can easily configure
&SCons; to use the
- modification times (or time stamps)
- instead.
- You can even specify your own Python function
- for deciding if an input file has changed.
+ modification times (or time stamps) instead.
+ You can even write your own Python function
+ for deciding if an input file should trigger a rebuild.
- Using MD5 Signatures to Decide if a File Has Changed
+ Using Content Signatures to Decide if a File Has Changed
- By default,
- &SCons; keeps track of whether a file has changed
- based on an MD5 checksum of the file's contents,
- not the file's modification time.
+ By default, &SCons;
+ uses a cryptographic hash of the file's contents,
+ not the file's modification time,
+ to decide whether a file has changed.
This means that you may be surprised by the
default &SCons; behavior if you are used to the
&Make; convention of forcing
@@ -168,31 +168,33 @@ int main() { printf("Hello, world!\n"); }
Note that you can, if you wish,
- specify this default behavior
- (MD5 signatures) explicitly
- using the &Decider; function as follows:
+ specify the default behavior of using
+ content signatures explicitly,
+ using the &f-link-Decider; function as follows:
Program('hello.c')
-Decider('MD5')
+Decider('content')
- You can also use the string 'content'
- as a synonym for 'MD5'
- when calling the &Decider; function.
+ You can also use the string 'MD5'
+ as a synonym for 'content'
+ when calling the &f-Decider; function - this older
+ name is deprecated since &SCons; now supports a
+ choice of hash functions, not just the MD5 function.
- Ramifications of Using MD5 Signatures
+ Ramifications of Using Content Signatures
- Using MD5 signatures to decide if an input file has changed
+ Using content signatures to decide if an input file has changed
has one surprising benefit:
if a source file has been changed
in such a way that the contents of the
@@ -265,7 +267,7 @@ Decider('MD5')
if a source file's modification time is
newer
than the target file.
- To do this, call the &Decider;
+ To do this, call the &f-link-Decider;
function as follows:
@@ -300,7 +302,7 @@ int main() { printf("Hello, world!\n"); }
as the behavior of &Make;,
you can also use the string 'make'
as a synonym for 'timestamp-newer'
- when calling the &Decider; function:
+ when calling the &f-Decider; function:
@@ -335,7 +337,7 @@ Decider('make')
is newer than the target file.
To do this, specify the argument
'timestamp-match'
- when calling the &Decider; function:
+ when calling the &f-Decider; function:
@@ -389,11 +391,11 @@ int main() { printf("Hello, world!\n"); }
As a performance enhancement,
&SCons; provides a way to use
- MD5 checksums of file contents
+ a file's content signature,
but to read those contents
only when the file's timestamp has changed.
- To do this, call the &Decider;
- function with 'MD5-timestamp'
+ To do this, call the &f-link-Decider;
+ function with 'content-timestamp'
argument as follows:
@@ -401,7 +403,7 @@ int main() { printf("Hello, world!\n"); }
Program('hello.c')
-Decider('MD5-timestamp')
+Decider('content-timestamp')
int main() { printf("Hello, world!\n"); }
@@ -411,7 +413,7 @@ int main() { printf("Hello, world!\n"); }
So configured, &SCons; will still behave like
- it does when using Decider('MD5'):
+ it does when using Decider('content'):
@@ -453,7 +455,7 @@ cc -o hello hello.o
will have been performed by simply looking at the
modification time of the &hello_c; file,
not by opening it and performing
- an MD5 checksum calcuation on its contents.
+ a signature calcuation on its contents.
This can significantly speed up many up-to-date builds.
@@ -461,7 +463,7 @@ cc -o hello hello.o
The only drawback to using
- Decider('MD5-timestamp')
+ Decider('content-timestamp')
is that &SCons; will not
rebuild a target file if a source file was modified
within one second of the last time &SCons; built the file.
@@ -475,7 +477,7 @@ cc -o hello hello.o
rely on the ability to apply changes to files
automatically and then rebuild as quickly as possible,
in which case use of
- Decider('MD5-timestamp')
+ Decider('content-timestamp')
may not be appropriate.
@@ -488,7 +490,7 @@ cc -o hello hello.o
The different string values that we've passed to
- the &Decider; function are essentially used by &SCons;
+ the &f-link-Decider; function are essentially used by &SCons;
to pick one of several specific internal functions
that implement various ways of deciding if a dependency
(usually a source file)
@@ -566,13 +568,13 @@ int main() { printf("Hello, world!\n"); }
- .csig
+ csig
- The content signature,
- or MD5 checksum, of the contents of the
- dependency
+ The content signature:
+ a cryptgraphic hash, or checksum, of the file contents
+ of the dependency
file the last time the ⌖ was built.
@@ -580,7 +582,7 @@ int main() { printf("Hello, world!\n"); }
- .size
+ size
@@ -592,7 +594,7 @@ int main() { printf("Hello, world!\n"); }
- .timestamp
+ timestamp
@@ -689,22 +691,21 @@ env.Install("install", "test.txt")
The previous examples have all demonstrated calling
- the global &Decider; function
+ the global &f-link-Decider; function
to configure all dependency decisions that &SCons; makes.
Sometimes, however, you want to be able to configure
different decision-making for different targets.
- When that's necessary, you can use the
- env.Decider
+ When that's necessary, you can use the &f-env-Decider;
method to affect only the configuration
decisions for targets built with a
- specific construction environment.
+ specific &consenv;.
For example, if we arbitrarily want to build
- one program using MD5 checkums
+ one program using content signatures
and another using file modification times
from the same source
we might configure it this way:
@@ -716,7 +717,7 @@ env.Install("install", "test.txt")
env1 = Environment(CPPPATH = ['.'])
env2 = env1.Clone()
env2.Decider('timestamp-match')
-env1.Program('prog-MD5', 'program1.c')
+env1.Program('prog-content', 'program1.c')
env2.Program('prog-timestamp', 'program2.c')
@@ -767,7 +768,7 @@ int main() { printf("Hello, world!\n"); }
-Program('hello.c', CPPPATH = '.')
+Program('hello.c', CPPPATH='.')
#include <hello.h>
@@ -825,8 +826,8 @@ main()
First, notice that &SCons;
- added the -I. argument
- from the &cv-CPPPATH; variable
+ constructed the -I. argument
+ from the '.' in the &cv-CPPPATH; variable
so that the compilation would find the
&hello_h; file in the local directory.
@@ -1104,7 +1105,7 @@ SetOption('implicit_cache', 1)
&SCons; allows you to specific explicitly that one file
depends on another file,
and must be rebuilt whenever that file changes.
- This is specified using the &Depends; method:
+ This is specified using the &f-link-Depends; method:
@@ -1131,7 +1132,7 @@ cc -o hello hello.o
Note that the dependency
- (the second argument to &Depends;)
+ (the second argument to &f-Depends;)
may also be a list of Node objects
(for example, as returned by a call to a Builder):
@@ -1206,7 +1207,7 @@ Program('hello', 'hello.c', CPPPATH='.')
Apparently, the scanner does not know about the header dependency.
- Being not a full-fledged C preprocessor, the scanner does not
+ Note being a full-fledged C preprocessor, the scanner does not
expand the macro.
@@ -1214,7 +1215,7 @@ Program('hello', 'hello.c', CPPPATH='.')
In these cases, you may also use the compiler to extract the
- implicit dependencies. &ParseDepends; can parse the contents of
+ implicit dependencies. &f-link-ParseDepends; can parse the contents of
the compiler output in the style of &Make;, and explicitly
establish all of the listed dependencies.
@@ -1222,7 +1223,7 @@ Program('hello', 'hello.c', CPPPATH='.')
- The following example uses &ParseDepends; to process a compiler
+ The following example uses &f-ParseDepends; to process a compiler
generated dependency file which is generated as a side effect
during compilation of the object file:
@@ -1320,13 +1321,13 @@ scons: `.' is up to date.
- &ParseDepends; immediately reads the specified file at invocation
+ &f-ParseDepends; immediately reads the specified file at invocation
time and just returns if the file does not exist. A dependency
file generated during the build process is not automatically
parsed again. Hence, the compiler-extracted dependencies are not
stored in the signature database during the same build pass. This
- limitation of &ParseDepends; leads to unnecessary recompilations.
- Therefore, &ParseDepends; should only be used if scanners are not
+ limitation of &f-ParseDepends; leads to unnecessary recompilations.
+ Therefore, &f-ParseDepends; should only be used if scanners are not
available for the employed language or not powerful enough for the
specific task.
@@ -1344,7 +1345,8 @@ scons: `.' is up to date.
even if a dependency file changes.
In this case,
you would tell &SCons; specifically
- to ignore a dependency as follows:
+ to ignore a dependency using the
+ &f-link-Ignore; function as follows:
@@ -1414,7 +1416,7 @@ Ignore(hello, '/usr/include/stdio.h')
- &Ignore; can also be used to prevent a generated file from being built
+ &f-Ignore; can also be used to prevent a generated file from being built
by default. This is due to the fact that directories depend on
their contents. So to ignore a generated file from the default build,
you specify that the directory should ignore the generated file.
@@ -1535,7 +1537,7 @@ int main() { printf("Hello, %s! I was built: %s\n", date); }
- One solution is to use the &Requires; function
+ One solution is to use the &f-link-Requires; function
to specify that the version.o
must be rebuilt before it is used by the link step,
but that changes to version.o
@@ -1573,7 +1575,7 @@ int main() { printf("Hello, %s! I was built: %s\n", date); }
we have to find some other way to get it into the link command line.
For this example, we're cheating a bit and stuffing the
object file name (extracted from version_obj
- list returned by the &b-Object; call)
+ list returned by the &b-Object; builder call)
into the &cv-link-LINKFLAGS; variable,
because &cv-LINKFLAGS; is already included
in the &cv-link-LINKCOM; command line.
@@ -1611,8 +1613,8 @@ int main() { printf("Hello, %s! I was built: %s\n", date); }
How &SCons; handles dependencies can also be affected
- by the &AlwaysBuild; method.
- When a file is passed to the &AlwaysBuild; method,
+ by the &f-link-AlwaysBuild; method.
+ When a file is passed to the &f-AlwaysBuild; method,
like so:
@@ -1643,7 +1645,7 @@ int main() { printf("Hello, %s!\n", string); }
- The &AlwaysBuild; function has a somewhat misleading name,
+ The &f-AlwaysBuild; function has a somewhat misleading name,
because it does not actually mean the target file will
be rebuilt every single time &SCons; is invoked.
Instead, it means that the target will, in fact,
@@ -1652,7 +1654,7 @@ int main() { printf("Hello, %s!\n", string); }
the command line (and their dependencies).
So specifying some other target on the command line,
a target that does not
- itself depend on the &AlwaysBuild; target,
+ itself depend on the &f-AlwaysBuild; target,
will still be rebuilt only if it's out-of-date
with respect to its dependencies:
--
cgit v0.12
From f21adf30aaa4412a3524b91dbbbb7681772e705e Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Wed, 9 Feb 2022 11:24:21 -0700
Subject: Fix typo Note -> Not [ci skip]
Signed-off-by: Mats Wichmann
---
doc/user/depends.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/user/depends.xml b/doc/user/depends.xml
index f8959a3..d3f80aa 100644
--- a/doc/user/depends.xml
+++ b/doc/user/depends.xml
@@ -1207,7 +1207,7 @@ Program('hello', 'hello.c', CPPPATH='.')
Apparently, the scanner does not know about the header dependency.
- Note being a full-fledged C preprocessor, the scanner does not
+ Not being a full-fledged C preprocessor, the scanner does not
expand the macro.
--
cgit v0.12
From 610a3026779747c6838cd6638e238231e4e99b27 Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Thu, 10 Feb 2022 07:35:30 -0700
Subject: Use entity for content signature [ci skip]
Signed-off-by: Mats Wichmann
---
RELEASE.txt | 4 ++--
doc/scons.mod | 5 +++--
doc/user/depends.xml | 14 +++++++-------
3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/RELEASE.txt b/RELEASE.txt
index aeffddf..c507dad 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -32,8 +32,6 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
POpen object is properly closed.
- SCons help (-H) no longer prints the "ignored for compatibility" options,
which are still listed in the manpage.
-- Help is now sensitive to the size of the terminal window: the width of the
- help text will scale to wider (or narrower) terminals than 80 characters.
FIXES
@@ -48,6 +46,8 @@ IMPROVEMENTS
- Verify that a user specified msvc script (via MSVC_USE_SCRIPT) exists and raise an
exception immediately when the user specified msvc script does not exist.
- Add cache-debug messages for push failures.
+- Command-line help is now sensitive to the size of the terminal window: the
+ width of the help text will scale for terminals other than 80 chars wide.
PACKAGING
---------
diff --git a/doc/scons.mod b/doc/scons.mod
index 6a2fd6b..5579d4d 100644
--- a/doc/scons.mod
+++ b/doc/scons.mod
@@ -450,8 +450,9 @@
Nodes">
-signature">
-build signature">
+content signature">
+content signatures">
+build signature">
true">
false">
diff --git a/doc/user/depends.xml b/doc/user/depends.xml
index d3f80aa..62b6d91 100644
--- a/doc/user/depends.xml
+++ b/doc/user/depends.xml
@@ -110,7 +110,7 @@ int main() { printf("Hello, world!\n"); }
so that the built software is up to date.
By default,
&SCons; keeps track of this through a
- content signature,
+ &contentsig;,
or hash, of the contents of each file,
although you can easily configure
&SCons; to use the
@@ -169,7 +169,7 @@ int main() { printf("Hello, world!\n"); }
Note that you can, if you wish,
specify the default behavior of using
- content signatures explicitly,
+ &contentsigs; explicitly,
using the &f-link-Decider; function as follows:
@@ -194,7 +194,7 @@ Decider('content')
- Using content signatures to decide if an input file has changed
+ Using &contentsigs; to decide if an input file has changed
has one surprising benefit:
if a source file has been changed
in such a way that the contents of the
@@ -391,7 +391,7 @@ int main() { printf("Hello, world!\n"); }
As a performance enhancement,
&SCons; provides a way to use
- a file's content signature,
+ a file's &contentsig;,
but to read those contents
only when the file's timestamp has changed.
To do this, call the &f-link-Decider;
@@ -552,7 +552,7 @@ int main() { printf("Hello, world!\n"); }
The third argument, prev_ni,
is an object that holds the
- signature or timestamp information
+ &contentsig; and/or timestamp information
that was recorded about the dependency
the last time the target was built.
A prev_ni object can hold
@@ -572,7 +572,7 @@ int main() { printf("Hello, world!\n"); }
- The content signature:
+ The &contentsig;:
a cryptgraphic hash, or checksum, of the file contents
of the dependency
file the last time the ⌖ was built.
@@ -705,7 +705,7 @@ env.Install("install", "test.txt")
For example, if we arbitrarily want to build
- one program using content signatures
+ one program using &contentsigs;
and another using file modification times
from the same source
we might configure it this way:
--
cgit v0.12
From 578b215c262b08d0887be534a09af5d14770b532 Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Thu, 10 Feb 2022 09:32:50 -0700
Subject: Update RELEASE.txt with decider name change [ci skip]
Signed-off-by: Mats Wichmann
---
RELEASE.txt | 3 +++
1 file changed, 3 insertions(+)
diff --git a/RELEASE.txt b/RELEASE.txt
index c507dad..2aac813 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -32,6 +32,9 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
POpen object is properly closed.
- SCons help (-H) no longer prints the "ignored for compatibility" options,
which are still listed in the manpage.
+- The change to "content" and "content-timestamp" Decider names is reflected
+ in the User Guide as well, since the hash function may be other than md5
+ (tidying up from earlier change)
FIXES
--
cgit v0.12
From d8932c2b87cfef617b2412afc6db024ba6e9560a Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Fri, 11 Feb 2022 14:13:05 -0600
Subject: fix up interactive to support ninja scons daemon
---
SCons/Node/__init__.py | 1 +
SCons/Script/Interactive.py | 149 +++++++++++++++++++++++--------------------
SCons/Script/Main.py | 5 +-
SCons/Script/SConsOptions.py | 5 ++
4 files changed, 88 insertions(+), 72 deletions(-)
diff --git a/SCons/Node/__init__.py b/SCons/Node/__init__.py
index ec742a6..d0539b4 100644
--- a/SCons/Node/__init__.py
+++ b/SCons/Node/__init__.py
@@ -105,6 +105,7 @@ SConscriptNodes = set()
# currently used to release parts of a target's info during
# clean builds and update runs (see release_target_info).
interactive = False
+ninja_scons_daemon = False
def is_derived_none(node):
raise NotImplementedError
diff --git a/SCons/Script/Interactive.py b/SCons/Script/Interactive.py
index 26a8bcd..ad021fd 100644
--- a/SCons/Script/Interactive.py
+++ b/SCons/Script/Interactive.py
@@ -188,77 +188,86 @@ version Prints SCons version information.
x.extend(n.alter_targets()[0])
nodes.extend(x)
- # Clean up so that we can perform the next build correctly.
- #
- # We do this by walking over all the children of the targets,
- # and clearing their state.
- #
- # We currently have to re-scan each node to find their
- # children, because built nodes have already been partially
- # cleared and don't remember their children. (In scons
- # 0.96.1 and earlier, this wasn't the case, and we didn't
- # have to re-scan the nodes.)
- #
- # Because we have to re-scan each node, we can't clear the
- # nodes as we walk over them, because we may end up rescanning
- # a cleared node as we scan a later node. Therefore, only
- # store the list of nodes that need to be cleared as we walk
- # the tree, and clear them in a separate pass.
- #
- # XXX: Someone more familiar with the inner workings of scons
- # may be able to point out a more efficient way to do this.
-
- SCons.Script.Main.progress_display("scons: Clearing cached node information ...")
-
- seen_nodes = {}
-
- def get_unseen_children(node, parent, seen_nodes=seen_nodes):
- def is_unseen(node, seen_nodes=seen_nodes):
- return node not in seen_nodes
- return [child for child in node.children(scan=1) if is_unseen(child)]
-
- def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes):
- seen_nodes[node] = 1
-
- # If this file is in a VariantDir and has a
- # corresponding source file in the source tree, remember the
- # node in the source tree, too. This is needed in
- # particular to clear cached implicit dependencies on the
- # source file, since the scanner will scan it if the
- # VariantDir was created with duplicate=0.
- try:
- rfile_method = node.rfile
- except AttributeError:
- return
- else:
- rfile = rfile_method()
- if rfile != node:
- seen_nodes[rfile] = 1
-
- for node in nodes:
- walker = SCons.Node.Walker(node,
- kids_func=get_unseen_children,
- eval_func=add_to_seen_nodes)
- n = walker.get_next()
- while n:
+ if SCons.Node.ninja_scons_daemon:
+ for n in nodes:
+ print(f"Node: {n}, State: {SCons.Node.failed}")
+ if n.get_state() == SCons.Node.failed:
+ print(n)
+ n.clear()
+ n.set_state(SCons.Node.no_state)
+ n.implicit = None
+ else:
+ # Clean up so that we can perform the next build correctly.
+ #
+ # We do this by walking over all the children of the targets,
+ # and clearing their state.
+ #
+ # We currently have to re-scan each node to find their
+ # children, because built nodes have already been partially
+ # cleared and don't remember their children. (In scons
+ # 0.96.1 and earlier, this wasn't the case, and we didn't
+ # have to re-scan the nodes.)
+ #
+ # Because we have to re-scan each node, we can't clear the
+ # nodes as we walk over them, because we may end up rescanning
+ # a cleared node as we scan a later node. Therefore, only
+ # store the list of nodes that need to be cleared as we walk
+ # the tree, and clear them in a separate pass.
+ #
+ # XXX: Someone more familiar with the inner workings of scons
+ # may be able to point out a more efficient way to do this.
+
+ SCons.Script.Main.progress_display("scons: Clearing cached node information ...")
+
+ seen_nodes = {}
+
+ def get_unseen_children(node, parent, seen_nodes=seen_nodes):
+ def is_unseen(node, seen_nodes=seen_nodes):
+ return node not in seen_nodes
+ return [child for child in node.children(scan=1) if is_unseen(child)]
+
+ def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes):
+ seen_nodes[node] = 1
+
+ # If this file is in a VariantDir and has a
+ # corresponding source file in the source tree, remember the
+ # node in the source tree, too. This is needed in
+ # particular to clear cached implicit dependencies on the
+ # source file, since the scanner will scan it if the
+ # VariantDir was created with duplicate=0.
+ try:
+ rfile_method = node.rfile
+ except AttributeError:
+ return
+ else:
+ rfile = rfile_method()
+ if rfile != node:
+ seen_nodes[rfile] = 1
+
+ for node in nodes:
+ walker = SCons.Node.Walker(node,
+ kids_func=get_unseen_children,
+ eval_func=add_to_seen_nodes)
n = walker.get_next()
-
- for node in seen_nodes.keys():
- # Call node.clear() to clear most of the state
- node.clear()
- # node.clear() doesn't reset node.state, so call
- # node.set_state() to reset it manually
- node.set_state(SCons.Node.no_state)
- node.implicit = None
-
- # Debug: Uncomment to verify that all Taskmaster reference
- # counts have been reset to zero.
- #if node.ref_count != 0:
- # from SCons.Debug import Trace
- # Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count))
-
- SCons.SConsign.Reset()
- SCons.Script.Main.progress_display("scons: done clearing node information.")
+ while n:
+ n = walker.get_next()
+
+ for node in seen_nodes.keys():
+ # Call node.clear() to clear most of the state
+ node.clear()
+ # node.clear() doesn't reset node.state, so call
+ # node.set_state() to reset it manually
+ node.set_state(SCons.Node.no_state)
+ node.implicit = None
+
+ # Debug: Uncomment to verify that all Taskmaster reference
+ # counts have been reset to zero.
+ #if node.ref_count != 0:
+ # from SCons.Debug import Trace
+ # Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count))
+
+ SCons.SConsign.Reset()
+ SCons.Script.Main.progress_display("scons: done clearing node information.")
def do_clean(self, argv):
"""\
diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py
index a340f5b..87d57bc 100644
--- a/SCons/Script/Main.py
+++ b/SCons/Script/Main.py
@@ -987,7 +987,8 @@ def _main(parser):
# This would then cause subtle bugs, as already happened in #2971.
if options.interactive:
SCons.Node.interactive = True
-
+ if options.ninja_scons_daemon:
+ SCons.Node.ninja_scons_daemon = True
# That should cover (most of) the options.
# Next, set up the variables that hold command-line arguments,
# so the SConscript files that we read and execute have access to them.
@@ -1125,7 +1126,7 @@ def _main(parser):
platform = SCons.Platform.platform_module()
- if options.interactive:
+ if options.interactive or options.ninja_scons_daemon:
SCons.Script.Interactive.interact(fs, OptionsParser, options,
targets, target_top)
diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py
index e2631fb..133d9ab 100644
--- a/SCons/Script/SConsOptions.py
+++ b/SCons/Script/SConsOptions.py
@@ -858,6 +858,11 @@ def Parser(version):
action="store_true",
help="Run in interactive mode")
+ op.add_option('--ninja-scons-daemon',
+ dest='ninja_scons_daemon', default=False,
+ action="store_true",
+ help="A special interactive mode to support a scons daemon for ninja builds. Intended for use only by the ninja tool.")
+
op.add_option('-j', '--jobs',
nargs=1, type="int",
dest="num_jobs", default=1,
--
cgit v0.12
From 6c8e521f59a796b1bb06870dce9bce093c15e5fe Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Fri, 11 Feb 2022 15:28:19 -0600
Subject: add scons daemon
---
SCons/Tool/ninja/NinjaState.py | 198 ++++++++++++++++---------
SCons/Tool/ninja/Utils.py | 4 +-
SCons/Tool/ninja/__init__.py | 6 +-
SCons/Tool/ninja/ninja_daemon_build.py | 52 +++++++
SCons/Tool/ninja/ninja_run_daemon.py | 71 +++++++++
SCons/Tool/ninja/ninja_scons_daemon.py | 259 +++++++++++++++++++++++++++++++++
test/ninja/force_scons_callback.py | 4 +-
7 files changed, 522 insertions(+), 72 deletions(-)
create mode 100644 SCons/Tool/ninja/ninja_daemon_build.py
create mode 100644 SCons/Tool/ninja/ninja_run_daemon.py
create mode 100644 SCons/Tool/ninja/ninja_scons_daemon.py
diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py
index d7c260e..f2da87a 100644
--- a/SCons/Tool/ninja/NinjaState.py
+++ b/SCons/Tool/ninja/NinjaState.py
@@ -24,11 +24,15 @@
import io
import os
+import pathlib
+import signal
+import tempfile
import shutil
import sys
from os.path import splitext
from tempfile import NamedTemporaryFile
import ninja
+import hashlib
import SCons
from SCons.Script import COMMAND_LINE_TARGETS
@@ -59,7 +63,7 @@ class NinjaState:
os.pardir,
'data',
'bin',
- ninja_bin))
+ ninja_bin))
if not os.path.exists(self.ninja_bin_path):
# couldn't find it, just give the bin name and hope
# its in the path later
@@ -84,6 +88,10 @@ class NinjaState:
# like CCFLAGS
scons_escape = env.get("ESCAPE", lambda x: x)
+ import random
+
+ PORT = str(random.randint(10000, 60000))
+
# if SCons was invoked from python, we expect the first arg to be the scons.py
# script, otherwise scons was invoked from the scons script
python_bin = ''
@@ -184,10 +192,10 @@ class NinjaState:
"restat": 1,
},
"TEMPLATE": {
- "command": "$SCONS_INVOCATION $out",
- "description": "Rendering $SCONS_INVOCATION $out",
- "pool": "scons_pool",
- "restat": 1,
+ "command": f"{sys.executable} {pathlib.Path(__file__).parent / 'ninja_daemon_build.py'} {PORT} {get_path(env.get('NINJA_DIR'))} $out",
+ "description": "Defer to SCons to build $out",
+ "pool": "local_pool",
+ "restat": 1
},
"SCONS": {
"command": "$SCONS_INVOCATION $out",
@@ -211,6 +219,29 @@ class NinjaState:
# output.
"restat": 1,
},
+
+ "SCONS_DAEMON": {
+ "command": f"{sys.executable} {pathlib.Path(__file__).parent / 'ninja_run_daemon.py'} {PORT} {get_path(env.get('NINJA_DIR'))} {str(env.get('NINJA_SCONS_DAEMON_KEEP_ALIVE'))} $SCONS_INVOCATION",
+ "description": "Starting scons daemon...",
+ "pool": "local_pool",
+ # restat
+ # if present, causes Ninja to re-stat the command's outputs
+ # after execution of the command. Each output whose
+ # modification time the command did not change will be
+ # treated as though it had never needed to be built. This
+ # may cause the output's reverse dependencies to be removed
+ # from the list of pending build actions.
+ #
+ # We use restat any time we execute SCons because
+ # SCons calls in Ninja typically create multiple
+ # targets. But since SCons is doing it's own up to
+ # date-ness checks it may only update say one of
+ # them. Restat will find out which of the multiple
+ # build targets did actually change then only rebuild
+ # those targets which depend specifically on that
+ # output.
+ "restat": 1,
+ },
"REGENERATE": {
"command": "$SCONS_INVOCATION_W_TARGETS",
"description": "Regenerating $self",
@@ -245,6 +276,9 @@ class NinjaState:
if not node.has_builder():
return False
+ if isinstance(node, SCons.Node.Python.Value):
+ return False
+
if isinstance(node, SCons.Node.Alias.Alias):
build = alias_to_ninja_build(node)
else:
@@ -256,7 +290,24 @@ class NinjaState:
node_string = str(node)
if node_string in self.builds:
- raise InternalError("Node {} added to ninja build state more than once".format(node_string))
+ warn_msg = f"Alias {node_string} name the same as File node, ninja does not support this. Renaming Alias {node_string} to {node_string}_alias."
+ if isinstance(node, SCons.Node.Alias.Alias):
+ for i, output in enumerate(build["outputs"]):
+ if output == node_string:
+ build["outputs"][i] += "_alias"
+ node_string += "_alias"
+ print(warn_msg)
+ elif self.builds[node_string]["rule"] == "phony":
+ for i, output in enumerate(self.builds[node_string]["outputs"]):
+ if output == node_string:
+ self.builds[node_string]["outputs"][i] += "_alias"
+ tmp_build = self.builds[node_string].copy()
+ del self.builds[node_string]
+ node_string += "_alias"
+ self.builds[node_string] = tmp_build
+ print(warn_msg)
+ else:
+ raise InternalError("Node {} added to ninja build state more than once".format(node_string))
self.builds[node_string] = build
self.built.update(build["outputs"])
return True
@@ -330,8 +381,12 @@ class NinjaState:
)
template_builders = []
+ scons_compiledb = False
for build in [self.builds[key] for key in sorted(self.builds.keys())]:
+ if "compile_commands.json" in build["outputs"]:
+ scons_compiledb = True
+
if build["rule"] == "TEMPLATE":
template_builders.append(build)
continue
@@ -345,10 +400,10 @@ class NinjaState:
# generated sources or else we will create a dependency
# cycle.
if (
- generated_source_files
- and not build["rule"] == "INSTALL"
- and set(build["outputs"]).isdisjoint(generated_source_files)
- and set(build.get("implicit", [])).isdisjoint(generated_source_files)
+ generated_source_files
+ and not build["rule"] == "INSTALL"
+ and set(build["outputs"]).isdisjoint(generated_source_files)
+ and set(build.get("implicit", [])).isdisjoint(generated_source_files)
):
# Make all non-generated source targets depend on
# _generated_sources. We use order_only for generated
@@ -422,41 +477,10 @@ class NinjaState:
ninja.build(**build)
- template_builds = dict()
+ scons_daemon_dirty = str(pathlib.Path(get_path(self.env.get("NINJA_DIR"))) / "scons_daemon_dirty")
for template_builder in template_builders:
-
- # Special handling for outputs and implicit since we need to
- # aggregate not replace for each builder.
- for agg_key in ["outputs", "implicit", "inputs"]:
- new_val = template_builds.get(agg_key, [])
-
- # Use pop so the key is removed and so the update
- # below will not overwrite our aggregated values.
- cur_val = template_builder.pop(agg_key, [])
- if is_List(cur_val):
- new_val += cur_val
- else:
- new_val.append(cur_val)
- template_builds[agg_key] = new_val
-
- # Collect all other keys
- template_builds.update(template_builder)
-
- if template_builds.get("outputs", []):
-
- # Try to clean up any dependency cycles. If we are passing an
- # ouptut node to SCons, it will build any dependencys if ninja
- # has not already.
- for output in template_builds.get("outputs", []):
- inputs = template_builds.get('inputs')
- if inputs and output in inputs:
- inputs.remove(output)
-
- implicits = template_builds.get('implicit')
- if implicits and output in implicits:
- implicits.remove(output)
-
- ninja.build(**template_builds)
+ template_builder["implicit"] += [scons_daemon_dirty]
+ ninja.build(**template_builder)
# We have to glob the SCons files here to teach the ninja file
# how to regenerate itself. We'll never see ourselves in the
@@ -483,30 +507,55 @@ class NinjaState:
}
)
- # If we ever change the name/s of the rules that include
- # compile commands (i.e. something like CC) we will need to
- # update this build to reflect that complete list.
- ninja.build(
- "compile_commands.json",
- rule="CMD",
- pool="console",
- implicit=[str(self.ninja_file)],
- variables={
- "cmd": "{} -f {} -t compdb {}CC CXX > compile_commands.json".format(
- # NINJA_COMPDB_EXPAND - should only be true for ninja
- # This was added to ninja's compdb tool in version 1.9.0 (merged April 2018)
- # https://github.com/ninja-build/ninja/pull/1223
- # TODO: add check in generate to check version and enable this by default if it's available.
- self.ninja_bin_path, str(self.ninja_file),
- '-x ' if self.env.get('NINJA_COMPDB_EXPAND', True) else ''
- )
- },
- )
+ if not scons_compiledb:
+ # If we ever change the name/s of the rules that include
+ # compile commands (i.e. something like CC) we will need to
+ # update this build to reflect that complete list.
+ ninja.build(
+ "compile_commands.json",
+ rule="CMD",
+ pool="console",
+ implicit=[str(self.ninja_file)],
+ variables={
+ "cmd": "{} -f {} -t compdb {}CC CXX > compile_commands.json".format(
+ # NINJA_COMPDB_EXPAND - should only be true for ninja
+ # This was added to ninja's compdb tool in version 1.9.0 (merged April 2018)
+ # https://github.com/ninja-build/ninja/pull/1223
+ # TODO: add check in generate to check version and enable this by default if it's available.
+ self.ninja_bin_path, str(self.ninja_file),
+ '-x ' if self.env.get('NINJA_COMPDB_EXPAND', True) else ''
+ )
+ },
+ )
+
+ ninja.build(
+ "compiledb", rule="phony", implicit=["compile_commands.json"],
+ )
ninja.build(
- "compiledb", rule="phony", implicit=["compile_commands.json"],
+ ["run_scons_daemon", scons_daemon_dirty],
+ rule="SCONS_DAEMON",
)
+ daemon_dir = pathlib.Path(tempfile.gettempdir()) / ('scons_daemon_' + str(hashlib.md5(str(get_path(self.env["NINJA_DIR"])).encode()).hexdigest()))
+ pidfile = None
+ if os.path.exists(scons_daemon_dirty):
+ pidfile = scons_daemon_dirty
+ elif os.path.exists(daemon_dir / 'pidfile'):
+ pidfile = daemon_dir / 'pidfile'
+
+ if pidfile:
+ with open(pidfile) as f:
+ pid = int(f.readline())
+ try:
+ os.kill(pid, signal.SIGINT)
+ except OSError:
+ pass
+
+ if os.path.exists(scons_daemon_dirty):
+ os.unlink(scons_daemon_dirty)
+
+
# Look in SCons's list of DEFAULT_TARGETS, find the ones that
# we generated a ninja build rule for.
scons_default_targets = [
@@ -588,7 +637,13 @@ class SConsToNinjaTranslator:
elif isinstance(action, COMMAND_TYPES):
build = get_command(env, node, action)
else:
- raise Exception("Got an unbuildable ListAction for: {}".format(str(node)))
+ return {
+ "rule": "TEMPLATE",
+ "order_only": get_order_only(node),
+ "outputs": get_outputs(node),
+ "inputs": get_inputs(node),
+ "implicit": get_dependencies(node, skip_sources=True),
+ }
if build is not None:
build["order_only"] = get_order_only(node)
@@ -657,7 +712,7 @@ class SConsToNinjaTranslator:
return results[0]
all_outputs = list({output for build in results for output in build["outputs"]})
- dependencies = list({dep for build in results for dep in build["implicit"]})
+ dependencies = list({dep for build in results for dep in build.get("implicit", [])})
if results[0]["rule"] == "CMD" or results[0]["rule"] == "GENERATED_CMD":
cmdline = ""
@@ -669,6 +724,9 @@ class SConsToNinjaTranslator:
# condition if not cmdstr. So here we strip preceding
# and proceeding whitespace to make strings like the
# above become empty strings and so will be skipped.
+ if not cmd.get("variables") or not cmd["variables"].get("cmd"):
+ continue
+
cmdstr = cmd["variables"]["cmd"].strip()
if not cmdstr:
continue
@@ -717,4 +775,10 @@ class SConsToNinjaTranslator:
"implicit": dependencies,
}
- raise Exception("Unhandled list action with rule: " + results[0]["rule"])
+ return {
+ "rule": "TEMPLATE",
+ "order_only": get_order_only(node),
+ "outputs": get_outputs(node),
+ "inputs": get_inputs(node),
+ "implicit": get_dependencies(node, skip_sources=True),
+ }
diff --git a/SCons/Tool/ninja/Utils.py b/SCons/Tool/ninja/Utils.py
index 3adbb53..888218d 100644
--- a/SCons/Tool/ninja/Utils.py
+++ b/SCons/Tool/ninja/Utils.py
@@ -123,7 +123,7 @@ def get_dependencies(node, skip_sources=False):
get_path(src_file(child))
for child in filter_ninja_nodes(node.children())
if child not in node.sources
- ]
+ ]
return [get_path(src_file(child)) for child in filter_ninja_nodes(node.children())]
@@ -370,7 +370,7 @@ def ninja_contents(original):
"""Return a dummy content without doing IO"""
def wrapper(self):
- if isinstance(self, SCons.Node.Node) and self.is_sconscript():
+ if isinstance(self, SCons.Node.Node) and (self.is_sconscript() or self.is_conftest()):
return original(self)
return bytes("dummy_ninja_contents", encoding="utf-8")
diff --git a/SCons/Tool/ninja/__init__.py b/SCons/Tool/ninja/__init__.py
index 44c2251..3d14faf 100644
--- a/SCons/Tool/ninja/__init__.py
+++ b/SCons/Tool/ninja/__init__.py
@@ -187,6 +187,10 @@ def generate(env):
env["NINJA_ALIAS_NAME"] = env.get("NINJA_ALIAS_NAME", "generate-ninja")
env['NINJA_DIR'] = env.Dir(env.get("NINJA_DIR", '#/.ninja'))
+ env["NINJA_SCONS_DAEMON_KEEP_ALIVE"] = env.get("NINJA_SCONS_DAEMON_KEEP_ALIVE", 180000)
+
+ if GetOption("disable_ninja"):
+ env.SConsignFile(os.path.join(str(env['NINJA_DIR']),'.ninja.sconsign'))
# here we allow multiple environments to construct rules and builds
# into the same ninja file
@@ -423,7 +427,7 @@ def generate(env):
return
if target.check_attributes('ninja_file') is None:
NINJA_STATE.add_build(target)
- else:
+ else:
target.build()
SCons.Taskmaster.Task.execute = ninja_execute
diff --git a/SCons/Tool/ninja/ninja_daemon_build.py b/SCons/Tool/ninja/ninja_daemon_build.py
new file mode 100644
index 0000000..4acd328
--- /dev/null
+++ b/SCons/Tool/ninja/ninja_daemon_build.py
@@ -0,0 +1,52 @@
+import http.client
+import sys
+import time
+import os
+import logging
+import pathlib
+import tempfile
+import hashlib
+import traceback
+
+ninja_builddir = pathlib.Path(sys.argv[2])
+daemon_dir = pathlib.Path(tempfile.gettempdir()) / ('scons_daemon_' + str(hashlib.md5(str(ninja_builddir).encode()).hexdigest()))
+os.makedirs(daemon_dir, exist_ok=True)
+
+logging.basicConfig(
+ filename=daemon_dir / "scons_daemon_request.log",
+ filemode="a",
+ format="%(asctime)s %(message)s",
+ level=logging.DEBUG,
+)
+
+while True:
+ try:
+ logging.debug(f"Sending request: {sys.argv[3]}")
+ conn = http.client.HTTPConnection("127.0.0.1", port=int(sys.argv[1]), timeout=60)
+ conn.request("GET", "/?build=" + sys.argv[3])
+ response = None
+
+ while not response:
+ try:
+ response = conn.getresponse()
+ except (http.client.RemoteDisconnected, http.client.ResponseNotReady):
+ time.sleep(0.01)
+ except http.client.HTTPException as e:
+ logging.debug(f"Error: {traceback.format_exc()}")
+ exit(1)
+ else:
+ msg = response.read()
+ status = response.status
+ if status != 200:
+ print(msg.decode('utf-8'))
+ exit(1)
+ logging.debug(f"Request Done: {sys.argv[3]}")
+ exit(0)
+
+ except ConnectionRefusedError:
+ logging.debug(f"Server not ready: {traceback.format_exc()}")
+ time.sleep(1)
+ except ConnectionResetError:
+ logging.debug(f"Server ConnectionResetError")
+ exit(1)
+
diff --git a/SCons/Tool/ninja/ninja_run_daemon.py b/SCons/Tool/ninja/ninja_run_daemon.py
new file mode 100644
index 0000000..2ab70e6
--- /dev/null
+++ b/SCons/Tool/ninja/ninja_run_daemon.py
@@ -0,0 +1,71 @@
+import subprocess
+import sys
+import os
+import pathlib
+import tempfile
+import hashlib
+import logging
+import time
+import http.client
+import traceback
+
+ninja_builddir = pathlib.Path(sys.argv[2])
+daemon_dir = pathlib.Path(tempfile.gettempdir()) / ('scons_daemon_' + str(hashlib.md5(str(ninja_builddir).encode()).hexdigest()))
+os.makedirs(daemon_dir, exist_ok=True)
+
+logging.basicConfig(
+ filename=daemon_dir / "scons_daemon.log",
+ filemode="a",
+ format="%(asctime)s %(message)s",
+ level=logging.DEBUG,
+)
+
+if not os.path.exists(ninja_builddir / "scons_daemon_dirty"):
+ cmd = [sys.executable, str(pathlib.Path(__file__).parent / "ninja_scons_daemon.py")] + sys.argv[1:]
+ logging.debug(f"Starting daemon with {' '.join(cmd)}")
+
+ p = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=False)
+
+ with open(daemon_dir / "pidfile", "w") as f:
+ f.write(str(p.pid))
+ with open(ninja_builddir / "scons_daemon_dirty", "w") as f:
+ f.write(str(p.pid))
+
+ error_msg = f"ERROR: Failed to connect to scons daemon.\n Check {daemon_dir / 'scons_daemon.log'} for more info.\n"
+
+ while True:
+ try:
+ logging.debug(f"Attempting to connect scons daemon")
+ conn = http.client.HTTPConnection("127.0.0.1", port=int(sys.argv[1]), timeout=60)
+ conn.request("GET", "/?ready=true")
+ response = None
+
+ try:
+ response = conn.getresponse()
+ except (http.client.RemoteDisconnected, http.client.ResponseNotReady):
+ time.sleep(0.01)
+ except http.client.HTTPException as e:
+ logging.debug(f"Error: {traceback.format_exc()}")
+ sys.stderr.write(error_msg)
+ exit(1)
+ else:
+ msg = response.read()
+ status = response.status
+ if status != 200:
+ print(msg.decode('utf-8'))
+ exit(1)
+ logging.debug(f"Request Done: {sys.argv[3]}")
+ break
+
+ except ConnectionRefusedError:
+ logging.debug(f"Server not ready: {sys.argv[3]}")
+ time.sleep(1)
+ except ConnectionResetError:
+ logging.debug(f"Server ConnectionResetError")
+ sys.stderr.write(error_msg)
+ exit(1)
+ except:
+ logging.debug(f"Error: {traceback.format_exc()}")
+ sys.stderr.write(error_msg)
+ exit(1)
+
diff --git a/SCons/Tool/ninja/ninja_scons_daemon.py b/SCons/Tool/ninja/ninja_scons_daemon.py
new file mode 100644
index 0000000..7b839fc
--- /dev/null
+++ b/SCons/Tool/ninja/ninja_scons_daemon.py
@@ -0,0 +1,259 @@
+import http.server
+import socketserver
+from urllib.parse import urlparse, parse_qs
+import time
+from threading import Condition
+from subprocess import PIPE, Popen
+import sys
+import os
+import threading, queue
+import pathlib
+import logging
+from timeit import default_timer as timer
+import traceback
+import tempfile
+import hashlib
+
+port = int(sys.argv[1])
+ninja_builddir = pathlib.Path(sys.argv[2])
+daemon_keep_alive = int(sys.argv[3])
+args = sys.argv[4:]
+
+daemon_dir = pathlib.Path(tempfile.gettempdir()) / ('scons_daemon_' + str(hashlib.md5(str(ninja_builddir).encode()).hexdigest()))
+os.makedirs(daemon_dir, exist_ok=True)
+logging.basicConfig(
+ filename=daemon_dir / "scons_daemon.log",
+ filemode="a",
+ format="%(asctime)s %(message)s",
+ level=logging.DEBUG,
+)
+def daemon_log(message):
+ logging.debug(message)
+
+def custom_readlines(handle, line_separator="\n", chunk_size=1):
+ buf = ""
+ while not handle.closed:
+ data = handle.read(chunk_size)
+ if not data:
+ break
+ buf += data.decode("utf-8")
+ if line_separator in buf:
+ chunks = buf.split(line_separator)
+ buf = chunks.pop()
+ for chunk in chunks:
+ yield chunk + line_separator
+ if buf.endswith("scons>>>"):
+ yield buf
+ buf = ""
+
+def custom_readerr(handle, line_separator="\n", chunk_size=1):
+ buf = ""
+ while not handle.closed:
+ data = handle.read(chunk_size)
+ if not data:
+ break
+ buf += data.decode("utf-8")
+ if line_separator in buf:
+ chunks = buf.split(line_separator)
+ buf = chunks.pop()
+ for chunk in chunks:
+ yield chunk + line_separator
+
+def enqueue_output(out, queue):
+ for line in iter(custom_readlines(out)):
+ queue.put(line)
+ out.close()
+
+def enqueue_error(err, queue):
+ for line in iter(custom_readerr(err)):
+ queue.put(line)
+ err.close()
+
+input_q = queue.Queue()
+output_q = queue.Queue()
+error_q = queue.Queue()
+
+finished_building = []
+error_nodes = []
+
+building_cv = Condition()
+error_cv = Condition()
+
+thread_error = False
+
+def daemon_thread_func():
+ global thread_error
+ global finished_building
+ global error_nodes
+ try:
+ args_list = args + ["--ninja-scons-daemon"]
+ daemon_log(f"Starting daemon with args: {' '.join(args_list)}")
+ daemon_log(f"cwd: {os.getcwd()}")
+
+ p = Popen(args_list, stdout=PIPE, stderr=PIPE, stdin=PIPE)
+
+ t = threading.Thread(target=enqueue_output, args=(p.stdout, output_q))
+ t.daemon = True
+ t.start()
+
+ te = threading.Thread(target=enqueue_error, args=(p.stderr, error_q))
+ te.daemon = True
+ te.start()
+
+ daemon_ready = False
+ building_node = None
+
+ while p.poll() is None:
+
+ while True:
+ try:
+ line = output_q.get(block=False, timeout=0.01)
+ except queue.Empty:
+ break
+ else:
+ daemon_log("output: " + line.strip())
+
+ if "scons: building terminated because of errors." in line:
+ error_output = ""
+ while True:
+ try:
+ error_output += error_q.get(block=False, timeout=0.01)
+ except queue.Empty:
+ break
+ error_nodes += [{'node': building_node, 'error': error_output}]
+ daemon_ready = True
+ building_node = None
+ with building_cv:
+ building_cv.notify()
+
+ elif line == "scons>>>":
+ with error_q.mutex:
+ error_q.queue.clear()
+ daemon_ready = True
+ with building_cv:
+ building_cv.notify()
+ building_node = None
+
+ while daemon_ready and not input_q.empty():
+
+ try:
+ building_node = input_q.get(block=False, timeout=0.01)
+ except queue.Empty:
+ break
+ if "exit" in building_node:
+ p.stdin.write("exit\n".encode("utf-8"))
+ p.stdin.flush()
+ with building_cv:
+ finished_building += [building_node]
+ daemon_ready = False
+ raise
+
+ else:
+ input_command = "build " + building_node + "\n"
+ daemon_log("input: " + input_command.strip())
+
+ p.stdin.write(input_command.encode("utf-8"))
+ p.stdin.flush()
+ with building_cv:
+ finished_building += [building_node]
+ daemon_ready = False
+
+ time.sleep(0.01)
+ except:
+ thread_error = True
+ daemon_log("SERVER ERROR: " + traceback.format_exc())
+ raise
+
+
+daemon_thread = threading.Thread(target=daemon_thread_func)
+daemon_thread.daemon = True
+daemon_thread.start()
+
+logging.debug(f"Starting request server on port {port}, keep alive: {daemon_keep_alive}")
+
+keep_alive_timer = timer()
+httpd = None
+
+
+def server_thread_func():
+
+ class S(http.server.BaseHTTPRequestHandler):
+ def do_GET(self):
+ global thread_error
+ global keep_alive_timer
+ global error_nodes
+
+ try:
+ gets = parse_qs(urlparse(self.path).query)
+ build = gets.get("build")
+ if build:
+ keep_alive_timer = timer()
+
+ daemon_log(f"Got request: {build[0]}")
+ input_q.put(build[0])
+
+ def pred():
+ return build[0] in finished_building
+
+ with building_cv:
+ building_cv.wait_for(pred)
+
+ for error_node in error_nodes:
+ if error_node['node'] == build[0]:
+ self.send_response(500)
+ self.send_header("Content-type", "text/html")
+ self.end_headers()
+ self.wfile.write(error_node['error'].encode())
+ return
+
+ self.send_response(200)
+ self.send_header("Content-type", "text/html")
+ self.end_headers()
+ return
+
+ exitbuild = gets.get("exit")
+ if exitbuild:
+ input_q.put('exit')
+
+ self.send_response(200)
+ self.send_header("Content-type", "text/html")
+ self.end_headers()
+
+ except:
+ thread_error = True
+ daemon_log("SERVER ERROR: " + traceback.format_exc())
+ raise
+
+ def log_message(self, format, *args):
+ return
+
+ httpd = socketserver.TCPServer(("127.0.0.1", port), S)
+ httpd.serve_forever()
+
+server_thread = threading.Thread(target=server_thread_func)
+server_thread.daemon = True
+server_thread.start()
+
+while timer() - keep_alive_timer < daemon_keep_alive and not thread_error:
+ time.sleep(1)
+
+if thread_error:
+ daemon_log(f"Shutting server on port {port} down because thread error.")
+else:
+ daemon_log(f"Shutting server on port {port} down because timed out: {daemon_keep_alive}")
+
+# if there are errors, don't immediatly shutdown the daemon
+# the process which started the server is attempt to connect to
+# the daemon before allowing jobs to start being sent. If the daemon
+# shutsdown too fast, the launchs script will think it has not
+# started yet and sit and wait. If the launch script is able to connect
+# and then the connection is dropped, it will immediatly exit with fail.
+time.sleep(5)
+
+if os.path.exists(ninja_builddir / "scons_daemon_dirty"):
+ os.unlink(ninja_builddir / "scons_daemon_dirty")
+if os.path.exists(daemon_dir / "pidfile"):
+ os.unlink(daemon_dir / "pidfile")
+
+httpd.shutdown()
+server_thread.join()
diff --git a/test/ninja/force_scons_callback.py b/test/ninja/force_scons_callback.py
index 55c12ca..59b54a0 100644
--- a/test/ninja/force_scons_callback.py
+++ b/test/ninja/force_scons_callback.py
@@ -53,7 +53,7 @@ test.run(stdout=None)
test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
test.must_contain_all(test.stdout(), 'Executing:')
test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals())
-if test.stdout().count('scons: Building targets') != 2:
+if test.stdout().count('Defer to SCons to build') != 1:
test.fail_test()
test.must_match('out.txt', 'foo.c' + os.linesep)
test.must_match('out2.txt', "test2.cpp" + os.linesep)
@@ -74,7 +74,7 @@ test.must_not_exist(test.workpath('out2.txt'))
# run ninja independently
program = test.workpath('run_ninja_env.bat') if IS_WINDOWS else ninja_bin
test.run(program=program, stdout=None)
-if test.stdout().count('scons: Building targets') != 1:
+if test.stdout().count('Defer to SCons to build') != 1:
test.fail_test()
test.must_match('out.txt', 'foo.c' + os.linesep)
test.must_match('out2.txt', "test2.cpp" + os.linesep)
--
cgit v0.12
From 77af301f7f40875837602a1245d23d92266b990c Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Mon, 14 Feb 2022 18:08:08 -0800
Subject: Fix issue where only the first interactive 'build' command's results
were being written to the sconsign file
---
SCons/Script/Interactive.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/SCons/Script/Interactive.py b/SCons/Script/Interactive.py
index ad021fd..1015b00 100644
--- a/SCons/Script/Interactive.py
+++ b/SCons/Script/Interactive.py
@@ -266,7 +266,12 @@ version Prints SCons version information.
# from SCons.Debug import Trace
# Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count))
- SCons.SConsign.Reset()
+ # TODO: REMOVE WPD DEBUG 02/14/2022
+ # This call was clearing the list of sconsign files to be written, so it would
+ # only write the results of the first build command. All others wouldn't be written
+ # to .SConsign.
+ # Pretty sure commenting this out is the correct fix.
+ # SCons.SConsign.Reset()
SCons.Script.Main.progress_display("scons: done clearing node information.")
def do_clean(self, argv):
--
cgit v0.12
From a3c6d2ac68aa36e2b1c5976f6a949b92ed428aec Mon Sep 17 00:00:00 2001
From: Zhichang Yu
Date: Fri, 18 Feb 2022 15:41:49 +0800
Subject: MSVC_SCRIPT_ARGS is the arguments (whitespace separated) passed to
the script MSVC_USE_SCRIPT.
---
CHANGES.txt | 3 +++
RELEASE.txt | 1 +
SCons/Tool/MSCommon/vc.py | 5 +++--
SCons/Tool/msvc.xml | 9 +++++++++
4 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 149de73..0411613 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -51,6 +51,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- runtest.py now accepts -j 0 to auto-detect number of usable
processors for testing threads.
+ From Zhichang Yu:
+ - Define MSVC_SCRIPT_ARGS to pass arguments (whitespace separated) to MSVC_USE_SCRIPT.
+
RELEASE 4.3.0 - Tue, 16 Nov 2021 18:12:46 -0700
diff --git a/RELEASE.txt b/RELEASE.txt
index aeffddf..9b75d5d 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -15,6 +15,7 @@ NEW FUNCTIONALITY
-----------------
- List new features (presumably why a checkpoint is being released)
+- Define MSVC_SCRIPT_ARGS to pass arguments (whitespace separated) to MSVC_USE_SCRIPT.
DEPRECATED FUNCTIONALITY
------------------------
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index 4f9f9c6..f3f9a07 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -942,8 +942,9 @@ def msvc_setup_env(env):
use_script = use_script.strip()
if not os.path.exists(use_script):
raise MSVCScriptNotFound('Script specified by MSVC_USE_SCRIPT not found: "{}"'.format(use_script))
- debug('use_script 1 %s', repr(use_script))
- d = script_env(use_script)
+ args = env.get('MSVC_SCRIPT_ARGS', None)
+ debug('use_script 1 %s %s', repr(use_script), repr(args))
+ d = script_env(use_script, args)
elif use_script:
d = msvc_find_valid_batch_script(env,version)
debug('use_script 2 %s', d)
diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml
index 3c6af95..959363d 100644
--- a/SCons/Tool/msvc.xml
+++ b/SCons/Tool/msvc.xml
@@ -395,6 +395,7 @@ and extract the relevant variables from the result (typically
%PATH%) for supplying to the build.
This can be useful to force the use of a compiler version that
&SCons; does not detect.
+MSVC_SCRIPT_ARGS is the arguments (whitespace separated) passed to this script.
@@ -412,6 +413,14 @@ you don't want &SCons; to change anything.
+
+
+
+This is the arguments (whitespace separated) passed to the script MSVC_USE_SCRIPT.
+
+
+
+
-
-
-
-
-
+
+
+
+
+
@@ -105,13 +105,12 @@ See its __doc__ string for a discussion of the format.
SHLINKCOMPROGSUFFIXPRINT_CMD_LINE_FUNC
-
+
-
+
+
+
+
@@ -119,31 +118,41 @@ See its __doc__ string for a discussion of the format.
- &b-Ninja; is a special builder which
- adds a target to create a ninja build file.
+ A special builder which
+ adds a target to create a Ninja build file.
The builder does not require any source files to be specified.
This is an experimental feature. To enable it you must use one of the following methods
-
+
- # On the command line
- --experimental=ninja
+# On the command line
+--experimental=ninja
- # Or in your SConstruct
- SetOption('experimental', 'ninja')
+# Or in your SConstruct
+SetOption('experimental', 'ninja')
This functionality is subject to change and/or removal without deprecation cycle.
-
- To use this tool you must install pypi's ninja
- package.
- This can be done via
- pip install ninja
+ To use this tool you need to install the &Python; &ninja; package,
+ as the tool by default depends on being able to do an
+ import of the package
+ (although see &cv-link-__NINJA_NO;).
+ The can be done via:
+
+# In a virtualenv, or "python" is the native executable:
+python -m pip install ninja
+
+# Windows using Python launcher:
+py -m pip install ninja
+
+# Anaconda:
+conda install -c conda-forge ninja
+
@@ -184,10 +193,10 @@ See its __doc__ string for a discussion of the format.
- The list of source file suffixes which are generated by SCons build steps.
+ The list of source file suffixes which are generated by &SCons; build steps.
All source files which match these suffixes will be added to the _generated_sources alias in the output
- ninja.build file.
- Then all other source files will be made to depend on this in the ninja.build file, forcing the
+ &ninja; build file.
+ Then all other source files will be made to depend on this in the &ninja; build file, forcing the
generated sources to be built first.
@@ -196,9 +205,9 @@ See its __doc__ string for a discussion of the format.
- This propagates directly into the generated ninja.build file.
- From Ninja's docs
- defines the string which should be stripped from msvc’s /showIncludes output
+ A string which propagates directly into the generated &ninja; build file.
+ From Ninja's docs:
+ defines the string which should be stripped from msvc’s output
@@ -206,15 +215,13 @@ See its __doc__ string for a discussion of the format.
- This propagates directly into the generated ninja.build file.
+ A directory name which propagates directly into the generated &ninja; build file.
From Ninja's docs:
-
-
- builddir
- A directory for some Ninja output files. ... (You can also store other build output in this
- directory.)
-
-
+ builddir:
+ A directory for some Ninja output files. ... (You can also store other build output in this
+ directory.)
+
+ The default value is .ninja.
@@ -222,8 +229,8 @@ See its __doc__ string for a discussion of the format.
- A generator function used to create a ninja depsfile which includes all the files which would require
- SCons to be invoked if they change.
+ A generator function used to create a &ninja; depsfile which includes all the files which would require
+ &SCons; to be invoked if they change.
Or a list of said files.
@@ -232,13 +239,14 @@ See its __doc__ string for a discussion of the format.
- Boolean value (True|False) to instruct ninja to expand the command line arguments normally put into
+ Boolean value to instruct &ninja; to expand the command line arguments normally put into
response files.
- This prevents lines in the compilation database like gcc @rsp_file and instead yields
- gcc -c -o myfile.o myfile.c -Ia -DXYZ
+ If true, prevents unexpanded lines in the compilation database like
+ gcc @rsp_file and instead yields expanded lines like
+ gcc -c -o myfile.o myfile.c -Ia -DXYZ.
- Ninja's compdb tool added the -x flag in Ninja V1.9.0
+ Ninja's compdb tool added the flag in Ninja V1.9.0
@@ -247,12 +255,17 @@ See its __doc__ string for a discussion of the format.
A string that sets the environment for any environment variables that
- differ between the OS environment and the SCons command ENV.
+ differ between the OS environment and the &SCons; execution environment.
+
+
It will be compatible with the default shell of the operating system.
+
- If not explicitly specified, SCons will generate this dynamically from the Environment()'s 'ENV'
- env['ENV']
+
+ If not explicitly set, &SCons; will generate this dynamically from the
+ execution environment stored in the current &consenv;
+ (e.g. env['ENV'])
where those values differ from the existing shell..
@@ -261,7 +274,7 @@ See its __doc__ string for a discussion of the format.
- Set the ninja_pool for this or all targets in scope for this env var.
+ Set the ninja_pool for this or all targets in scope for this env var.
@@ -269,11 +282,14 @@ See its __doc__ string for a discussion of the format.
- Boolean (True|False). Default: False
- When True, SCons will not run ninja automatically after creating the ninja.build file.
+ Boolean. Default: False.
+ If true, &SCons; will not run &ninja; automatically after creating the &ninja; build file.
+
- If not set, this will be set to True if --disable_execute_ninja or
- SetOption('disable_execute_ninja', True)
+
+ If not explicitly set, this will be set to True
+ if or
+ SetOption('disable_execute_ninja', True) is seen.
@@ -281,7 +297,7 @@ See its __doc__ string for a discussion of the format.
- Internal flag. Used to tell SCons whether or not to try to import pypi's ninja python package.
+ Internal flag. Used to tell &SCons; whether or not to try to import the &Python; &ninja; module.
This is set to True when being called by Ninja?
@@ -291,8 +307,8 @@ See its __doc__ string for a discussion of the format.
- The filename for the generated Ninja build file defaults to
- ninja.build
+ The filename for the generated Ninja build file.
+ The default is ninja.build.
@@ -300,8 +316,9 @@ See its __doc__ string for a discussion of the format.
- Name of the Alias() which is will cause SCons to create the ninja.build file, and
- then (optionally) run ninja.
+ The name of the alias target which will cause &SCons; to create the &ninja; build file,
+ and then (optionally) run &ninja;.
+ The default value is generate-ninja.
@@ -309,9 +326,9 @@ See its __doc__ string for a discussion of the format.
- Theres also NINJA_SYNTAX which is the path to a custom ninja_syntax.py file which is used in generation.
- The tool currently assumes you have ninja installed through pip, and grabs the syntax file from that
- installation if none specified.
+ The path to a custom ninja_syntax.py file which is used in generation.
+ The tool currently assumes you have &ninja; installed as a &Python; module and grabs the syntax file from that
+ installation if &cv-NINJA_SYNTAX; is not explicitly set.
@@ -319,9 +336,9 @@ See its __doc__ string for a discussion of the format.
- When NINJA_FORCE_SCONS_BUILD is True, this will cause the build nodes to callback to scons instead of using
- ninja to build them. This is intended to be passed to the environment on the builder invocation.
- It is useful if you have a build node which does something which is not easily translated into ninja.
+ If true, causes the build nodes to callback to scons instead of using
+ &ninja; to build them. This is intended to be passed to the environment on the builder invocation.
+ It is useful if you have a build node which does something which is not easily translated into &ninja;.
@@ -330,7 +347,7 @@ See its __doc__ string for a discussion of the format.
Internal value used to specify the function to call with argument env to generate the list of files
- which if changed would require the ninja file to be regenerated.
+ which if changed would require the &ninja; build file to be regenerated.
diff --git a/doc/user/external.xml b/doc/user/external.xml
index 5f88f5a..1d67edd 100644
--- a/doc/user/external.xml
+++ b/doc/user/external.xml
@@ -110,7 +110,7 @@
The entries in this file can be filtered by using
COMPILATIONDB_PATH_FILTER='pattern'
- where the filter pattern is a string following the Python
+ where the filter pattern is a string following the &Python;
fnmatch
@@ -306,54 +306,90 @@
- This is an experimental new feature. It is subject to change and/or removal without depreciation cycle
-
-
- To use this tool you must install pypi's ninja
- package.
- This can be done via
- pip install ninja
+ This is an experimental new feature.
+ It is subject to change and/or removal without a depreciation cycle.
+
- To enable this feature you'll need to use one of the following
+ To enable this feature you'll need to use one of the following:
+
- # On the command line
- --experimental=ninja
+# On the command line
+--experimental=ninja
- # Or in your SConstruct
- SetOption('experimental', 'ninja')
+# Or in your SConstruct
+SetOption('experimental', 'ninja')
-
- This tool will enabled creating a ninja build file from your SCons based build system. It can then invoke
- ninja to run your build. For most builds ninja will be significantly faster, but you may have to give up
- some accuracy. You are NOT advised to use this for production builds. It can however significantly speed up
- your build/debug/compile iterations.
+ Ninja is a small build system that tries to be fast
+ by not making decisions. &SCons; can at times be slow
+ because it makes lots of decisions to carry out its goal
+ of "correctness". The two tools can be paired to benefit
+ some build scenarios: by using the &t-link-ninja; tool,
+ &SCons; can generate the build file &ninja; uses (basically
+ doing the decision-making ahead of time and recording that
+ for &ninja;), and can invoke &ninja; to perform a build.
+ For situations where relationships are not changing, such
+ as edit/build/debug iterations, this works fine and should
+ provide considerable speedups for more complex builds.
+ The implication is if there are larger changes taking place,
+ &ninja; is not as appropriate - but you can always use &SCons;
+ to regenerate the build file. You are NOT advised to use
+ this for production builds.
+
+
+
+ To use the &t-link-ninja; tool you'll need to first install the
+ &Python; &ninja; package, as the tool depends on being able to do an
+ import of the package.
+ This can be done via:
+
+
+# In a virtualenv, or "python" is the native executable:
+python -m pip install ninja
+
+# Windows using Python launcher:
+py -m pip install ninja
+
+# Anaconda:
+conda install -c conda-forge ninja
+
+
- It's not expected that the ninja builder will work for all builds at this point. It's still under active
- development. If you find that your build doesn't work with ninja please bring this to the users mailing list
- or devel channel on our discord server.
+ Reminder that like any non-default tool, you need to initialize it before use
+ (e.g. env.Tool('ninja')).
+
- Specifically if your build has many (or even any) python function actions you may find that the ninja build
- will be slower as it will run ninja, which will then run SCons for each target created by a python action.
- To alleviate some of these, especially those python based actions built into SCons there is special logic to
- implement those actions via shell commands in the ninja build file.
+ It is not expected that the &b-link-Ninja; builder will work for all builds at this point. It is still under active
+ development. If you find that your build doesn't work with &ninja; please bring this to the users mailing list
+ or devel channel on our Discord server.
+
+ Specifically if your build has many (or even any) &Python; function actions you may find that the &ninja; build
+ will be slower as it will run &ninja;, which will then run SCons for each target created by a &Python; action.
+ To alleviate some of these, especially those &Python; based actions built into SCons there is special logic to
+ implement those actions via shell commands in the &ninja; build file.
+
+
+ See:
+
+
+ Ninja Build System
-
-
-
+
+ Ninja File Format Specification
-
+
+
--
cgit v0.12
From 5741258b63df0acd080232fa0bb464b51e7e7282 Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Thu, 24 Feb 2022 14:19:26 -0600
Subject: Added ninja mingw support and improved CommandGeneratorAction support
---
CHANGES.txt | 1 +
RELEASE.txt | 1 +
SCons/Tool/ninja/Methods.py | 3 ++-
SCons/Tool/ninja/__init__.py | 11 ++++++++---
SCons/Util.py | 2 +-
5 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 149de73..7c3062c 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -29,6 +29,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
From Daniel Moody:
- Add cache-debug messages for push failures.
+ - Added ninja mingw support and improved ninja CommandGeneratorAction support.
From Mats Wichmann:
- Tweak the way default site_scons paths on Windows are expressed to
diff --git a/RELEASE.txt b/RELEASE.txt
index aeffddf..b1784ea 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -48,6 +48,7 @@ IMPROVEMENTS
- Verify that a user specified msvc script (via MSVC_USE_SCRIPT) exists and raise an
exception immediately when the user specified msvc script does not exist.
- Add cache-debug messages for push failures.
+- Added ninja mingw support and improved ninja CommandGeneratorAction support.
PACKAGING
---------
diff --git a/SCons/Tool/ninja/Methods.py b/SCons/Tool/ninja/Methods.py
index 073cf71..35cf280 100644
--- a/SCons/Tool/ninja/Methods.py
+++ b/SCons/Tool/ninja/Methods.py
@@ -134,7 +134,7 @@ def get_command(env, node, action): # pylint: disable=too-many-branches
variables = {}
- comstr = get_comstr(sub_env, action, tlist, slist)
+ comstr = str(get_comstr(sub_env, action, tlist, slist))
if not comstr:
return None
@@ -255,6 +255,7 @@ def gen_get_response_file_command(env, rule, tool, tool_is_dynamic=False, custom
)
cmd, rsp_content = cmd_list[:tool_idx], cmd_list[tool_idx:]
+ rsp_content = [rsp_content_item.replace('\\', '/') for rsp_content_item in rsp_content]
rsp_content = ['"' + rsp_content_item + '"' for rsp_content_item in rsp_content]
rsp_content = " ".join(rsp_content)
diff --git a/SCons/Tool/ninja/__init__.py b/SCons/Tool/ninja/__init__.py
index 44c2251..893990d 100644
--- a/SCons/Tool/ninja/__init__.py
+++ b/SCons/Tool/ninja/__init__.py
@@ -214,7 +214,7 @@ def generate(env):
# This adds the required flags such that the generated compile
# commands will create depfiles as appropriate in the Ninja file.
- if env["PLATFORM"] == "win32":
+ if env["PLATFORM"] == "win32" and 'mingw' not in env['TOOLS']:
env.Append(CCFLAGS=["/showIncludes"])
else:
env.Append(CCFLAGS=["-MMD", "-MF", "${TARGET}.d"])
@@ -267,7 +267,12 @@ def generate(env):
def robust_rule_mapping(var, rule, tool):
provider = gen_get_response_file_command(env, rule, tool)
env.NinjaRuleMapping("${" + var + "}", provider)
- env.NinjaRuleMapping(env.get(var, None), provider)
+
+ # some of these construction vars could be generators, e.g.
+ # CommandGeneratorAction, so if the var is not a string, we
+ # can't parse the generated string.
+ if isinstance(env.get(var), str):
+ env.NinjaRuleMapping(env.get(var, None), provider)
robust_rule_mapping("CCCOM", "CC", "$CC")
robust_rule_mapping("SHCCCOM", "CC", "$CC")
@@ -423,7 +428,7 @@ def generate(env):
return
if target.check_attributes('ninja_file') is None:
NINJA_STATE.add_build(target)
- else:
+ else:
target.build()
SCons.Taskmaster.Task.execute = ninja_execute
diff --git a/SCons/Util.py b/SCons/Util.py
index 31202cc..17bb4f2 100644
--- a/SCons/Util.py
+++ b/SCons/Util.py
@@ -1213,7 +1213,7 @@ class CLVar(UserList):
return super().__iadd__(CLVar(other))
def __str__(self):
- return ' '.join(self.data)
+ return ' '.join([str(d) for d in self.data])
class Selector(OrderedDict):
--
cgit v0.12
From 247af9aaee58822b9be66034d3bb9067b2cecda9 Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Thu, 24 Feb 2022 17:28:32 -0700
Subject: Typo fix [ci skip]
Signed-off-by: Mats Wichmann
---
SCons/Tool/ninja/ninja.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SCons/Tool/ninja/ninja.xml b/SCons/Tool/ninja/ninja.xml
index 3510a90..08139d7 100644
--- a/SCons/Tool/ninja/ninja.xml
+++ b/SCons/Tool/ninja/ninja.xml
@@ -142,7 +142,7 @@ SetOption('experimental', 'ninja')
as the tool by default depends on being able to do an
import of the package
(although see &cv-link-__NINJA_NO;).
- The can be done via:
+ This can be done via:
# In a virtualenv, or "python" is the native executable:
python -m pip install ninja
--
cgit v0.12
From 2617860ac856dc081d511f75ef1711fb3375c50f Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Fri, 25 Feb 2022 18:10:47 -0800
Subject: Initial change to use CCDEFFLAGS set by individual compilers rather
than hardcoding those flags for all compilers in the ninja tool itself
---
SCons/Tool/cc.xml | 13 +++++++++++++
SCons/Tool/clang.py | 8 ++++++--
SCons/Tool/clang.xml | 25 ++++++++++++++++++++++++-
SCons/Tool/gcc.py | 3 +++
SCons/Tool/gcc.xml | 1 +
SCons/Tool/gxx.py | 3 +++
SCons/Tool/msvc.py | 3 +++
SCons/Tool/msvc.xml | 1 +
SCons/Tool/ninja/__init__.py | 7 ++++---
SCons/Tool/ninja/ninja.xml | 1 +
10 files changed, 59 insertions(+), 6 deletions(-)
diff --git a/SCons/Tool/cc.xml b/SCons/Tool/cc.xml
index a7d6daa..7c8f944 100644
--- a/SCons/Tool/cc.xml
+++ b/SCons/Tool/cc.xml
@@ -48,6 +48,7 @@ Sets construction variables for generic POSIX C compilers.
SHOBJSUFFIXCFILESUFFIX
+ CCDEPFLAGSPLATFORM
@@ -215,4 +216,16 @@ See also &cv-link-CFLAGS; for compiling to static objects.
+
+
+
+Options to pass to C or C++ compiler to generate list of dependency files.
+
+
+ This is set only by compilers which support this functionality. (&t-link-gcc;, &t-link-clang;, and &t-link-msvc; currently)
+
+
+
+
+
diff --git a/SCons/Tool/clang.py b/SCons/Tool/clang.py
index 6c9227c..8c2f728 100644
--- a/SCons/Tool/clang.py
+++ b/SCons/Tool/clang.py
@@ -43,6 +43,7 @@ from SCons.Tool.MSCommon import msvc_setup_env_once
compilers = ['clang']
+
def generate(env):
"""Add Builders and construction variables for clang to an Environment."""
SCons.Tool.cc.generate(env)
@@ -58,7 +59,6 @@ def generate(env):
# Set-up ms tools paths
msvc_setup_env_once(env)
-
env['CC'] = env.Detect(compilers) or 'clang'
if env['PLATFORM'] in ['cygwin', 'win32']:
env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
@@ -67,7 +67,7 @@ def generate(env):
# determine compiler version
if env['CC']:
- #pipe = SCons.Action._subproc(env, [env['CC'], '-dumpversion'],
+ # pipe = SCons.Action._subproc(env, [env['CC'], '-dumpversion'],
pipe = SCons.Action._subproc(env, [env['CC'], '--version'],
stdin='devnull',
stderr='devnull',
@@ -81,6 +81,10 @@ def generate(env):
if match:
env['CCVERSION'] = match.group(1)
+ env['CCDEPFLAGS'] = '.MMD -MF ${TARGET}.d'
+
+
+
def exists(env):
return env.Detect(compilers)
diff --git a/SCons/Tool/clang.xml b/SCons/Tool/clang.xml
index 2d989fa..8fdd3c1 100644
--- a/SCons/Tool/clang.xml
+++ b/SCons/Tool/clang.xml
@@ -1,6 +1,28 @@
diff --git a/SCons/Tool/ninja/__init__.py b/SCons/Tool/ninja/__init__.py
index 893990d..cc69554 100644
--- a/SCons/Tool/ninja/__init__.py
+++ b/SCons/Tool/ninja/__init__.py
@@ -214,10 +214,11 @@ def generate(env):
# This adds the required flags such that the generated compile
# commands will create depfiles as appropriate in the Ninja file.
- if env["PLATFORM"] == "win32" and 'mingw' not in env['TOOLS']:
- env.Append(CCFLAGS=["/showIncludes"])
+ if 'CCDEPFLAGS' not in env:
+ # Issue some warning here
+ pass
else:
- env.Append(CCFLAGS=["-MMD", "-MF", "${TARGET}.d"])
+ env.Append(CCFLAGS='$CCDEPFLAGS')
env.AddMethod(CheckNinjaCompdbExpand, "CheckNinjaCompdbExpand")
diff --git a/SCons/Tool/ninja/ninja.xml b/SCons/Tool/ninja/ninja.xml
index c5ee15b..f0ca4f8 100644
--- a/SCons/Tool/ninja/ninja.xml
+++ b/SCons/Tool/ninja/ninja.xml
@@ -90,6 +90,7 @@ See its __doc__ string for a discussion of the format.
ARFLAGSCCCCCOM
+ CCDEPFLAGSCCFLAGSCXXCXXCOM
--
cgit v0.12
From 5b855a33291742122709d6242f9017d1c43bbb08 Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Sat, 26 Feb 2022 00:27:24 -0600
Subject: fix a few typos and add tests back
---
SCons/Tool/clang.py | 2 +-
SCons/Tool/clangxx.py | 3 +-
SCons/Tool/gcc.py | 2 +-
SCons/Tool/gxx.py | 2 +-
test/ninja/mingw_command_generator_action.py | 89 ++++++++++++++++++++++
.../sconstruct_mingw_command_generator_action | 7 ++
6 files changed, 101 insertions(+), 4 deletions(-)
create mode 100644 test/ninja/mingw_command_generator_action.py
create mode 100644 test/ninja/ninja_test_sconscripts/sconstruct_mingw_command_generator_action
diff --git a/SCons/Tool/clang.py b/SCons/Tool/clang.py
index 8c2f728..2a12a31 100644
--- a/SCons/Tool/clang.py
+++ b/SCons/Tool/clang.py
@@ -81,7 +81,7 @@ def generate(env):
if match:
env['CCVERSION'] = match.group(1)
- env['CCDEPFLAGS'] = '.MMD -MF ${TARGET}.d'
+ env['CCDEPFLAGS'] = '-MMD -MF ${TARGET}.d'
diff --git a/SCons/Tool/clangxx.py b/SCons/Tool/clangxx.py
index 4443b39..88ba6cc 100644
--- a/SCons/Tool/clangxx.py
+++ b/SCons/Tool/clangxx.py
@@ -51,7 +51,8 @@ def generate(env):
SCons.Tool.cxx.generate(env)
env['CXX'] = env.Detect(compilers) or 'clang++'
-
+ env['CCDEPFLAGS'] = '-MMD -MF ${TARGET}.d'
+
# platform specific settings
if env['PLATFORM'] == 'aix':
env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc')
diff --git a/SCons/Tool/gcc.py b/SCons/Tool/gcc.py
index 0474d6a..94dfad3 100644
--- a/SCons/Tool/gcc.py
+++ b/SCons/Tool/gcc.py
@@ -57,7 +57,7 @@ def generate(env):
if version:
env['CCVERSION'] = version
- env['CCDEPFLAGS'] = '.MMD -MF ${TARGET}.d'
+ env['CCDEPFLAGS'] = '-MMD -MF ${TARGET}.d'
diff --git a/SCons/Tool/gxx.py b/SCons/Tool/gxx.py
index 77ed148..cc93f93 100644
--- a/SCons/Tool/gxx.py
+++ b/SCons/Tool/gxx.py
@@ -64,7 +64,7 @@ def generate(env):
if version:
env['CXXVERSION'] = version
- env['CCDEPFLAGS'] = '.MMD -MF ${TARGET}.d'
+ env['CCDEPFLAGS'] = '-MMD -MF ${TARGET}.d'
diff --git a/test/ninja/mingw_command_generator_action.py b/test/ninja/mingw_command_generator_action.py
new file mode 100644
index 0000000..58c5106
--- /dev/null
+++ b/test/ninja/mingw_command_generator_action.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+import os
+import sys
+
+import TestSCons
+from TestCmd import IS_WINDOWS
+import SCons
+from SCons.Platform.mingw import MINGW_DEFAULT_PATHS
+from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS
+
+test = TestSCons.TestSCons()
+
+if sys.platform not in ('cygwin', 'win32',):
+ test.skip_test("Skipping mingw test on non-Windows platform %s." % sys.platform)
+
+dp = MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS
+gcc = SCons.Tool.find_program_path(test.Environment(), 'gcc', default_paths=dp)
+if not gcc:
+ test.skip_test("Skipping mingw test, no MinGW found.\n")
+
+# ninja must have the os environment setup to work properly
+os.environ["PATH"] += os.pathsep + os.path.dirname(gcc)
+
+try:
+ import ninja
+except ImportError:
+ test.skip_test("Could not find module in python")
+
+_python_ = TestSCons._python_
+_exe = TestSCons._exe
+
+ninja_bin = os.path.abspath(os.path.join(
+ ninja.BIN_DIR,
+ 'ninja' + _exe))
+
+test.dir_fixture('ninja-fixture')
+
+test.file_fixture('ninja_test_sconscripts/sconstruct_mingw_command_generator_action', 'SConstruct')
+
+# generate simple build
+test.run(stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
+test.must_contain_all(test.stdout(), 'Executing:')
+test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals())
+test.run(program=test.workpath('test' + _exe), stdout="library_function")
+
+# clean build and ninja files
+test.run(arguments='-c', stdout=None)
+
+# only generate the ninja file
+test.run(arguments='--disable-execute-ninja', stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
+test.must_not_exist(test.workpath('test' + _exe))
+
+# run ninja independently
+program = test.workpath('run_ninja_env.bat') if IS_WINDOWS else ninja_bin
+test.run(program=program, stdout=None)
+test.run(program=test.workpath('test' + _exe), stdout="library_function")
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/ninja/ninja_test_sconscripts/sconstruct_mingw_command_generator_action b/test/ninja/ninja_test_sconscripts/sconstruct_mingw_command_generator_action
new file mode 100644
index 0000000..e3fcfe2
--- /dev/null
+++ b/test/ninja/ninja_test_sconscripts/sconstruct_mingw_command_generator_action
@@ -0,0 +1,7 @@
+SetOption('experimental','ninja')
+DefaultEnvironment(tools=[])
+
+env = Environment(tools=['mingw'])
+env.Tool('ninja')
+dll = env.SharedLibrary(target='test_impl', source='test_impl.c')
+env.Program(target='test', source='test1.c', LIBS=[dll])
\ No newline at end of file
--
cgit v0.12
From c9bd964430508256a246b3af00358e4fcd42f714 Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Sat, 26 Feb 2022 00:53:38 -0600
Subject: Add mingw setup for windows
---
.github/workflows/experimental_tests.yml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/.github/workflows/experimental_tests.yml b/.github/workflows/experimental_tests.yml
index aac28d0..3672144 100644
--- a/.github/workflows/experimental_tests.yml
+++ b/.github/workflows/experimental_tests.yml
@@ -30,6 +30,12 @@ jobs:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
+ - name: Set up MinGW
+ uses: egor-tensin/setup-mingw@v2
+ if: matrix.os == 'windows-latest'
+ with:
+ platform: x64
+
- name: Set up Python 3.8 ${{ matrix.os }}
uses: actions/setup-python@v2
with:
--
cgit v0.12
From bcf17d9bd1d223472a67870a41bb10fe15b28814 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Sat, 26 Feb 2022 19:11:40 -0800
Subject: fix typo in gcc/clang CCDEFLAGS
---
SCons/Tool/clang.py | 2 +-
SCons/Tool/clangxx.py | 3 +++
SCons/Tool/gcc.py | 2 +-
SCons/Tool/gxx.py | 2 +-
4 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/SCons/Tool/clang.py b/SCons/Tool/clang.py
index 8c2f728..2a12a31 100644
--- a/SCons/Tool/clang.py
+++ b/SCons/Tool/clang.py
@@ -81,7 +81,7 @@ def generate(env):
if match:
env['CCVERSION'] = match.group(1)
- env['CCDEPFLAGS'] = '.MMD -MF ${TARGET}.d'
+ env['CCDEPFLAGS'] = '-MMD -MF ${TARGET}.d'
diff --git a/SCons/Tool/clangxx.py b/SCons/Tool/clangxx.py
index 4443b39..a78dc6c 100644
--- a/SCons/Tool/clangxx.py
+++ b/SCons/Tool/clangxx.py
@@ -89,6 +89,9 @@ def generate(env):
if match:
env['CXXVERSION'] = match.group(1)
+ env['CCDEPFLAGS'] = '-MMD -MF ${TARGET}.d'
+
+
def exists(env):
return env.Detect(compilers)
diff --git a/SCons/Tool/gcc.py b/SCons/Tool/gcc.py
index 0474d6a..94dfad3 100644
--- a/SCons/Tool/gcc.py
+++ b/SCons/Tool/gcc.py
@@ -57,7 +57,7 @@ def generate(env):
if version:
env['CCVERSION'] = version
- env['CCDEPFLAGS'] = '.MMD -MF ${TARGET}.d'
+ env['CCDEPFLAGS'] = '-MMD -MF ${TARGET}.d'
diff --git a/SCons/Tool/gxx.py b/SCons/Tool/gxx.py
index 77ed148..cc93f93 100644
--- a/SCons/Tool/gxx.py
+++ b/SCons/Tool/gxx.py
@@ -64,7 +64,7 @@ def generate(env):
if version:
env['CXXVERSION'] = version
- env['CCDEPFLAGS'] = '.MMD -MF ${TARGET}.d'
+ env['CCDEPFLAGS'] = '-MMD -MF ${TARGET}.d'
--
cgit v0.12
From b27c096277ab49f046a5b934d1a938e9ed5e4325 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Sun, 27 Feb 2022 20:48:18 -0800
Subject: fix duplicate CCDEPFLAGS def in clangxx tool
---
SCons/Tool/clangxx.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/SCons/Tool/clangxx.py b/SCons/Tool/clangxx.py
index 775e943..a78dc6c 100644
--- a/SCons/Tool/clangxx.py
+++ b/SCons/Tool/clangxx.py
@@ -51,8 +51,7 @@ def generate(env):
SCons.Tool.cxx.generate(env)
env['CXX'] = env.Detect(compilers) or 'clang++'
- env['CCDEPFLAGS'] = '-MMD -MF ${TARGET}.d'
-
+
# platform specific settings
if env['PLATFORM'] == 'aix':
env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc')
--
cgit v0.12
From 5353f82282315a347dd3a7ee212adbb82d42b7ba Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Mon, 28 Feb 2022 08:03:00 -0700
Subject: Ninja docs (#4105): address some review comments
Signed-off-by: Mats Wichmann
---
SCons/Tool/ninja/ninja.xml | 18 +++++++-----------
doc/user/external.xml | 2 +-
2 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/SCons/Tool/ninja/ninja.xml b/SCons/Tool/ninja/ninja.xml
index 08139d7..f9a44ae 100644
--- a/SCons/Tool/ninja/ninja.xml
+++ b/SCons/Tool/ninja/ninja.xml
@@ -144,14 +144,7 @@ SetOption('experimental', 'ninja')
(although see &cv-link-__NINJA_NO;).
This can be done via:
-# In a virtualenv, or "python" is the native executable:
python -m pip install ninja
-
-# Windows using Python launcher:
-py -m pip install ninja
-
-# Anaconda:
-conda install -c conda-forge ninja
@@ -205,7 +198,8 @@ conda install -c conda-forge ninja
- A string which propagates directly into the generated &ninja; build file.
+ The msvc_deps_prefix string.
+ Propagates directly into the generated &ninja; build file.
From Ninja's docs:
defines the string which should be stripped from msvc’s output
@@ -215,9 +209,10 @@ conda install -c conda-forge ninja
- A directory name which propagates directly into the generated &ninja; build file.
+ The builddir value.
+ Propagates directly into the generated &ninja; build file.
From Ninja's docs:
- builddir:
+
A directory for some Ninja output files. ... (You can also store other build output in this
directory.)
@@ -229,7 +224,8 @@ conda install -c conda-forge ninja
- A generator function used to create a &ninja; depsfile which includes all the files which would require
+ A generator function used to create a &ninja; depfile which
+ includes all the files which would require
&SCons; to be invoked if they change.
Or a list of said files.
diff --git a/doc/user/external.xml b/doc/user/external.xml
index 1d67edd..b483196 100644
--- a/doc/user/external.xml
+++ b/doc/user/external.xml
@@ -367,7 +367,7 @@ conda install -c conda-forge ninja
It is not expected that the &b-link-Ninja; builder will work for all builds at this point. It is still under active
development. If you find that your build doesn't work with &ninja; please bring this to the users mailing list
- or devel channel on our Discord server.
+ or #scons-help channel on our Discord server.
--
cgit v0.12
From 2e94a5bc02f4e2f873e881a7a95f4c6093025258 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Tue, 1 Mar 2022 20:27:06 -0800
Subject: hide __NINJA_NO for now
---
SCons/Tool/ninja/ninja.xml | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/SCons/Tool/ninja/ninja.xml b/SCons/Tool/ninja/ninja.xml
index f9a44ae..4638efb 100644
--- a/SCons/Tool/ninja/ninja.xml
+++ b/SCons/Tool/ninja/ninja.xml
@@ -71,7 +71,7 @@ See its __doc__ string for a discussion of the format.
NINJA_SYNTAXNINJA_FORCE_SCONS_BUILD_NINJA_REGENERATE_DEPS_FUNC
- __NINJA_NO
+
IMPLICIT_COMMAND_DEPENDENCIES
@@ -141,7 +141,7 @@ SetOption('experimental', 'ninja')
To use this tool you need to install the &Python; &ninja; package,
as the tool by default depends on being able to do an
import of the package
- (although see &cv-link-__NINJA_NO;).
+
This can be done via:
python -m pip install ninja
@@ -290,6 +290,7 @@ python -m pip install ninja
+
--
cgit v0.12
From 306b34fa44d175730f9fbc191c9a63aed313aeec Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Fri, 11 Feb 2022 12:34:23 -0700
Subject: Updates to User Guide: 22/Caching [skip appveyor]
Use entities. Adopt the "derived-file cache" terminology used
elsewhere instead of the former wording "shared cache".
Also added entity references for content/build sigs to manpage,
as well as some other entity fiddling.
CacheDir entry now mentions SCons doesn't do cache maintenance.
Signed-off-by: Mats Wichmann
---
SCons/Environment.xml | 20 ++++---
doc/man/scons.xml | 60 +++++++++----------
doc/user/caching.xml | 155 +++++++++++++++++++++++++++++---------------------
3 files changed, 133 insertions(+), 102 deletions(-)
diff --git a/SCons/Environment.xml b/SCons/Environment.xml
index f471866..d314711 100644
--- a/SCons/Environment.xml
+++ b/SCons/Environment.xml
@@ -107,8 +107,7 @@ to the commands executed
to build target files,
you must do so explicitly.
A common example is
-the system
-PATH
+the system &PATH;
environment variable,
so that
&scons;
@@ -547,7 +546,7 @@ and/or suffix,
so the contents are treated as a list of strings, that is,
adding a string will result in a separate string entry,
not a combined string. For &cv-CPPDEFINES; as well as
-for &cv-link-LIBS;, and the various *PATH
+for &cv-link-LIBS;, and the various *PATH;
variables, &SCons; will supply the compiler-specific
syntax (e.g. adding a -D or /D
prefix for &cv-CPPDEFINES;), so this syntax should be omitted when
@@ -625,7 +624,7 @@ do not make sense and a &Python; exception will be raised.
When using &f-env-Append; to modify &consvars;
which are path specifications (conventionally,
-the names of such end in PATH),
+the names of such end in PATH),
it is recommended to add the values as a list of strings,
even if there is only a single string to add.
The same goes for adding library names to &cv-LIBS;.
@@ -812,7 +811,7 @@ is being used and
&scons;
finds a derived file that needs to be rebuilt,
it will first look in the cache to see if a
-file with matching build signature exists
+file with matching &buildsig; exists
(indicating the input file(s) and build action(s)
were identical to those for the current target),
and if so, will retrieve the file from the cache.
@@ -824,7 +823,7 @@ If the derived file is not present in the cache,
&scons;
will build it and
then place a copy of the built file in the cache,
-identified by its build signature, for future use.
+identified by its &buildsig;, for future use.
@@ -881,6 +880,13 @@ method can be used to disable caching of specific files. This can be
useful if inputs and/or outputs of some tool are impossible to
predict or prohibitively large.
+
+
+Note that (at this time) &SCons; provides no facilities
+for managing the derived-file cache. It is up to the developer
+to arrange for cache pruning, expiry, etc. if needed.
+
+
@@ -1324,7 +1330,7 @@ was built.
This can be consulted to match various
file characteristics
such as the timestamp,
-size, or content signature.
+size, or &contentsig;.
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index fc2e24d..047f230 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -1165,7 +1165,7 @@ the help message not to be displayed.
-Set the block size used when computing content signatures to
+Set the block size used when computing &contentsigs; to
KILOBYTES.
This value determines the size of the chunks which are read in at once when
computing signature hashes. Files below that size are fully stored in memory
@@ -1187,8 +1187,8 @@ be appropriate for most uses.Set the hashing algorithm used by SCons to
ALGORITHM.
-This value determines the hashing algorithm used in generating content
-signatures or &f-link-CacheDir; keys.
+This value determines the hashing algorithm used in generating
+&contentsigs; or &CacheDir; keys.The supported list of values are: md5, sha1, and sha256.
However, the Python interpreter used to run SCons must have the corresponding
@@ -1272,8 +1272,7 @@ but with the following limitations:&scons;
will not detect changes to implicit dependency search paths
-(e.g.
-CPPPATH, LIBPATH)
+(e.g. &cv-link-CPPPATH;, &cv-link-LIBPATH;)
that would ordinarily
cause different versions of same-named files to be used.
@@ -1281,8 +1280,7 @@ cause different versions of same-named files to be used.
will miss changes in the implicit dependencies
in cases where a new implicit
dependency is added earlier in the implicit dependency search path
-(e.g.
-CPPPATH, LIBPATH)
+(e.g. &cv-link-CPPPATH;, &cv-link-LIBPATH;)
than a current implicit dependency with the same name.
@@ -1551,16 +1549,16 @@ targets specified on the command line will still be processed.
Set the maximum expected drift in the modification time of files to
SECONDS.
This value determines how long a file must be unmodified
-before its cached content signature
+before its cached &contentsig;
will be used instead of
-calculating a new content signature (hash)
+calculating a new &contentsig; (hash)
of the file's contents.
The default value is 2 days, which means a file must have a
modification time of at least two days ago in order to have its
-cached content signature used.
-A negative value means to never cache the content
-signature and to ignore the cached value if there already is one. A value
-of 0 means to always use the cached signature,
+cached &contentsig; used.
+A negative value means to never cache the
+&contentsig; and to ignore the cached value if there already is one.
+A value of 0 means to always use the cached signature,
no matter how old the file is.
@@ -2408,11 +2406,11 @@ env = Environment(parse_flags='-Iinclude -DEBUG -lm')
This example adds 'include' to
-the CPPPATH &consvar;,
+the &cv-link-CPPPATH; &consvar;,
'EBUG' to
-CPPDEFINES,
+&cv-link-CPPDEFINES;,
and 'm' to
-LIBS.
+&cv-link-LIBS;.
@@ -2574,7 +2572,7 @@ or if tools includes 'default',
then &scons; will auto-detect usable tools,
using the execution environment value of PATH
(that is, env['ENV']['PATH'] -
-the external evironment PATH from os.environ
+the external evironment &PATH; from os.environ
is not used)
for looking up any backing programs, and the platform name in effect
to determine the default tools for that platform.
@@ -2856,11 +2854,12 @@ env.Program('hello', 'hello.c', parse_flags='-Iinclude -DEBUG -lm')
This example adds 'include' to
-CPPPATH,
+the &cv-link-CPPPATH; &consvar;,
'EBUG' to
-CPPDEFINES,
+&cv-link-CPPDEFINES;,
and 'm' to
-LIBS.
+&cv-link-LIBS;.
+Although the builder methods defined by
&scons;
@@ -5546,9 +5545,9 @@ must accept four arguments:
env is the &consenv; to use for context,
and for_signature is
a Boolean value that tells the function
-if it is being called for the purpose of generating a build signature
+if it is being called for the purpose of generating a &buildsig;
(as opposed to actually executing the command).
-Since the build signature is used for rebuild determination,
+Since the &buildsig; is used for rebuild determination,
the function should omit those elements
that do not affect whether a rebuild should be triggered
if for_signature is true.
@@ -5974,6 +5973,7 @@ l = Action(build_it, '$STRINGIT')
Any additional positional arguments, if present,
may either be &consvars; or lists of &consvars;
whose values will be included in the signature of the Action
+(the &buildsig;)
when deciding whether a target should be rebuilt because the action changed.
Such variables may also be specified using the
varlist
@@ -6637,14 +6637,14 @@ may be used to surround parts of a command line
that may change
without
causing a rebuild--that is,
-which are not included in the signature
+which are not included in the &buildsig;
of target files built with this command.
All text between
$(
and
$)
will be removed from the command line
-before it is added to the build action signature,
+before it is added to the &buildsig;
and the
$(
and
@@ -6662,7 +6662,9 @@ echo Last build occurred $( $TODAY $). > $TARGET
echo Last build occurred $TODAY. > $TARGET
-but the command signature added to any target files would be:
+but the command portion of the
+the &buildsig; computed for any target files built
+by this action would be:
echo Last build occurred . > $TARGET
@@ -6682,8 +6684,8 @@ Such a function must accept four arguments:
env is the &consenv; to use for context,
and for_signature is
a Boolean value that tells the function
-if it is being called for the purpose of generating a build signature.
-Since the build signature is used for rebuild determination,
+if it is being called for the purpose of generating a &buildsig;.
+Since the &buildsig; is used for rebuild determination,
the function should omit variable elements
that do not affect whether a rebuild should be triggered
(see $(
@@ -6921,7 +6923,7 @@ directories when generating the dependency Nodes. To illustrate this,
a C language source file may contain a line like
#include "foo.h". However, there is no guarantee
that foo.h exists in the current directory:
-the contents of &cv-CPPPATH; is passed to the C preprocessor which
+the contents of &cv-link-CPPPATH; is passed to the C preprocessor which
will look in those places for the header,
so the scanner function needs to look in those places as well
in order to build Nodes with correct paths.
@@ -7201,7 +7203,7 @@ to vary its initialization.Returns True if the tool can
be called in the context of env.
Usually this means looking up one or more
-known programs using the PATH from the
+known programs using the PATH from the
supplied env, but the tool can
make the "exists" decision in any way it chooses.
diff --git a/doc/user/caching.xml b/doc/user/caching.xml
index c7b3842..69368d7 100644
--- a/doc/user/caching.xml
+++ b/doc/user/caching.xml
@@ -22,7 +22,9 @@
@@ -335,5 +336,14 @@ See its __doc__ string for a discussion of the format.
+
+
+
+ The number of seconds for the SCons deamon launched by ninja to stay alive.
+ (Default: 180000)
+
+
+
+
diff --git a/SCons/Tool/ninja/ninja_daemon_build.py b/SCons/Tool/ninja/ninja_daemon_build.py
index af0e6b4..6e1c7be 100644
--- a/SCons/Tool/ninja/ninja_daemon_build.py
+++ b/SCons/Tool/ninja/ninja_daemon_build.py
@@ -87,4 +87,3 @@ while True:
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=4 shiftwidth=4:
-
diff --git a/SCons/Tool/ninja/ninja_scons_daemon.py b/SCons/Tool/ninja/ninja_scons_daemon.py
index d644fe9..2b13993 100644
--- a/SCons/Tool/ninja/ninja_scons_daemon.py
+++ b/SCons/Tool/ninja/ninja_scons_daemon.py
@@ -306,4 +306,3 @@ server_thread.join()
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=4 shiftwidth=4:
-
--
cgit v0.12
From 3c7a7395d80f97f4e0b2f8d038b2a09bcbeb9e60 Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Mon, 14 Mar 2022 15:24:16 -0500
Subject: remove scons-ninja-daemon option in favor of interactive
---
SCons/Node/__init__.py | 1 -
SCons/Script/Interactive.py | 161 ++++++++++++++++-----------------
SCons/Script/Main.py | 4 +-
SCons/Script/SConsOptions.py | 5 -
SCons/Tool/ninja/NinjaState.py | 8 +-
SCons/Tool/ninja/ninja_scons_daemon.py | 2 +-
6 files changed, 82 insertions(+), 99 deletions(-)
diff --git a/SCons/Node/__init__.py b/SCons/Node/__init__.py
index d0539b4..ec742a6 100644
--- a/SCons/Node/__init__.py
+++ b/SCons/Node/__init__.py
@@ -105,7 +105,6 @@ SConscriptNodes = set()
# currently used to release parts of a target's info during
# clean builds and update runs (see release_target_info).
interactive = False
-ninja_scons_daemon = False
def is_derived_none(node):
raise NotImplementedError
diff --git a/SCons/Script/Interactive.py b/SCons/Script/Interactive.py
index 1015b00..d694740 100644
--- a/SCons/Script/Interactive.py
+++ b/SCons/Script/Interactive.py
@@ -29,7 +29,7 @@
# of its own, which might or might not be a good thing. Nevertheless,
# here are some enhancements that will probably be requested some day
# and are worth keeping in mind (assuming this takes off):
-#
+#
# - A command to re-read / re-load the SConscript files. This may
# involve allowing people to specify command-line options (e.g. -f,
# -I, --no-site-dir) that affect how the SConscript files are read.
@@ -188,91 +188,82 @@ version Prints SCons version information.
x.extend(n.alter_targets()[0])
nodes.extend(x)
- if SCons.Node.ninja_scons_daemon:
- for n in nodes:
- print(f"Node: {n}, State: {SCons.Node.failed}")
- if n.get_state() == SCons.Node.failed:
- print(n)
- n.clear()
- n.set_state(SCons.Node.no_state)
- n.implicit = None
- else:
- # Clean up so that we can perform the next build correctly.
- #
- # We do this by walking over all the children of the targets,
- # and clearing their state.
- #
- # We currently have to re-scan each node to find their
- # children, because built nodes have already been partially
- # cleared and don't remember their children. (In scons
- # 0.96.1 and earlier, this wasn't the case, and we didn't
- # have to re-scan the nodes.)
- #
- # Because we have to re-scan each node, we can't clear the
- # nodes as we walk over them, because we may end up rescanning
- # a cleared node as we scan a later node. Therefore, only
- # store the list of nodes that need to be cleared as we walk
- # the tree, and clear them in a separate pass.
- #
- # XXX: Someone more familiar with the inner workings of scons
- # may be able to point out a more efficient way to do this.
-
- SCons.Script.Main.progress_display("scons: Clearing cached node information ...")
-
- seen_nodes = {}
-
- def get_unseen_children(node, parent, seen_nodes=seen_nodes):
- def is_unseen(node, seen_nodes=seen_nodes):
- return node not in seen_nodes
- return [child for child in node.children(scan=1) if is_unseen(child)]
-
- def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes):
- seen_nodes[node] = 1
-
- # If this file is in a VariantDir and has a
- # corresponding source file in the source tree, remember the
- # node in the source tree, too. This is needed in
- # particular to clear cached implicit dependencies on the
- # source file, since the scanner will scan it if the
- # VariantDir was created with duplicate=0.
- try:
- rfile_method = node.rfile
- except AttributeError:
- return
- else:
- rfile = rfile_method()
- if rfile != node:
- seen_nodes[rfile] = 1
-
- for node in nodes:
- walker = SCons.Node.Walker(node,
- kids_func=get_unseen_children,
- eval_func=add_to_seen_nodes)
+ # Clean up so that we can perform the next build correctly.
+ #
+ # We do this by walking over all the children of the targets,
+ # and clearing their state.
+ #
+ # We currently have to re-scan each node to find their
+ # children, because built nodes have already been partially
+ # cleared and don't remember their children. (In scons
+ # 0.96.1 and earlier, this wasn't the case, and we didn't
+ # have to re-scan the nodes.)
+ #
+ # Because we have to re-scan each node, we can't clear the
+ # nodes as we walk over them, because we may end up rescanning
+ # a cleared node as we scan a later node. Therefore, only
+ # store the list of nodes that need to be cleared as we walk
+ # the tree, and clear them in a separate pass.
+ #
+ # XXX: Someone more familiar with the inner workings of scons
+ # may be able to point out a more efficient way to do this.
+
+ SCons.Script.Main.progress_display("scons: Clearing cached node information ...")
+
+ seen_nodes = {}
+
+ def get_unseen_children(node, parent, seen_nodes=seen_nodes):
+ def is_unseen(node, seen_nodes=seen_nodes):
+ return node not in seen_nodes
+ return [child for child in node.children(scan=1) if is_unseen(child)]
+
+ def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes):
+ seen_nodes[node] = 1
+
+ # If this file is in a VariantDir and has a
+ # corresponding source file in the source tree, remember the
+ # node in the source tree, too. This is needed in
+ # particular to clear cached implicit dependencies on the
+ # source file, since the scanner will scan it if the
+ # VariantDir was created with duplicate=0.
+ try:
+ rfile_method = node.rfile
+ except AttributeError:
+ return
+ else:
+ rfile = rfile_method()
+ if rfile != node:
+ seen_nodes[rfile] = 1
+
+ for node in nodes:
+ walker = SCons.Node.Walker(node,
+ kids_func=get_unseen_children,
+ eval_func=add_to_seen_nodes)
+ n = walker.get_next()
+ while n:
n = walker.get_next()
- while n:
- n = walker.get_next()
-
- for node in seen_nodes.keys():
- # Call node.clear() to clear most of the state
- node.clear()
- # node.clear() doesn't reset node.state, so call
- # node.set_state() to reset it manually
- node.set_state(SCons.Node.no_state)
- node.implicit = None
-
- # Debug: Uncomment to verify that all Taskmaster reference
- # counts have been reset to zero.
- #if node.ref_count != 0:
- # from SCons.Debug import Trace
- # Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count))
-
- # TODO: REMOVE WPD DEBUG 02/14/2022
- # This call was clearing the list of sconsign files to be written, so it would
- # only write the results of the first build command. All others wouldn't be written
- # to .SConsign.
- # Pretty sure commenting this out is the correct fix.
- # SCons.SConsign.Reset()
- SCons.Script.Main.progress_display("scons: done clearing node information.")
+
+ for node in seen_nodes.keys():
+ # Call node.clear() to clear most of the state
+ node.clear()
+ # node.clear() doesn't reset node.state, so call
+ # node.set_state() to reset it manually
+ node.set_state(SCons.Node.no_state)
+ node.implicit = None
+
+ # Debug: Uncomment to verify that all Taskmaster reference
+ # counts have been reset to zero.
+ #if node.ref_count != 0:
+ # from SCons.Debug import Trace
+ # Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count))
+
+ # TODO: REMOVE WPD DEBUG 02/14/2022
+ # This call was clearing the list of sconsign files to be written, so it would
+ # only write the results of the first build command. All others wouldn't be written
+ # to .SConsign.
+ # Pretty sure commenting this out is the correct fix.
+ # SCons.SConsign.Reset()
+ SCons.Script.Main.progress_display("scons: done clearing node information.")
def do_clean(self, argv):
"""\
diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py
index 87d57bc..ab2dc0e 100644
--- a/SCons/Script/Main.py
+++ b/SCons/Script/Main.py
@@ -987,8 +987,6 @@ def _main(parser):
# This would then cause subtle bugs, as already happened in #2971.
if options.interactive:
SCons.Node.interactive = True
- if options.ninja_scons_daemon:
- SCons.Node.ninja_scons_daemon = True
# That should cover (most of) the options.
# Next, set up the variables that hold command-line arguments,
# so the SConscript files that we read and execute have access to them.
@@ -1126,7 +1124,7 @@ def _main(parser):
platform = SCons.Platform.platform_module()
- if options.interactive or options.ninja_scons_daemon:
+ if options.interactive:
SCons.Script.Interactive.interact(fs, OptionsParser, options,
targets, target_top)
diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py
index 133d9ab..e2631fb 100644
--- a/SCons/Script/SConsOptions.py
+++ b/SCons/Script/SConsOptions.py
@@ -858,11 +858,6 @@ def Parser(version):
action="store_true",
help="Run in interactive mode")
- op.add_option('--ninja-scons-daemon',
- dest='ninja_scons_daemon', default=False,
- action="store_true",
- help="A special interactive mode to support a scons daemon for ninja builds. Intended for use only by the ninja tool.")
-
op.add_option('-j', '--jobs',
nargs=1, type="int",
dest="num_jobs", default=1,
diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py
index b6de65c..3ef3bb2 100644
--- a/SCons/Tool/ninja/NinjaState.py
+++ b/SCons/Tool/ninja/NinjaState.py
@@ -63,7 +63,7 @@ class NinjaState:
os.pardir,
'data',
'bin',
- ninja_bin))
+ ninja_bin))
if not os.path.exists(self.ninja_bin_path):
# couldn't find it, just give the bin name and hope
# its in the path later
@@ -219,7 +219,7 @@ class NinjaState:
# output.
"restat": 1,
},
-
+
"SCONS_DAEMON": {
"command": f"{sys.executable} {pathlib.Path(__file__).parent / 'ninja_run_daemon.py'} {PORT} {get_path(env.get('NINJA_DIR'))} {str(env.get('NINJA_SCONS_DAEMON_KEEP_ALIVE'))} $SCONS_INVOCATION",
"description": "Starting scons daemon...",
@@ -551,8 +551,8 @@ class NinjaState:
os.kill(pid, signal.SIGINT)
except OSError:
pass
-
- if os.path.exists(scons_daemon_dirty):
+
+ if os.path.exists(scons_daemon_dirty):
os.unlink(scons_daemon_dirty)
diff --git a/SCons/Tool/ninja/ninja_scons_daemon.py b/SCons/Tool/ninja/ninja_scons_daemon.py
index d644fe9..69d984d 100644
--- a/SCons/Tool/ninja/ninja_scons_daemon.py
+++ b/SCons/Tool/ninja/ninja_scons_daemon.py
@@ -124,7 +124,7 @@ def daemon_thread_func():
global finished_building
global error_nodes
try:
- args_list = args + ["--ninja-scons-daemon"]
+ args_list = args + ["--interactive"]
daemon_log(f"Starting daemon with args: {' '.join(args_list)}")
daemon_log(f"cwd: {os.getcwd()}")
--
cgit v0.12
From d382175ba00943cbc488a648e973f3e38162397e Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Mon, 14 Mar 2022 13:27:49 -0700
Subject: [ci skip] Add comment/TODO to code to handle Alias() name conflicts
with real files/directories
---
SCons/Tool/ninja/NinjaState.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py
index 3ef3bb2..b1d7fdd 100644
--- a/SCons/Tool/ninja/NinjaState.py
+++ b/SCons/Tool/ninja/NinjaState.py
@@ -290,6 +290,10 @@ class NinjaState:
node_string = str(node)
if node_string in self.builds:
+ # TODO: If we work out a way to handle Alias() with same name as file this logic can be removed
+ # This works around adding Alias with the same name as a Node.
+ # It's not great way to workaround because it force renames the alias,
+ # but the alternative is broken ninja support.
warn_msg = f"Alias {node_string} name the same as File node, ninja does not support this. Renaming Alias {node_string} to {node_string}_alias."
if isinstance(node, SCons.Node.Alias.Alias):
for i, output in enumerate(build["outputs"]):
--
cgit v0.12
From e3cbfd6ae3986513148872308e2ddd0584364da4 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Mon, 14 Mar 2022 14:01:46 -0700
Subject: Allow explicitly setting the daemon port with NINJA_SCONS_DAEMON_PORT
---
SCons/Tool/ninja/NinjaState.py | 9 +++------
SCons/Tool/ninja/__init__.py | 2 ++
SCons/Tool/ninja/ninja.xml | 11 +++++++++++
3 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py
index b1d7fdd..11e928d 100644
--- a/SCons/Tool/ninja/NinjaState.py
+++ b/SCons/Tool/ninja/NinjaState.py
@@ -87,10 +87,7 @@ class NinjaState:
# to make the SCONS_INVOCATION variable properly quoted for things
# like CCFLAGS
scons_escape = env.get("ESCAPE", lambda x: x)
-
- import random
-
- PORT = str(random.randint(10000, 60000))
+ scons_daemon_port = int(env.get('NINJA_SCONS_DAEMON_PORT',-1))
# if SCons was invoked from python, we expect the first arg to be the scons.py
# script, otherwise scons was invoked from the scons script
@@ -192,7 +189,7 @@ class NinjaState:
"restat": 1,
},
"TEMPLATE": {
- "command": f"{sys.executable} {pathlib.Path(__file__).parent / 'ninja_daemon_build.py'} {PORT} {get_path(env.get('NINJA_DIR'))} $out",
+ "command": f"{sys.executable} {pathlib.Path(__file__).parent / 'ninja_daemon_build.py'} {scons_daemon_port} {get_path(env.get('NINJA_DIR'))} $out",
"description": "Defer to SCons to build $out",
"pool": "local_pool",
"restat": 1
@@ -221,7 +218,7 @@ class NinjaState:
},
"SCONS_DAEMON": {
- "command": f"{sys.executable} {pathlib.Path(__file__).parent / 'ninja_run_daemon.py'} {PORT} {get_path(env.get('NINJA_DIR'))} {str(env.get('NINJA_SCONS_DAEMON_KEEP_ALIVE'))} $SCONS_INVOCATION",
+ "command": f"{sys.executable} {pathlib.Path(__file__).parent / 'ninja_run_daemon.py'} {scons_daemon_port} {get_path(env.get('NINJA_DIR'))} {str(env.get('NINJA_SCONS_DAEMON_KEEP_ALIVE'))} $SCONS_INVOCATION",
"description": "Starting scons daemon...",
"pool": "local_pool",
# restat
diff --git a/SCons/Tool/ninja/__init__.py b/SCons/Tool/ninja/__init__.py
index 3d14faf..9fe9dd4 100644
--- a/SCons/Tool/ninja/__init__.py
+++ b/SCons/Tool/ninja/__init__.py
@@ -26,6 +26,7 @@
import importlib
import os
+import random
import subprocess
import sys
@@ -188,6 +189,7 @@ def generate(env):
env["NINJA_ALIAS_NAME"] = env.get("NINJA_ALIAS_NAME", "generate-ninja")
env['NINJA_DIR'] = env.Dir(env.get("NINJA_DIR", '#/.ninja'))
env["NINJA_SCONS_DAEMON_KEEP_ALIVE"] = env.get("NINJA_SCONS_DAEMON_KEEP_ALIVE", 180000)
+ env["NINJA_SCONS_DAEMON_PORT"] = env.get('NINJA_SCONS_DAEMON_PORT', random.randint(10000,60000))
if GetOption("disable_ninja"):
env.SConsignFile(os.path.join(str(env['NINJA_DIR']),'.ninja.sconsign'))
diff --git a/SCons/Tool/ninja/ninja.xml b/SCons/Tool/ninja/ninja.xml
index b023e86..b497156 100644
--- a/SCons/Tool/ninja/ninja.xml
+++ b/SCons/Tool/ninja/ninja.xml
@@ -74,6 +74,7 @@ See its __doc__ string for a discussion of the format.
__NINJA_NOIMPLICIT_COMMAND_DEPENDENCIESNINJA_SCONS_DAEMON_KEEP_ALIVE
+ NINJA_SCONS_DAEMON_PORT
@@ -345,5 +346,15 @@ See its __doc__ string for a discussion of the format.
+
+
+
+ The TCP/IP port for the SCons daemon to listen on.
+ NOTE: You cannot use a port already being listened to on your build machine.
+ (Default: random number between 10000,60000)
+
+
+
+
--
cgit v0.12
From 53a9b7cc192bc589a28ef2f73ee6b8428c1b1549 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Mon, 14 Mar 2022 14:27:24 -0700
Subject: update test/ninja/force_scons_callback.py to test using
NINJA_SCONS_DAEMON_PORT to explicitly set the daemon port
---
SCons/Tool/ninja/__init__.py | 2 +-
test/ninja/force_scons_callback.py | 57 ++++++++++++----------
.../sconstruct_force_scons_callback | 11 +++--
3 files changed, 38 insertions(+), 32 deletions(-)
diff --git a/SCons/Tool/ninja/__init__.py b/SCons/Tool/ninja/__init__.py
index 9fe9dd4..913738a 100644
--- a/SCons/Tool/ninja/__init__.py
+++ b/SCons/Tool/ninja/__init__.py
@@ -189,7 +189,7 @@ def generate(env):
env["NINJA_ALIAS_NAME"] = env.get("NINJA_ALIAS_NAME", "generate-ninja")
env['NINJA_DIR'] = env.Dir(env.get("NINJA_DIR", '#/.ninja'))
env["NINJA_SCONS_DAEMON_KEEP_ALIVE"] = env.get("NINJA_SCONS_DAEMON_KEEP_ALIVE", 180000)
- env["NINJA_SCONS_DAEMON_PORT"] = env.get('NINJA_SCONS_DAEMON_PORT', random.randint(10000,60000))
+ env["NINJA_SCONS_DAEMON_PORT"] = env.get('NINJA_SCONS_DAEMON_PORT', random.randint(10000, 60000))
if GetOption("disable_ninja"):
env.SConsignFile(os.path.join(str(env['NINJA_DIR']),'.ninja.sconsign'))
diff --git a/test/ninja/force_scons_callback.py b/test/ninja/force_scons_callback.py
index 59b54a0..c99ed58 100644
--- a/test/ninja/force_scons_callback.py
+++ b/test/ninja/force_scons_callback.py
@@ -37,47 +37,50 @@ except ImportError:
_python_ = TestSCons._python_
_exe = TestSCons._exe
-ninja_bin = os.path.abspath(os.path.join(
- ninja.__file__,
- os.pardir,
- 'data',
- 'bin',
- 'ninja' + _exe))
+ninja_bin = os.path.abspath(
+ os.path.join(ninja.__file__, os.pardir, "data", "bin", "ninja" + _exe)
+)
-test.dir_fixture('ninja-fixture')
+test.dir_fixture("ninja-fixture")
-test.file_fixture('ninja_test_sconscripts/sconstruct_force_scons_callback', 'SConstruct')
+test.file_fixture(
+ "ninja_test_sconscripts/sconstruct_force_scons_callback", "SConstruct"
+)
# generate simple build
test.run(stdout=None)
-test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
-test.must_contain_all(test.stdout(), 'Executing:')
-test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals())
-if test.stdout().count('Defer to SCons to build') != 1:
+test.must_contain_all_lines(test.stdout(), ["Generating: build.ninja"])
+test.must_contain_all(test.stdout(), "Executing:")
+test.must_contain_all(test.stdout(), "ninja%(_exe)s -f" % locals())
+if test.stdout().count("Defer to SCons to build") != 1:
test.fail_test()
-test.must_match('out.txt', 'foo.c' + os.linesep)
-test.must_match('out2.txt', "test2.cpp" + os.linesep)
+test.must_match("out.txt", "foo.c" + os.linesep)
+test.must_match("out2.txt", "test2.cpp" + os.linesep)
# clean build and ninja files
-test.run(arguments='-c', stdout=None)
-test.must_contain_all_lines(test.stdout(), [
- 'Removed out.txt',
- 'Removed out2.txt',
- 'Removed build.ninja'])
+test.run(arguments="-c", stdout=None)
+test.must_contain_all_lines(
+ test.stdout(), ["Removed out.txt", "Removed out2.txt", "Removed build.ninja"]
+)
# only generate the ninja file
-test.run(arguments='--disable-execute-ninja', stdout=None)
-test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
-test.must_not_exist(test.workpath('out.txt'))
-test.must_not_exist(test.workpath('out2.txt'))
+test.run(arguments="--disable-execute-ninja", stdout=None)
+test.must_contain_all_lines(test.stdout(), ["Generating: build.ninja"])
+test.must_not_exist(test.workpath("out.txt"))
+test.must_not_exist(test.workpath("out2.txt"))
# run ninja independently
-program = test.workpath('run_ninja_env.bat') if IS_WINDOWS else ninja_bin
+program = test.workpath("run_ninja_env.bat") if IS_WINDOWS else ninja_bin
test.run(program=program, stdout=None)
-if test.stdout().count('Defer to SCons to build') != 1:
+if test.stdout().count("Defer to SCons to build") != 1:
test.fail_test()
-test.must_match('out.txt', 'foo.c' + os.linesep)
-test.must_match('out2.txt', "test2.cpp" + os.linesep)
+test.must_match("out.txt", "foo.c" + os.linesep)
+test.must_match("out2.txt", "test2.cpp" + os.linesep)
+
+# only generate the ninja file with specific NINJA_SCONS_DAEMON_PORT
+test.run(arguments="PORT=9999 --disable-execute-ninja", stdout=None)
+# Verify that port # propagates to call to ninja_run_daemon.py
+test.must_contain(test.workpath("build.ninja"), "ninja_run_daemon.py 9999")
test.pass_test()
diff --git a/test/ninja/ninja_test_sconscripts/sconstruct_force_scons_callback b/test/ninja/ninja_test_sconscripts/sconstruct_force_scons_callback
index 55729a6..ef3562b 100644
--- a/test/ninja/ninja_test_sconscripts/sconstruct_force_scons_callback
+++ b/test/ninja/ninja_test_sconscripts/sconstruct_force_scons_callback
@@ -1,8 +1,11 @@
-SetOption('experimental','ninja')
+SetOption("experimental", "ninja")
DefaultEnvironment(tools=[])
env = Environment(tools=[])
-env.Tool('ninja')
+daemon_port = ARGUMENTS.get("PORT", False)
+if daemon_port:
+ env["NINJA_SCONS_DAEMON_PORT"] = int(daemon_port)
+env.Tool("ninja")
-env.Command('out.txt', 'foo.c', 'echo $SOURCE> $TARGET', NINJA_FORCE_SCONS_BUILD=True)
-env.Command('out2.txt', 'test2.cpp', 'echo $SOURCE> $TARGET')
\ No newline at end of file
+env.Command("out.txt", "foo.c", "echo $SOURCE> $TARGET", NINJA_FORCE_SCONS_BUILD=True)
+env.Command("out2.txt", "test2.cpp", "echo $SOURCE> $TARGET")
--
cgit v0.12
From d19f74f8be81e8b1a18033afbb66726674311f6f Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Mon, 14 Mar 2022 14:38:46 -0700
Subject: [ci skip] Updated CHANGES/RELEASE
---
CHANGES.txt | 6 ++++++
RELEASE.txt | 9 +++++++--
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 149de73..e2b26e4 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -29,6 +29,12 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
From Daniel Moody:
- Add cache-debug messages for push failures.
+ - Ninja: Changed generated build.ninja file to run SCons only build Actions via
+ a SCons Deamon. Added logic for starting and connecting to SCons daemon (currently
+ only used for ninja)
+ - Ninja: Fix issue where Configure files weren't being properly processed when build run
+ via ninja.
+
From Mats Wichmann:
- Tweak the way default site_scons paths on Windows are expressed to
diff --git a/RELEASE.txt b/RELEASE.txt
index aeffddf..becceaa 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -34,13 +34,18 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
which are still listed in the manpage.
- Help is now sensitive to the size of the terminal window: the width of the
help text will scale to wider (or narrower) terminals than 80 characters.
-
+- Ninja: Changed generated build.ninja file to run SCons only build Actions via
+ a SCons Deamon. Added logic for starting and connecting to SCons daemon (currently
+ only used for ninja)
FIXES
-----
- Fix a number of Python ResourceWarnings which are issued when running SCons and/or it's tests
with python 3.9 (or higher)
+- Ninja: Fix issue where Configure files weren't being properly processed when build run
+ via ninja.
+
IMPROVEMENTS
------------
@@ -70,4 +75,4 @@ Thanks to the following contributors listed below for their contributions to thi
==========================================================================================
.. code-block:: text
- git shortlog --no-merges -ns 4.0.1..HEAD
+ git shortlog --no-merges -ns 4.3.0..HEAD
--
cgit v0.12
From 2a14bcda67108a86b342a4b4d6f5d4b1d394f3fb Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Mon, 14 Mar 2022 12:10:58 -0600
Subject: man: rearrange the variable substitution section [skip appveyor]
The idea is to get the syntactial elements presented in a relatively
concise order, in the existing text you have to hunt quite a bit
for certain parts. Some portions reworded.
The mention of $$ as a sbust escape (PR 4091) is also added to
the env.subst entry.
Signed-off-by: Mats Wichmann
---
SCons/Environment.xml | 15 +-
doc/man/scons.xml | 398 ++++++++++++++++++++++++++------------------------
2 files changed, 221 insertions(+), 192 deletions(-)
diff --git a/SCons/Environment.xml b/SCons/Environment.xml
index d314711..5b41f22 100644
--- a/SCons/Environment.xml
+++ b/SCons/Environment.xml
@@ -2691,7 +2691,7 @@ Prepend values to &consvars; in the current &consenv;,
maintaining uniqueness.
Works like &f-link-env-Append; (see for details),
except that values are added to the front,
-rather than the end, of any existing value of the the &consvar;,
+rather than the end, of any existing value of the &consvar;,
and values already present in the &consvar;
will not be added again.
If delete_existing
@@ -3139,9 +3139,16 @@ files = Split("""
-Performs construction variable interpolation
+Performs &consvar; interpolation
+(substitution)
on input,
which can be a string or a sequence.
+Substitutable elements take the form
+${expression},
+although if there is no ambiguity in recognizing the element,
+the braces can be omitted.
+A literal $ can be entered by
+using $$.
@@ -3176,7 +3183,7 @@ pairs
-If the input is a sequence
+If input is a sequence
(list or tuple),
the individual elements of
the sequence will be expanded,
@@ -3388,7 +3395,7 @@ env.Config(target = 'package-config', source = Value(prefix))
def build_value(target, source, env):
# A function that "builds" a Python Value by updating
- # the the Python value with the contents of the file
+ # the Python value with the contents of the file
# specified as the source of the Builder call ($SOURCE).
target[0].write(source[0].get_contents())
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index 71ae139..47aba1f 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -145,7 +145,7 @@ to support additional input file types.
Information about files involved in the build,
including a cryptographic hash of the contents,
-is cached for later reuse,
+is cached for later reuse,
By default content hashes are used to determine if a file
has changed since the last build,
but this can be controlled by selecting an appropriate
@@ -3983,7 +3983,7 @@ actually two bytes.Checks whether the C compiler
(as defined by the &cv-link-CC; &consvar;) works,
by trying to compile a small source file.
-This provides a more rigorous check:
+This provides a more rigorous check:
by default, &SCons; itself only detects if there is a program
with the correct name, not if it is a functioning compiler.
Returns a boolean indicating success or failure.
@@ -4003,7 +4003,7 @@ be accepted or rejected by the compiler.
Checks whether the C++ compiler
(as defined by the &cv-link-CXX; &consvar;) works,
by trying to compile a small source file.
-This provides a more rigorous check:
+This provides a more rigorous check:
by default, &SCons; itself only detects if there is a program
with the correct name, not if it is a functioning compiler.
Returns a boolean indicating success or failure.
@@ -4023,7 +4023,7 @@ be accepted or rejected by the compiler.
Checks whether the shared-object C compiler (as defined by the
&cv-link-SHCC; &consvar;) works
by trying to compile a small source file.
-This provides a more rigorous check:
+This provides a more rigorous check:
by default, &SCons; itself only detects if there is a program
with the correct name, not if it is a functioning compiler.
Returns a boolean indicating success or failure.
@@ -4045,7 +4045,7 @@ be created.
Checks whether the shared-object C++ compiler (as defined by the
&cv-link-SHCXX; &consvar;)
works by trying to compile a small source file.
-This provides a more rigorous check:
+This provides a more rigorous check:
by default, &SCons; itself only detects if there is a program
with the correct name, not if it is a functioning compiler.
Returns a boolean indicating success or failure.
@@ -4064,7 +4064,7 @@ be created.
context.CheckProg(prog_name)
-Checks if
+Checks if
prog_name
exists in the path &SCons; will use at build time.
(context.env['ENV']['PATH']).
@@ -4100,7 +4100,7 @@ it will be be used as the macro replacement value.
If value is a string and needs to
display with quotes, the quotes need to be included,
as in '"string"'
-If the optional
+If the optional
comment is given,
it is inserted as a comment above the macro definition
(suitable comment marks will be added automatically).
@@ -6410,34 +6410,200 @@ env.Command('marker', 'input_file', action=[MyBuildAction, Touch('$TARGET')])
Variable Substitution
-Before executing a command,
-&scons;
-performs variable substitution on the string that makes up
-the action part of the builder.
-Variables to be substituted are indicated in the
-string by a leading $,
-to distinguish them from plain text
-which is not to be substituted.
-The substitutable text may be surrounded by curly braces
-to separate it from surrounding characters if necessary
-(for example ${FOO}BAR).
-To avoid substituting a substring that looks like a variable name,
-escape it with an additional $,
-(for example, $$FOO will be left in the
-final string as $FOO).
+
+Before executing a command, &scons;
+performs parameter expansion (substitution)
+on the string that makes up the action part of the builder.
+The format of a substitutable parameter is
+${expression}.
+If expression refers to a variable,
+the braces in ${expression}
+can be omitted unless the variable name is
+immediately followed by a character that could either be interpreted
+as part of the name, or is &Python; syntax such as
+[ (for indexing/slicing)
+or . (for attribute access -
+see Special Attributes below).
+
-The curly brace notation is required when you use
-Python list subscripting/slicing notation on a variable
-to select one or more items from a list,
-or access a variable's special attributes,
-or when you use Python expression substitution
-(see below for descriptions of these).
+If expression refers to a &consvar;,
+it is replaced with the value of that variable in the
+&consenv; at the time of execution.
+If expression looks like
+a variable name but is not defined in the &consenv;
+it is replaced with an empty string.
+If expression refers to one of the
+Special Variables
+(see below) the corresponding value of the variable is substituted.
+expression may also be
+a &Python; expression to be evaluated.
+See Python Code Substitution
+below for a description.
+&SCons; uses the following rules when converting &consvars; into
+command line strings:
+
+
+
+ If the value is a string it is interpreted as space delimited
+ command line arguments.
+
+
+
+ If the value is a list it is interpreted as a list of command
+ line arguments. Each element of the list is converted to a string.
+
+
+
+ Anything that is not a list or string is converted to a string and
+ interpreted as a single command line argument.
+
+
+
+ Newline characters (\n) delimit lines.
+ The newline parsing is done after
+ all other parsing, so it is not possible for arguments (e.g. file names) to
+ contain embedded newline characters.
+
+
+
+ For a literal $
+ use $$.
+ For example, $$FOO will be left in the
+ final string as $FOO.
+
+
+
+
+When a build action is executed, a hash of the command line is
+saved, together with other information about the target(s) built
+by the action, for future use in rebuild determination.
+This is called the &buildsig;
+(or &build_action; signature).
+The escape sequence
+$(
+subexpression
+$)
+may be used to indicate parts of a command line
+that may change without
+causing a rebuild--that is,
+which are not to be included when calculating the &buildsig;.
+All text from
+$(
+up to and including the matching
+$)
+will be removed from the command line
+before it is added to the &buildsig;
+while only the
+$(
+and
+$)
+will be removed before the command is executed.
+For example, the command line string:
+
+
+"echo Last build occurred $( $TODAY $). > $TARGET"
+
+
+would execute the command:
+
+
+echo Last build occurred $TODAY. > $TARGET
+
+
+but the build signature added to any target files would be computed from:
+
+
+echo Last build occurred . > $TARGET
+
+
+While &consvars; are normally directly substituted,
+if a &consvar; has a value which
+is a callable &Python; object
+(a function, or a class with a __call__ method),
+that object is called during substitution.
+The callable must accept four arguments:
+target,
+source,
+env and
+for_signature.
+source is a list of source nodes,
+target is a list of target nodes,
+env is the &consenv; to use for context,
+and for_signature is
+a boolean value that tells the callable
+if it is being called for the purpose of generating a build signature.
+Since the build signature is used for rebuild determination,
+variable elements that do not affect whether
+a rebuild should be triggered
+should be omitted from the returned string
+if for_signature is true.
+See $(
+and $) above
+for the syntax.
+
+
+
+&SCons; will insert whatever
+the callable returns
+into the expanded string:
+
+
+
+def foo(target, source, env, for_signature):
+ return "bar"
+
+# Will expand $BAR to "bar baz"
+env = Environment(FOO=foo, BAR="$FOO baz")
+
+
+As a reminder, substitution happens when
+$BAR is actually used in a
+builder action. The value of env['BAR']
+will be exactly as it was set: "$FOO baz".
+This can make debugging tricky,
+as the substituted result is not available at the time
+the SConscript files are being interpreted and
+thus not available to print().
+However, you can perform the substitution on demand
+by calling the &f-link-env-subst; method for this purpose.
+
+
+You can use this feature to pass arguments to a
+callable variable by creating a callable class
+that stores passed arguments in the instance,
+and then uses them
+(in the __call__ method)
+when the instance is called.
+Note that in this case,
+the entire variable expansion must
+be enclosed by curly braces
+so that the arguments will
+be associated with the
+instantiation of the class:
+
+
+class foo:
+ def __init__(self, arg):
+ self.arg = arg
+
+ def __call__(self, target, source, env, for_signature):
+ return self.arg + " bar"
+
+# Will expand $BAR to "my argument bar baz"
+env=Environment(FOO=foo, BAR="${FOO('my argument')} baz")
+
+
+
+
+
+Substitution: Special Variables
+
Besides regular &consvars;, scons provides the following
-special variables for use in expanding commands:
+Special Variables for use in expanding commands:
@@ -6509,8 +6675,12 @@ changed since the target was last built.
-These names are reserved
-and may not be assigned to or used as &consvars;.
+
+These names are reserved
+and may not be assigned to or used as &consvars;.
+&SCons; computes them in a context-dependent manner
+and they and are not retrieved from a &consenv;.
+For example, the following builder call:
@@ -6536,6 +6706,11 @@ In the previous example, a string
would expand to: bar.c.
+
+
+
+Substitution: Special Attributes
+A variable name may
have the following
modifiers appended within the enclosing curly braces
@@ -6631,120 +6806,6 @@ Some modifiers can be combined, like
${TARGET.file.suffix}, etc.
-The curly brace notation may also be used
-to enclose a Python expression to be evaluated.
-See below
-for a description.
-
-The special escape sequences
-$(
-and
-$)
-may be used to surround parts of a command line
-that may change
-without
-causing a rebuild--that is,
-which are not included in the &buildsig;
-of target files built with this command.
-All text between
-$(
-and
-$)
-will be removed from the command line
-before it is added to the &buildsig;
-and the
-$(
-and
-$)
-will be removed before the command is executed.
-For example, the command line:
-
-
-echo Last build occurred $( $TODAY $). > $TARGET
-
-
-would execute the command:
-
-
-echo Last build occurred $TODAY. > $TARGET
-
-
-but the command portion of the
-the &buildsig; computed for any target files built
-by this action would be:
-
-
-echo Last build occurred . > $TARGET
-
-
-While &consvars; are normally directly substituted,
-if a variable refers to a &consvar;
-whose value is a callable &Python; object (a function
-or a class with a __call__ method),
-that object is called during substitution.
-The callable must accept four arguments:
-target,
-source,
-env and
-for_signature.
-source is a list of source nodes,
-target is a list of target nodes,
-env is the &consenv; to use for context,
-and for_signature is
-a Boolean value that tells the function
-if it is being called for the purpose of generating a &buildsig;.
-Since the &buildsig; is used for rebuild determination,
-the function should omit variable elements
-that do not affect whether a rebuild should be triggered
-(see $(
-and $)
-above) if for_signature is true.
-
-
-
-&SCons; will insert whatever
-the callable returns
-into the expanded string:
-
-
-
-def foo(target, source, env, for_signature):
- return "bar"
-
-# Will expand $BAR to "bar baz"
-env = Environment(FOO=foo, BAR="$FOO baz")
-
-
-As a reminder, substitution happens when
-$BAR is actually used in a
-builder action. The value of env['BAR']
-will be exactly as it was set: "$FOO baz".
-
-
-You can use this feature to pass arguments to a
-callable variable by creating a callable class
-that stores passed arguments in the instance,
-and then uses them
-(in the __call__ method)
-when the instance is called.
-Note that in this case,
-the entire variable expansion must
-be enclosed by curly braces
-so that the arguments will
-be associated with the
-instantiation of the class:
-
-
-class foo:
- def __init__(self, arg):
- self.arg = arg
-
- def __call__(self, target, source, env, for_signature):
- return self.arg + " bar"
-
-# Will expand $BAR to "my argument bar baz"
-env=Environment(FOO=foo, BAR="${FOO('my argument')} baz")
-
@@ -6752,8 +6813,8 @@ env=Environment(FOO=foo, BAR="${FOO('my argument')} baz")
If a substitutable expression using the notation
-${something} does not appear to match one of
-the other substitution patterns,
+${expression}
+does not appear to match one of the other substitution patterns,
it is evaluated as a Python expression.
This uses Python's eval function,
with the globals parameter set to
@@ -6819,45 +6880,6 @@ which &SCons; passes to eval which
returns the value.
-&SCons; uses the following rules when converting &consvars; into
-command lines:
-
-
-
- string
-
-When the value is a string it is interpreted as a space delimited list of
-command line arguments.
-
-
-
-
- list
-
-When the value is a list it is interpreted as a list of command line
-arguments. Each element of the list is converted to a string.
-
-
-
-
- other
-
-Anything that is not a list or string is converted to a string and
-interpreted as a single command line argument.
-
-
-
-
- newline
-
-Newline characters (\n) delimit lines.
-The newline parsing is done after
-all other parsing, so it is not possible for arguments (e.g. file names) to
-contain embedded newline characters.
-
-
-
-
Use of the Python eval function
is considered to have security implications, since,
@@ -6917,7 +6939,7 @@ is optional, the default is no arg.
The function can use use
str(node)
-to fetch the name of the file,
+to fetch the name of the file,
node.dir
to fetch the directory the file is in,
node.get_contents()
@@ -6938,7 +6960,7 @@ in order to build Nodes with correct paths.
Using &f-link-FindPathDirs; with an argument of CPPPATH
as the path_function in the &f-Scanner; call
means the scanner function will be called with the paths extracted
-from &cv-CPPPATH; in the environment env
+from &cv-CPPPATH; in the environment env
passed as the paths parameter.
--
cgit v0.12
From ff81eebb73825091051e5ee846d0d89c60798112 Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Fri, 16 Aug 2019 08:19:20 -0600
Subject: Use super call instead of direct class call
- super used where direct call to superclass existed
- convert a few older-style super() (two-argument) uses
- in a few places, where there was an intersection with a super change,
variables that override a builtin (e.g. "dict") were renamed.
Signed-off-by: Mats Wichmann
---
CHANGES.txt | 3 +
SCons/Action.py | 4 +-
SCons/Builder.py | 13 ++--
SCons/Environment.py | 109 +++++++++++++++++---------------
SCons/Errors.py | 6 +-
SCons/Job.py | 2 +-
SCons/JobTests.py | 6 +-
SCons/Memoize.py | 2 +-
SCons/Node/Alias.py | 2 +-
SCons/Node/FS.py | 12 ++--
SCons/Node/FSTests.py | 4 +-
SCons/Node/NodeTests.py | 6 +-
SCons/Node/Python.py | 2 +-
SCons/SConf.py | 6 +-
SCons/SConsign.py | 6 +-
SCons/Subst.py | 4 +-
SCons/SubstTests.py | 2 +-
SCons/Tool/GettextCommon.py | 2 +-
SCons/Tool/MSCommon/sdk.py | 4 +-
SCons/Tool/msvs.py | 6 +-
SCons/UtilTests.py | 2 +-
SCons/cppTests.py | 2 +-
bin/SConsDoc.py | 2 +-
src/test_setup.py | 2 +-
testing/framework/TestCommon.py | 2 +-
testing/framework/TestRuntest.py | 2 +-
testing/framework/TestSCons.py | 4 +-
testing/framework/TestSCons_time.py | 2 +-
testing/framework/TestSConsign.py | 2 +-
testing/framework/TestUnit/taprunner.py | 14 ++--
30 files changed, 122 insertions(+), 113 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index d36e911..d19fb06 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -56,6 +56,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
processors for testing threads.
- Fixed crash in C scanner's dictify_CPPDEFINES() function which happens if
AppendUnique is called on CPPPATH. (Issue #4108).
+ - As "code modernization" all of SCons now uses the current super()
+ zero-argument syntax instead of direct calls to a parent class method
+ or the super() two-argument syntax.
From Zhichang Yu:
- Added MSVC_USE_SCRIPT_ARGS variable to pass arguments to MSVC_USE_SCRIPT.
diff --git a/SCons/Action.py b/SCons/Action.py
index b7c6bb7..81dc033 100644
--- a/SCons/Action.py
+++ b/SCons/Action.py
@@ -848,7 +848,7 @@ class CommandAction(_ActionAction):
# variables.
if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.CommandAction')
- _ActionAction.__init__(self, **kw)
+ super().__init__(**kw)
if is_List(cmd):
if [c for c in cmd if is_List(c)]:
raise TypeError("CommandAction should be given only "
@@ -1231,7 +1231,7 @@ class FunctionAction(_ActionAction):
# This is weird, just do the best we can.
self.funccontents = _object_contents(execfunction)
- _ActionAction.__init__(self, **kw)
+ super().__init__(**kw)
def function_name(self):
try:
diff --git a/SCons/Builder.py b/SCons/Builder.py
index 41475ac..ab51c32 100644
--- a/SCons/Builder.py
+++ b/SCons/Builder.py
@@ -130,8 +130,8 @@ class DictCmdGenerator(SCons.Util.Selector):
to return the proper action based on the file suffix of
the source file."""
- def __init__(self, dict=None, source_ext_match=1):
- SCons.Util.Selector.__init__(self, dict)
+ def __init__(self, mapping=None, source_ext_match=True):
+ super().__init__(mapping)
self.source_ext_match = source_ext_match
def src_suffixes(self):
@@ -222,10 +222,11 @@ class OverrideWarner(UserDict):
can actually invoke multiple builders. This class only emits the
warnings once, no matter how many Builders are invoked.
"""
- def __init__(self, dict):
- UserDict.__init__(self, dict)
+ def __init__(self, mapping):
+ super().__init__(mapping)
if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.OverrideWarner')
self.already_warned = None
+
def warn(self):
if self.already_warned:
return
@@ -245,7 +246,7 @@ def Builder(**kw):
kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {})
del kw['generator']
elif 'action' in kw:
- source_ext_match = kw.get('source_ext_match', 1)
+ source_ext_match = kw.get('source_ext_match', True)
if 'source_ext_match' in kw:
del kw['source_ext_match']
if SCons.Util.is_Dict(kw['action']):
@@ -876,7 +877,7 @@ class CompositeBuilder(SCons.Util.Proxy):
def __init__(self, builder, cmdgen):
if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.CompositeBuilder')
- SCons.Util.Proxy.__init__(self, builder)
+ super().__init__(builder)
# cmdgen should always be an instance of DictCmdGenerator.
self.cmdgen = cmdgen
diff --git a/SCons/Environment.py b/SCons/Environment.py
index d0a4baf..34e4968 100644
--- a/SCons/Environment.py
+++ b/SCons/Environment.py
@@ -275,12 +275,12 @@ class BuilderDict(UserDict):
the Builders. We need to do this because every time someone changes
the Builders in the Environment's BUILDERS dictionary, we must
update the Environment's attributes."""
- def __init__(self, dict, env):
+ def __init__(self, mapping, env):
# Set self.env before calling the superclass initialization,
# because it will end up calling our other methods, which will
# need to point the values in this dictionary to self.env.
self.env = env
- UserDict.__init__(self, dict)
+ super().__init__(mapping)
def __semi_deepcopy__(self):
# These cannot be copied since they would both modify the same builder object, and indeed
@@ -294,15 +294,15 @@ class BuilderDict(UserDict):
pass
else:
self.env.RemoveMethod(method)
- UserDict.__setitem__(self, item, val)
+ super().__setitem__(item, val)
BuilderWrapper(self.env, val, item)
def __delitem__(self, item):
- UserDict.__delitem__(self, item)
+ super().__delitem__(item)
delattr(self.env, item)
- def update(self, dict):
- for i, v in dict.items():
+ def update(self, mapping):
+ for i, v in mapping.items():
self.__setitem__(i, v)
@@ -637,7 +637,7 @@ class SubstitutionEnvironment:
it is assumed to be a command and the rest of the string is executed;
the result of that evaluation is then added to the dict.
"""
- dict = {
+ mapping = {
'ASFLAGS' : CLVar(''),
'CFLAGS' : CLVar(''),
'CCFLAGS' : CLVar(''),
@@ -667,12 +667,12 @@ class SubstitutionEnvironment:
arg = self.backtick(arg[1:])
# utility function to deal with -D option
- def append_define(name, dict = dict):
+ def append_define(name, mapping=mapping):
t = name.split('=')
if len(t) == 1:
- dict['CPPDEFINES'].append(name)
+ mapping['CPPDEFINES'].append(name)
else:
- dict['CPPDEFINES'].append([t[0], '='.join(t[1:])])
+ mapping['CPPDEFINES'].append([t[0], '='.join(t[1:])])
# Loop through the flags and add them to the appropriate option.
# This tries to strike a balance between checking for all possible
@@ -702,67 +702,67 @@ class SubstitutionEnvironment:
append_define(arg)
elif append_next_arg_to == '-include':
t = ('-include', self.fs.File(arg))
- dict['CCFLAGS'].append(t)
+ mapping['CCFLAGS'].append(t)
elif append_next_arg_to == '-imacros':
t = ('-imacros', self.fs.File(arg))
- dict['CCFLAGS'].append(t)
+ mapping['CCFLAGS'].append(t)
elif append_next_arg_to == '-isysroot':
t = ('-isysroot', arg)
- dict['CCFLAGS'].append(t)
- dict['LINKFLAGS'].append(t)
+ mapping['CCFLAGS'].append(t)
+ mapping['LINKFLAGS'].append(t)
elif append_next_arg_to == '-isystem':
t = ('-isystem', arg)
- dict['CCFLAGS'].append(t)
+ mapping['CCFLAGS'].append(t)
elif append_next_arg_to == '-iquote':
t = ('-iquote', arg)
- dict['CCFLAGS'].append(t)
+ mapping['CCFLAGS'].append(t)
elif append_next_arg_to == '-idirafter':
t = ('-idirafter', arg)
- dict['CCFLAGS'].append(t)
+ mapping['CCFLAGS'].append(t)
elif append_next_arg_to == '-arch':
t = ('-arch', arg)
- dict['CCFLAGS'].append(t)
- dict['LINKFLAGS'].append(t)
+ mapping['CCFLAGS'].append(t)
+ mapping['LINKFLAGS'].append(t)
elif append_next_arg_to == '--param':
t = ('--param', arg)
- dict['CCFLAGS'].append(t)
+ mapping['CCFLAGS'].append(t)
else:
- dict[append_next_arg_to].append(arg)
+ mapping[append_next_arg_to].append(arg)
append_next_arg_to = None
elif not arg[0] in ['-', '+']:
- dict['LIBS'].append(self.fs.File(arg))
+ mapping['LIBS'].append(self.fs.File(arg))
elif arg == '-dylib_file':
- dict['LINKFLAGS'].append(arg)
+ mapping['LINKFLAGS'].append(arg)
append_next_arg_to = 'LINKFLAGS'
elif arg[:2] == '-L':
if arg[2:]:
- dict['LIBPATH'].append(arg[2:])
+ mapping['LIBPATH'].append(arg[2:])
else:
append_next_arg_to = 'LIBPATH'
elif arg[:2] == '-l':
if arg[2:]:
- dict['LIBS'].append(arg[2:])
+ mapping['LIBS'].append(arg[2:])
else:
append_next_arg_to = 'LIBS'
elif arg[:2] == '-I':
if arg[2:]:
- dict['CPPPATH'].append(arg[2:])
+ mapping['CPPPATH'].append(arg[2:])
else:
append_next_arg_to = 'CPPPATH'
elif arg[:4] == '-Wa,':
- dict['ASFLAGS'].append(arg[4:])
- dict['CCFLAGS'].append(arg)
+ mapping['ASFLAGS'].append(arg[4:])
+ mapping['CCFLAGS'].append(arg)
elif arg[:4] == '-Wl,':
if arg[:11] == '-Wl,-rpath=':
- dict['RPATH'].append(arg[11:])
+ mapping['RPATH'].append(arg[11:])
elif arg[:7] == '-Wl,-R,':
- dict['RPATH'].append(arg[7:])
+ mapping['RPATH'].append(arg[7:])
elif arg[:6] == '-Wl,-R':
- dict['RPATH'].append(arg[6:])
+ mapping['RPATH'].append(arg[6:])
else:
- dict['LINKFLAGS'].append(arg)
+ mapping['LINKFLAGS'].append(arg)
elif arg[:4] == '-Wp,':
- dict['CPPFLAGS'].append(arg)
+ mapping['CPPFLAGS'].append(arg)
elif arg[:2] == '-D':
if arg[2:]:
append_define(arg[2:])
@@ -771,32 +771,32 @@ class SubstitutionEnvironment:
elif arg == '-framework':
append_next_arg_to = 'FRAMEWORKS'
elif arg[:14] == '-frameworkdir=':
- dict['FRAMEWORKPATH'].append(arg[14:])
+ mapping['FRAMEWORKPATH'].append(arg[14:])
elif arg[:2] == '-F':
if arg[2:]:
- dict['FRAMEWORKPATH'].append(arg[2:])
+ mapping['FRAMEWORKPATH'].append(arg[2:])
else:
append_next_arg_to = 'FRAMEWORKPATH'
- elif arg in [
+ elif arg in (
'-mno-cygwin',
'-pthread',
'-openmp',
'-fmerge-all-constants',
'-fopenmp',
- ]:
- dict['CCFLAGS'].append(arg)
- dict['LINKFLAGS'].append(arg)
+ ):
+ mapping['CCFLAGS'].append(arg)
+ mapping['LINKFLAGS'].append(arg)
elif arg == '-mwindows':
- dict['LINKFLAGS'].append(arg)
+ mapping['LINKFLAGS'].append(arg)
elif arg[:5] == '-std=':
if '++' in arg[5:]:
- key='CXXFLAGS'
+ key = 'CXXFLAGS'
else:
- key='CFLAGS'
- dict[key].append(arg)
+ key = 'CFLAGS'
+ mapping[key].append(arg)
elif arg[0] == '+':
- dict['CCFLAGS'].append(arg)
- dict['LINKFLAGS'].append(arg)
+ mapping['CCFLAGS'].append(arg)
+ mapping['LINKFLAGS'].append(arg)
elif arg in [
'-include',
'-imacros',
@@ -809,11 +809,11 @@ class SubstitutionEnvironment:
]:
append_next_arg_to = arg
else:
- dict['CCFLAGS'].append(arg)
+ mapping['CCFLAGS'].append(arg)
for arg in flags:
do_parse(arg)
- return dict
+ return mapping
def MergeFlags(self, args, unique=True):
"""Merge flags into construction variables.
@@ -2375,7 +2375,7 @@ class OverrideEnvironment(Base):
if SCons.Debug.track_instances: logInstanceCreation(self, 'Environment.OverrideEnvironment')
self.__dict__['__subject'] = subject
if overrides is None:
- self.__dict__['overrides'] = dict()
+ self.__dict__['overrides'] = {}
else:
self.__dict__['overrides'] = overrides
@@ -2472,6 +2472,11 @@ class OverrideEnvironment(Base):
self.__dict__['overrides'].update(other)
def _update_onlynew(self, other):
+ """Update a dict with new keys.
+
+ Unlike the .update method, if the key is already present,
+ it is not replaced.
+ """
for k, v in other.items():
if k not in self.__dict__['overrides']:
self.__dict__['overrides'][k] = v
@@ -2526,14 +2531,14 @@ def NoSubstitutionProxy(subject):
del kwdict['executor']
else:
kwdict['lvars'] = {}
- def raw_to_mode(self, dict):
+ def raw_to_mode(self, mapping):
try:
- raw = dict['raw']
+ raw = mapping['raw']
except KeyError:
pass
else:
- del dict['raw']
- dict['mode'] = raw
+ del mapping['raw']
+ mapping['mode'] = raw
def subst(self, string, *args, **kwargs):
return string
def subst_kw(self, kw, *args, **kwargs):
diff --git a/SCons/Errors.py b/SCons/Errors.py
index 42db072..04cea38 100644
--- a/SCons/Errors.py
+++ b/SCons/Errors.py
@@ -99,8 +99,8 @@ class BuildError(Exception):
self.action = action
self.command = command
- Exception.__init__(self, node, errstr, status, exitstatus, filename,
- executor, action, command, exc_info)
+ super().__init__(node, errstr, status, exitstatus, filename,
+ executor, action, command, exc_info)
def __str__(self):
if self.filename:
@@ -128,7 +128,7 @@ class ExplicitExit(Exception):
self.node = node
self.status = status
self.exitstatus = status
- Exception.__init__(self, *args)
+ super().__init__(*args)
def convert_to_BuildError(status, exc_info=None):
"""Convert a return code to a BuildError Exception.
diff --git a/SCons/Job.py b/SCons/Job.py
index bcdb88a..b398790 100644
--- a/SCons/Job.py
+++ b/SCons/Job.py
@@ -238,7 +238,7 @@ else:
and a boolean indicating whether the task executed successfully. """
def __init__(self, requestQueue, resultsQueue, interrupted):
- threading.Thread.__init__(self)
+ super().__init__()
self.daemon = True
self.requestQueue = requestQueue
self.resultsQueue = resultsQueue
diff --git a/SCons/JobTests.py b/SCons/JobTests.py
index 5b5a590..54d7fa4 100644
--- a/SCons/JobTests.py
+++ b/SCons/JobTests.py
@@ -410,13 +410,13 @@ class DummyNodeInfo:
class testnode (SCons.Node.Node):
def __init__(self):
- SCons.Node.Node.__init__(self)
+ super().__init__()
self.expect_to_be = SCons.Node.executed
self.ninfo = DummyNodeInfo()
class goodnode (testnode):
def __init__(self):
- SCons.Node.Node.__init__(self)
+ super().__init__()
self.expect_to_be = SCons.Node.up_to_date
self.ninfo = DummyNodeInfo()
@@ -430,7 +430,7 @@ class slowgoodnode (goodnode):
class badnode (goodnode):
def __init__(self):
- goodnode.__init__(self)
+ super().__init__()
self.expect_to_be = SCons.Node.failed
def build(self, **kw):
raise Exception('badnode exception')
diff --git a/SCons/Memoize.py b/SCons/Memoize.py
index 8c3303f..b02c144 100644
--- a/SCons/Memoize.py
+++ b/SCons/Memoize.py
@@ -159,7 +159,7 @@ class CountDict(Counter):
def __init__(self, cls_name, method_name, keymaker):
"""
"""
- Counter.__init__(self, cls_name, method_name)
+ super().__init__(cls_name, method_name)
self.keymaker = keymaker
def count(self, *args, **kw):
""" Counts whether the computed key value is already present
diff --git a/SCons/Node/Alias.py b/SCons/Node/Alias.py
index b5e4eb4..1125c22 100644
--- a/SCons/Node/Alias.py
+++ b/SCons/Node/Alias.py
@@ -99,7 +99,7 @@ class Alias(SCons.Node.Node):
BuildInfo = AliasBuildInfo
def __init__(self, name):
- SCons.Node.Node.__init__(self)
+ super().__init__()
self.name = name
self.changed_since_last_build = 1
self.store_info = 0
diff --git a/SCons/Node/FS.py b/SCons/Node/FS.py
index 5f05a86..c1bfd6a 100644
--- a/SCons/Node/FS.py
+++ b/SCons/Node/FS.py
@@ -82,7 +82,7 @@ class EntryProxyAttributeError(AttributeError):
of the underlying Entry involved in an AttributeError exception.
"""
def __init__(self, entry_proxy, attribute):
- AttributeError.__init__(self)
+ super().__init__()
self.entry_proxy = entry_proxy
self.attribute = attribute
def __str__(self):
@@ -579,7 +579,7 @@ class Base(SCons.Node.Node):
signatures."""
if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS.Base')
- SCons.Node.Node.__init__(self)
+ super().__init__()
# Filenames and paths are probably reused and are intern'ed to save some memory.
# Filename with extension as it was specified when the object was
@@ -982,7 +982,7 @@ class Entry(Base):
'contentsig']
def __init__(self, name, directory, fs):
- Base.__init__(self, name, directory, fs)
+ super().__init__(name, directory, fs)
self._func_exists = 3
self._func_get_contents = 1
@@ -1574,7 +1574,7 @@ class Dir(Base):
def __init__(self, name, directory, fs):
if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS.Dir')
- Base.__init__(self, name, directory, fs)
+ super().__init__(name, directory, fs)
self._morph()
def _morph(self):
@@ -2563,7 +2563,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase):
if key != 'dependency_map' and hasattr(self, 'dependency_map'):
del self.dependency_map
- return super(FileBuildInfo, self).__setattr__(key, value)
+ return super().__setattr__(key, value)
def convert_to_sconsign(self):
"""
@@ -2674,7 +2674,7 @@ class File(Base):
def __init__(self, name, directory, fs):
if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS.File')
- Base.__init__(self, name, directory, fs)
+ super().__init__(name, directory, fs)
self._morph()
def Entry(self, name):
diff --git a/SCons/Node/FSTests.py b/SCons/Node/FSTests.py
index d78940e..b93efc8 100644
--- a/SCons/Node/FSTests.py
+++ b/SCons/Node/FSTests.py
@@ -2568,7 +2568,7 @@ class FileTestCase(_tempdirTestCase):
class ChangedNode(SCons.Node.FS.File):
def __init__(self, name, directory=None, fs=None):
- SCons.Node.FS.File.__init__(self, name, directory, fs)
+ super().__init__(name, directory, fs)
self.name = name
self.Tag('found_includes', [])
self.stored_info = None
@@ -2608,7 +2608,7 @@ class FileTestCase(_tempdirTestCase):
class ChangedEnvironment(SCons.Environment.Base):
def __init__(self):
- SCons.Environment.Base.__init__(self)
+ super().__init__()
self.decide_source = self._changed_timestamp_then_content
class FakeNodeInfo:
diff --git a/SCons/Node/NodeTests.py b/SCons/Node/NodeTests.py
index b36c2e9..ee4d080 100644
--- a/SCons/Node/NodeTests.py
+++ b/SCons/Node/NodeTests.py
@@ -160,7 +160,7 @@ class NoneBuilder(Builder):
class ListBuilder(Builder):
def __init__(self, *nodes):
- Builder.__init__(self)
+ super().__init__()
self.nodes = nodes
def execute(self, target, source, env):
if hasattr(self, 'status'):
@@ -200,7 +200,7 @@ class MyNode(SCons.Node.Node):
simulate a real, functional Node subclass.
"""
def __init__(self, name):
- SCons.Node.Node.__init__(self)
+ super().__init__()
self.name = name
self.Tag('found_includes', [])
def __str__(self):
@@ -1226,7 +1226,7 @@ class NodeTestCase(unittest.TestCase):
"""Test the get_string() method."""
class TestNode(MyNode):
def __init__(self, name, sig):
- MyNode.__init__(self, name)
+ super().__init__(name)
self.sig = sig
def for_signature(self):
diff --git a/SCons/Node/Python.py b/SCons/Node/Python.py
index c6850ab..80d2762 100644
--- a/SCons/Node/Python.py
+++ b/SCons/Node/Python.py
@@ -83,7 +83,7 @@ class Value(SCons.Node.Node):
BuildInfo = ValueBuildInfo
def __init__(self, value, built_value=None, name=None):
- SCons.Node.Node.__init__(self)
+ super().__init__()
self.value = value
self.changed_since_last_build = 6
self.store_info = 0
diff --git a/SCons/SConf.py b/SCons/SConf.py
index 0ad712d..ad29153 100644
--- a/SCons/SConf.py
+++ b/SCons/SConf.py
@@ -142,7 +142,7 @@ SCons.Warnings.enableWarningClass(SConfWarning)
# some error definitions
class SConfError(SCons.Errors.UserError):
def __init__(self,msg):
- SCons.Errors.UserError.__init__(self,msg)
+ super().__init__(msg)
class ConfigureDryRunError(SConfError):
"""Raised when a file or directory needs to be updated during a Configure
@@ -152,13 +152,13 @@ class ConfigureDryRunError(SConfError):
msg = 'Cannot create configure directory "%s" within a dry-run.' % str(target)
else:
msg = 'Cannot update configure test "%s" within a dry-run.' % str(target)
- SConfError.__init__(self,msg)
+ super().__init__(msg)
class ConfigureCacheError(SConfError):
"""Raised when a use explicitely requested the cache feature, but the test
is run the first time."""
def __init__(self,target):
- SConfError.__init__(self, '"%s" is not yet built and cache is forced.' % str(target))
+ super().__init__('"%s" is not yet built and cache is forced.' % str(target))
# define actions for building text files
diff --git a/SCons/SConsign.py b/SCons/SConsign.py
index 5b78855..ecca391 100644
--- a/SCons/SConsign.py
+++ b/SCons/SConsign.py
@@ -248,7 +248,7 @@ class DB(Base):
determined by the database module.
"""
def __init__(self, dir):
- Base.__init__(self)
+ super().__init__()
self.dir = dir
@@ -319,7 +319,7 @@ class Dir(Base):
"""
fp - file pointer to read entries from
"""
- Base.__init__(self)
+ super().__init__()
if not fp:
return
@@ -352,7 +352,7 @@ class DirFile(Dir):
fp = None
try:
- Dir.__init__(self, fp, dir)
+ super().__init__(fp, dir)
except KeyboardInterrupt:
raise
except Exception:
diff --git a/SCons/Subst.py b/SCons/Subst.py
index 298df38..7535772 100644
--- a/SCons/Subst.py
+++ b/SCons/Subst.py
@@ -132,7 +132,7 @@ class CmdStringHolder(collections.UserString):
proper escape sequences inserted.
"""
def __init__(self, cmd, literal=None):
- collections.UserString.__init__(self, cmd)
+ super().__init__(cmd)
self.literal = literal
def is_literal(self):
@@ -490,7 +490,7 @@ class ListSubber(collections.UserList):
internally.
"""
def __init__(self, env, mode, conv, gvars):
- collections.UserList.__init__(self, [])
+ super().__init__([])
self.env = env
self.mode = mode
self.conv = conv
diff --git a/SCons/SubstTests.py b/SCons/SubstTests.py
index b956a25..1a203a0 100644
--- a/SCons/SubstTests.py
+++ b/SCons/SubstTests.py
@@ -121,7 +121,7 @@ class SubstTestCase(unittest.TestCase):
class MyNode(DummyNode):
"""Simple node work-alike with some extra stuff for testing."""
def __init__(self, name):
- DummyNode.__init__(self, name)
+ super().__init__(name)
class Attribute:
pass
self.attribute = Attribute()
diff --git a/SCons/Tool/GettextCommon.py b/SCons/Tool/GettextCommon.py
index 16900e0..058b997 100644
--- a/SCons/Tool/GettextCommon.py
+++ b/SCons/Tool/GettextCommon.py
@@ -206,7 +206,7 @@ class _POFileBuilder(BuilderBase):
del kw['target_alias']
if 'target_factory' not in kw:
kw['target_factory'] = _POTargetFactory(env, alias=alias).File
- BuilderBase.__init__(self, **kw)
+ super().__init__(**kw)
def _execute(self, env, target, source, *args, **kw):
""" Execute builder's actions.
diff --git a/SCons/Tool/MSCommon/sdk.py b/SCons/Tool/MSCommon/sdk.py
index d95df91..aa94f4d 100644
--- a/SCons/Tool/MSCommon/sdk.py
+++ b/SCons/Tool/MSCommon/sdk.py
@@ -131,7 +131,7 @@ class WindowsSDK(SDKDefinition):
"""
HKEY_FMT = r'Software\Microsoft\Microsoft SDKs\Windows\v%s\InstallationFolder'
def __init__(self, *args, **kw):
- SDKDefinition.__init__(self, *args, **kw)
+ super().__init__(*args, **kw)
self.hkey_data = self.version
class PlatformSDK(SDKDefinition):
@@ -140,7 +140,7 @@ class PlatformSDK(SDKDefinition):
"""
HKEY_FMT = r'Software\Microsoft\MicrosoftSDK\InstalledSDKS\%s\Install Dir'
def __init__(self, *args, **kw):
- SDKDefinition.__init__(self, *args, **kw)
+ super().__init__(*args, **kw)
self.hkey_data = self.uuid
#
diff --git a/SCons/Tool/msvs.py b/SCons/Tool/msvs.py
index 7a827f4..887cb59 100644
--- a/SCons/Tool/msvs.py
+++ b/SCons/Tool/msvs.py
@@ -320,7 +320,7 @@ class _GenerateV7User(_UserGenerator):
self.usrhead = V9UserHeader
self.usrconf = V9UserConfiguration
self.usrdebg = V9DebugSettings
- _UserGenerator.__init__(self, dspfile, source, env)
+ super().__init__(dspfile, source, env)
def UserProject(self):
confkeys = sorted(self.configs.keys())
@@ -395,7 +395,7 @@ class _GenerateV10User(_UserGenerator):
self.usrhead = V10UserHeader
self.usrconf = V10UserConfiguration
self.usrdebg = V10DebugSettings
- _UserGenerator.__init__(self, dspfile, source, env)
+ super().__init__(dspfile, source, env)
def UserProject(self):
confkeys = sorted(self.configs.keys())
@@ -1487,7 +1487,7 @@ class _DSWGenerator:
class _GenerateV7DSW(_DSWGenerator):
"""Generates a Solution file for MSVS .NET"""
def __init__(self, dswfile, source, env):
- _DSWGenerator.__init__(self, dswfile, source, env)
+ super().__init__(dswfile, source, env)
self.file = None
self.version = self.env['MSVS_VERSION']
diff --git a/SCons/UtilTests.py b/SCons/UtilTests.py
index 5ed31cb..0cfb70f 100644
--- a/SCons/UtilTests.py
+++ b/SCons/UtilTests.py
@@ -899,7 +899,7 @@ class HashTestCase(unittest.TestCase):
class FIPSHashTestCase(unittest.TestCase):
def __init__(self, *args, **kwargs):
- super(FIPSHashTestCase, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
###############################
# algorithm mocks, can check if we called with usedforsecurity=False for python >= 3.9
diff --git a/SCons/cppTests.py b/SCons/cppTests.py
index 99fa6cf..a9aef9d 100644
--- a/SCons/cppTests.py
+++ b/SCons/cppTests.py
@@ -853,7 +853,7 @@ class fileTestCase(unittest.TestCase):
""")
class MyPreProcessor(cpp.DumbPreProcessor):
def __init__(self, *args, **kw):
- cpp.DumbPreProcessor.__init__(self, *args, **kw)
+ super().__init__(*args, **kw)
self.files = []
def __call__(self, file):
self.files.append(file)
diff --git a/bin/SConsDoc.py b/bin/SConsDoc.py
index a465057..82e2c72 100644
--- a/bin/SConsDoc.py
+++ b/bin/SConsDoc.py
@@ -499,7 +499,7 @@ class Function(Item):
class Tool(Item):
def __init__(self, name):
- Item.__init__(self, name)
+ super().__init__(name)
self.entity = self.name.replace('+', 'X')
diff --git a/src/test_setup.py b/src/test_setup.py
index 8410be3..5db21a1 100644
--- a/src/test_setup.py
+++ b/src/test_setup.py
@@ -81,7 +81,7 @@ class MyTestSCons(TestSCons.TestSCons):
]
def __init__(self):
- TestSCons.TestSCons.__init__(self)
+ super().__init__()
self.root = self.workpath('root')
self.prefix = self.root + os.path.splitdrive(sys.prefix)[1]
diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py
index 2eeaad0..1999f74 100644
--- a/testing/framework/TestCommon.py
+++ b/testing/framework/TestCommon.py
@@ -261,7 +261,7 @@ class TestCommon(TestCmd):
calling the base class initialization, and then changing directory
to the workdir.
"""
- TestCmd.__init__(self, **kw)
+ super().__init__(**kw)
os.chdir(self.workdir)
def options_arguments(self, options, arguments):
diff --git a/testing/framework/TestRuntest.py b/testing/framework/TestRuntest.py
index 18dcb94..9368953 100644
--- a/testing/framework/TestRuntest.py
+++ b/testing/framework/TestRuntest.py
@@ -146,7 +146,7 @@ class TestRuntest(TestCommon):
del kw['things_to_copy']
orig_cwd = os.getcwd()
- TestCommon.__init__(self, **kw)
+ super().__init__(**kw)
dirs = [os.environ.get('SCONS_RUNTEST_DIR', orig_cwd)]
diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py
index 0bf6abd..079e17d 100644
--- a/testing/framework/TestSCons.py
+++ b/testing/framework/TestSCons.py
@@ -321,7 +321,7 @@ class TestSCons(TestCommon):
if kw.get('ignore_python_version', -1) != -1:
del kw['ignore_python_version']
- TestCommon.__init__(self, **kw)
+ super().__init__(**kw)
if not self.external:
import SCons.Node.FS
@@ -1755,7 +1755,7 @@ class TimeSCons(TestSCons):
if 'verbose' not in kw and not self.calibrate:
kw['verbose'] = True
- TestSCons.__init__(self, *args, **kw)
+ super().__init__(*args, **kw)
# TODO(sgk): better way to get the script dir than sys.argv[0]
self.test_dir = os.path.dirname(sys.argv[0])
diff --git a/testing/framework/TestSCons_time.py b/testing/framework/TestSCons_time.py
index a57ca88..e647fe2 100644
--- a/testing/framework/TestSCons_time.py
+++ b/testing/framework/TestSCons_time.py
@@ -199,7 +199,7 @@ class TestSCons_time(TestCommon):
if 'workdir' not in kw:
kw['workdir'] = ''
- TestCommon.__init__(self, **kw)
+ super().__init__(**kw)
def archive_split(self, path):
if path[-7:] == '.tar.gz':
diff --git a/testing/framework/TestSConsign.py b/testing/framework/TestSConsign.py
index 699e929..b0562bf 100644
--- a/testing/framework/TestSConsign.py
+++ b/testing/framework/TestSConsign.py
@@ -64,7 +64,7 @@ class TestSConsign(TestSCons):
os.chdir(script_dir)
self.script_dir = os.getcwd()
- TestSCons.__init__(self, *args, **kw)
+ super().__init__(*args, **kw)
self.my_kw = {
'interpreter' : python, # imported from TestSCons
diff --git a/testing/framework/TestUnit/taprunner.py b/testing/framework/TestUnit/taprunner.py
index 0dde327..001db5c 100644
--- a/testing/framework/TestUnit/taprunner.py
+++ b/testing/framework/TestUnit/taprunner.py
@@ -43,29 +43,29 @@ class TAPTestResult(TextTestResult):
self.stream.flush()
def addSuccess(self, test):
- super(TextTestResult, self).addSuccess(test)
+ super().addSuccess(test)
self._process(test, "ok")
def addFailure(self, test, err):
- super(TextTestResult, self).addFailure(test, err)
+ super().addFailure(test, err)
self._process(test, "not ok", "FAIL")
# [ ] add structured data about assertion
def addError(self, test, err):
- super(TextTestResult, self).addError(test, err)
+ super().addError(test, err)
self._process(test, "not ok", "ERROR")
# [ ] add structured data about exception
def addSkip(self, test, reason):
- super(TextTestResult, self).addSkip(test, reason)
+ super().addSkip(test, reason)
self._process(test, "ok", directive=(" # SKIP %s" % reason))
def addExpectedFailure(self, test, err):
- super(TextTestResult, self).addExpectedFailure(test, err)
+ super().addExpectedFailure(test, err)
self._process(test, "not ok", directive=" # TODO")
def addUnexpectedSuccess(self, test):
- super(TextTestResult, self).addUnexpectedSuccess(test)
+ super().addUnexpectedSuccess(test)
self._process(test, "not ok", "FAIL (unexpected success)")
"""
@@ -90,7 +90,7 @@ class TAPTestRunner(TextTestRunner):
for case in test:
case.suite = test
- return super(TAPTestRunner, self).run(test)
+ return super().run(test)
if __name__ == "__main__":
--
cgit v0.12
From 5b0b9b89151f9abb0ed0ae7f47adfe3718088c3c Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Tue, 15 Mar 2022 08:52:06 -0600
Subject: Fix sider complaint
It wants blanks between method definitions, this was one of
the compressed proxy classes which was "touched" (not changed).
Signed-off-by: Mats Wichmann
---
SCons/Environment.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/SCons/Environment.py b/SCons/Environment.py
index 34e4968..3507263 100644
--- a/SCons/Environment.py
+++ b/SCons/Environment.py
@@ -2521,16 +2521,20 @@ def NoSubstitutionProxy(subject):
class _NoSubstitutionProxy(Environment):
def __init__(self, subject):
self.__dict__['__subject'] = subject
+
def __getattr__(self, name):
return getattr(self.__dict__['__subject'], name)
+
def __setattr__(self, name, value):
return setattr(self.__dict__['__subject'], name, value)
+
def executor_to_lvars(self, kwdict):
if 'executor' in kwdict:
kwdict['lvars'] = kwdict['executor'].get_lvars()
del kwdict['executor']
else:
kwdict['lvars'] = {}
+
def raw_to_mode(self, mapping):
try:
raw = mapping['raw']
@@ -2539,10 +2543,13 @@ def NoSubstitutionProxy(subject):
else:
del mapping['raw']
mapping['mode'] = raw
+
def subst(self, string, *args, **kwargs):
return string
+
def subst_kw(self, kw, *args, **kwargs):
return kw
+
def subst_list(self, string, *args, **kwargs):
nargs = (string, self,) + args
nkw = kwargs.copy()
@@ -2550,6 +2557,7 @@ def NoSubstitutionProxy(subject):
self.executor_to_lvars(nkw)
self.raw_to_mode(nkw)
return SCons.Subst.scons_subst_list(*nargs, **nkw)
+
def subst_target_source(self, string, *args, **kwargs):
nargs = (string, self,) + args
nkw = kwargs.copy()
@@ -2557,6 +2565,7 @@ def NoSubstitutionProxy(subject):
self.executor_to_lvars(nkw)
self.raw_to_mode(nkw)
return SCons.Subst.scons_subst(*nargs, **nkw)
+
return _NoSubstitutionProxy(subject)
# Local Variables:
--
cgit v0.12
From 17c7e6a59b311b1f3b8c5daedc749f815b2f9d74 Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Tue, 15 Mar 2022 15:13:50 -0500
Subject: updated startup sequence to check if server process died during
start.
---
SCons/Tool/ninja/NinjaState.py | 2 +-
SCons/Tool/ninja/ninja_run_daemon.py | 7 +++++--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py
index 11e928d..9fb697e 100644
--- a/SCons/Tool/ninja/NinjaState.py
+++ b/SCons/Tool/ninja/NinjaState.py
@@ -218,7 +218,7 @@ class NinjaState:
},
"SCONS_DAEMON": {
- "command": f"{sys.executable} {pathlib.Path(__file__).parent / 'ninja_run_daemon.py'} {scons_daemon_port} {get_path(env.get('NINJA_DIR'))} {str(env.get('NINJA_SCONS_DAEMON_KEEP_ALIVE'))} $SCONS_INVOCATION",
+ "command": f"{sys.executable} {pathlib.Path(__file__).parent / 'ninja_run_daemon.py'} {scons_daemon_port} {env.get('NINJA_DIR').abspath} {str(env.get('NINJA_SCONS_DAEMON_KEEP_ALIVE'))} $SCONS_INVOCATION",
"description": "Starting scons daemon...",
"pool": "local_pool",
# restat
diff --git a/SCons/Tool/ninja/ninja_run_daemon.py b/SCons/Tool/ninja/ninja_run_daemon.py
index 965e50b..fbeaf67 100644
--- a/SCons/Tool/ninja/ninja_run_daemon.py
+++ b/SCons/Tool/ninja/ninja_run_daemon.py
@@ -90,12 +90,15 @@ if not os.path.exists(ninja_builddir / "scons_daemon_dirty"):
if status != 200:
print(msg.decode("utf-8"))
exit(1)
- logging.debug(f"Request Done: {sys.argv[3]}")
+ logging.debug(f"Server Responded it was ready!")
break
except ConnectionRefusedError:
- logging.debug(f"Server not ready: {sys.argv[3]}")
+ logging.debug(f"Server not ready, server PID: {p.pid}")
time.sleep(1)
+ if p.poll is not None:
+ logging.debug(f"Server process died, aborting: {p.returncode}")
+ sys.exit(p.returncode)
except ConnectionResetError:
logging.debug("Server ConnectionResetError")
sys.stderr.write(error_msg)
--
cgit v0.12
From 0a1a6f14c8c954fa968b6d911fe1a3ad6d4881f8 Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Tue, 15 Mar 2022 16:12:07 -0500
Subject: adjust iterative speedup to pre-launch scons daemon before taking
timing
---
SCons/Tool/ninja/NinjaState.py | 3 ++-
SCons/Tool/ninja/__init__.py | 4 ++++
test/ninja/iterative_speedup.py | 10 ++++++----
3 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py
index 9fb697e..fff60be 100644
--- a/SCons/Tool/ninja/NinjaState.py
+++ b/SCons/Tool/ninja/NinjaState.py
@@ -534,10 +534,11 @@ class NinjaState:
)
ninja.build(
- ["run_scons_daemon", scons_daemon_dirty],
+ ["run_ninja_scons_daemon_phony", scons_daemon_dirty],
rule="SCONS_DAEMON",
)
+
daemon_dir = pathlib.Path(tempfile.gettempdir()) / ('scons_daemon_' + str(hashlib.md5(str(get_path(self.env["NINJA_DIR"])).encode()).hexdigest()))
pidfile = None
if os.path.exists(scons_daemon_dirty):
diff --git a/SCons/Tool/ninja/__init__.py b/SCons/Tool/ninja/__init__.py
index 913738a..9734ffc 100644
--- a/SCons/Tool/ninja/__init__.py
+++ b/SCons/Tool/ninja/__init__.py
@@ -206,6 +206,7 @@ def generate(env):
SCons.Warnings.SConsWarning("Generating multiple ninja files not supported, set ninja file name before tool initialization.")
ninja_file = [NINJA_STATE.ninja_file]
+
def ninja_generate_deps(env):
"""Return a list of SConscripts
TODO: Should we also include files loaded from site_scons/***
@@ -456,3 +457,6 @@ def generate(env):
env['TEMPFILEDIR'] = "$NINJA_DIR/.response_files"
env["TEMPFILE"] = NinjaNoResponseFiles
+
+
+ env.Alias('run-ninja-scons-daemon', 'run_ninja_scons_daemon_phony')
diff --git a/test/ninja/iterative_speedup.py b/test/ninja/iterative_speedup.py
index 05e372c..e5673b0 100644
--- a/test/ninja/iterative_speedup.py
+++ b/test/ninja/iterative_speedup.py
@@ -109,7 +109,7 @@ def generate_source(parent_source, current_source):
test.write('source_{}.h'.format(current_source), """
#include
#include
-
+
int
print_function%(current_source)s();
""" % locals())
@@ -125,7 +125,7 @@ def mod_source_return(test_num):
int
print_function%(test_num)s()
- {
+ {
int test = 5 + 5;
print_function%(parent_source)s();
return test;
@@ -143,7 +143,7 @@ def mod_source_orig(test_num):
int
print_function%(test_num)s()
- {
+ {
return print_function%(parent_source)s();
}
""" % locals())
@@ -190,13 +190,15 @@ jobs = '-j' + str(get_num_cpus())
ninja_program = [test.workpath('run_ninja_env.bat'), jobs] if IS_WINDOWS else [ninja_bin, jobs]
-start = time.perf_counter()
test.run(arguments='--disable-execute-ninja', stdout=None)
+test.run(program=ninja_program, arguments='run-ninja-scons-daemon', stdout=None)
+start = time.perf_counter()
test.run(program=ninja_program, stdout=None)
stop = time.perf_counter()
ninja_times += [stop - start]
test.run(program=test.workpath('print_bin'), stdout="main print")
+
for test_mod in tests_mods:
mod_source_return(test_mod)
start = time.perf_counter()
--
cgit v0.12
From 77ba2d3b905b284350d5773b6fb06d6abcdf5236 Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Wed, 16 Mar 2022 01:02:07 -0500
Subject: Added doc strings for ninja scripts
---
SCons/Tool/ninja/ninja_daemon_build.py | 15 +++++++++------
SCons/Tool/ninja/ninja_run_daemon.py | 13 ++++++++++++-
SCons/Tool/ninja/ninja_scons_daemon.py | 11 ++++++++++-
3 files changed, 31 insertions(+), 8 deletions(-)
diff --git a/SCons/Tool/ninja/ninja_daemon_build.py b/SCons/Tool/ninja/ninja_daemon_build.py
index 6e1c7be..f34935b 100644
--- a/SCons/Tool/ninja/ninja_daemon_build.py
+++ b/SCons/Tool/ninja/ninja_daemon_build.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python3
+#
# MIT License
#
# Copyright The SCons Foundation
@@ -22,7 +24,10 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
-
+This script is intended to execute a single build target. This script should be
+called by ninja, passing the port, ninja dir, and build target via arguments.
+The script then executes a simple get request to the scons daemon which is listening
+on from localhost on the set port.
"""
import http.client
@@ -74,14 +79,12 @@ while True:
logging.debug(f"Request Done: {sys.argv[3]}")
exit(0)
- except ConnectionRefusedError:
- logging.debug(f"Server not ready: {traceback.format_exc()}")
- time.sleep(1)
- except ConnectionResetError:
- logging.debug("Server ConnectionResetError")
+ except Exception:
+ logging.debug(f"Failed to send command: {traceback.format_exc()}")
exit(1)
+
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
diff --git a/SCons/Tool/ninja/ninja_run_daemon.py b/SCons/Tool/ninja/ninja_run_daemon.py
index fbeaf67..68d9893 100644
--- a/SCons/Tool/ninja/ninja_run_daemon.py
+++ b/SCons/Tool/ninja/ninja_run_daemon.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python3
+#
# MIT License
#
# Copyright The SCons Foundation
@@ -22,7 +24,16 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
+This script is intended to be called by ninja to start up the scons daemon process. It will
+launch the server and attempt to connect to it. This process needs to completely detach
+from the spawned process so ninja can consider the build edge completed. It should be passed
+the args which should be forwarded to the scons daemon process which could be any number of
+# arguments. However the first few arguements are required to be port, ninja dir, and keep alive
+timeout in seconds.
+The scons_daemon_dirty file acts as a pidfile marker letting this script quickly skip over
+restarting the server if the server is running. The assumption here is the pidfile should only
+exist if the server is running.
"""
import subprocess
@@ -90,7 +101,7 @@ if not os.path.exists(ninja_builddir / "scons_daemon_dirty"):
if status != 200:
print(msg.decode("utf-8"))
exit(1)
- logging.debug(f"Server Responded it was ready!")
+ logging.debug("Server Responded it was ready!")
break
except ConnectionRefusedError:
diff --git a/SCons/Tool/ninja/ninja_scons_daemon.py b/SCons/Tool/ninja/ninja_scons_daemon.py
index 99fcbb9..88f3b22 100644
--- a/SCons/Tool/ninja/ninja_scons_daemon.py
+++ b/SCons/Tool/ninja/ninja_scons_daemon.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python3
+#
# MIT License
#
# Copyright The SCons Foundation
@@ -22,7 +24,14 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
-
+This script primarily consists of two threads, the http server thread and the scons interactive
+process thread. The http server thread will listen on the passed port for http get request
+which should indicate some action for the scons interactive process to take.
+
+The daemon will keep log files in a tmp directory correlated to the hash of the absolute path
+of the ninja build dir passed. The daemon will also use a keep alive time to know when to shut
+itself down after the passed timeout of no activity. Any time the server receives a get request,
+the keep alive time will be reset.
"""
import http.server
--
cgit v0.12
From 77a1e99d356990b03c2be014658a072a5d2be4c1 Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Wed, 16 Mar 2022 01:08:36 -0500
Subject: fix spelling mistake
---
SCons/Tool/ninja/ninja_run_daemon.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SCons/Tool/ninja/ninja_run_daemon.py b/SCons/Tool/ninja/ninja_run_daemon.py
index 68d9893..057537a 100644
--- a/SCons/Tool/ninja/ninja_run_daemon.py
+++ b/SCons/Tool/ninja/ninja_run_daemon.py
@@ -28,7 +28,7 @@ This script is intended to be called by ninja to start up the scons daemon proce
launch the server and attempt to connect to it. This process needs to completely detach
from the spawned process so ninja can consider the build edge completed. It should be passed
the args which should be forwarded to the scons daemon process which could be any number of
-# arguments. However the first few arguements are required to be port, ninja dir, and keep alive
+# arguments. However the first few arguments are required to be port, ninja dir, and keep alive
timeout in seconds.
The scons_daemon_dirty file acts as a pidfile marker letting this script quickly skip over
--
cgit v0.12
From c47a00687a2753c00af8682f00bfe671671074b5 Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Thu, 17 Mar 2022 12:36:44 -0600
Subject: Add simple unittest for -include
Missing test was flagged by code coverage because the line was
"touched" due to a variable rename.
Signed-off-by: Mats Wichmann
---
SCons/EnvironmentTests.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py
index c505422..8359405 100644
--- a/SCons/EnvironmentTests.py
+++ b/SCons/EnvironmentTests.py
@@ -810,11 +810,13 @@ sys.exit(0)
"-fmerge-all-constants "
"-fopenmp "
"-mno-cygwin -mwindows "
- "-arch i386 -isysroot /tmp "
+ "-arch i386 "
+ "-isysroot /tmp "
"-iquote /usr/include/foo1 "
"-isystem /usr/include/foo2 "
"-idirafter /usr/include/foo3 "
"-imacros /usr/include/foo4 "
+ "-include /usr/include/foo5 "
"--param l1-cache-size=32 --param l2-cache-size=6144 "
"+DD64 "
"-DFOO -DBAR=value -D BAZ "
@@ -832,6 +834,7 @@ sys.exit(0)
('-isystem', '/usr/include/foo2'),
('-idirafter', '/usr/include/foo3'),
('-imacros', env.fs.File('/usr/include/foo4')),
+ ('-include', env.fs.File('/usr/include/foo5')),
('--param', 'l1-cache-size=32'), ('--param', 'l2-cache-size=6144'),
'+DD64'], repr(d['CCFLAGS'])
assert d['CXXFLAGS'] == ['-std=c++0x'], repr(d['CXXFLAGS'])
--
cgit v0.12
From 915bfb3a55619175e03c3dc3573ad5ba83611e75 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Fri, 18 Mar 2022 17:19:04 -0700
Subject: flake8 fixes
---
test/ninja/ninja_handle_control_c_rebuild.py | 2 +-
test/ninja/ninja_test_sconscripts/sconstruct_control_c_ninja | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/test/ninja/ninja_handle_control_c_rebuild.py b/test/ninja/ninja_handle_control_c_rebuild.py
index c2c8c8d..9f6b413 100644
--- a/test/ninja/ninja_handle_control_c_rebuild.py
+++ b/test/ninja/ninja_handle_control_c_rebuild.py
@@ -22,7 +22,7 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
"""
-This test ensures if ninja get's a control-c (or other interrupting signal) while
+This test ensures if ninja gets a control-c (or other interrupting signal) while
regenerating the build.ninja, it doesn't remove the build.ninja leaving it
in an unworkable state.
"""
diff --git a/test/ninja/ninja_test_sconscripts/sconstruct_control_c_ninja b/test/ninja/ninja_test_sconscripts/sconstruct_control_c_ninja
index 0124576..34d7872 100644
--- a/test/ninja/ninja_test_sconscripts/sconstruct_control_c_ninja
+++ b/test/ninja/ninja_test_sconscripts/sconstruct_control_c_ninja
@@ -1,7 +1,7 @@
import os
import signal
-SetOption('experimental','ninja')
+SetOption('experimental', 'ninja')
DefaultEnvironment(tools=[])
env = Environment()
@@ -9,4 +9,4 @@ env.Tool('ninja')
env.Program(target='foo', source='foo.c')
if ARGUMENTS.get('NINJA_DISABLE_AUTO_RUN', 0):
- os.kill(os.getppid(),signal.SIGINT )
+ os.kill(os.getppid(), signal.SIGINT)
--
cgit v0.12
From ec47a7085b922d977c13f1e309ee549af43b0782 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Fri, 18 Mar 2022 17:23:08 -0700
Subject: Only swap path dirsep if needed
---
SCons/Tool/ninja/Methods.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/SCons/Tool/ninja/Methods.py b/SCons/Tool/ninja/Methods.py
index c89c42f..4c6aa2f 100644
--- a/SCons/Tool/ninja/Methods.py
+++ b/SCons/Tool/ninja/Methods.py
@@ -257,7 +257,10 @@ def gen_get_response_file_command(env, rule, tool, tool_is_dynamic=False, custom
)
cmd, rsp_content = cmd_list[:tool_idx], cmd_list[tool_idx:]
- rsp_content = [rsp_content_item.replace('\\', '/') for rsp_content_item in rsp_content]
+
+ # Canonicalize the path to have forward (posix style) dir sep characters.
+ if os.altsep:
+ rsp_content = [rsp_content_item.replace(os.sep, os.altsep) for rsp_content_item in rsp_content]
rsp_content = ['"' + rsp_content_item + '"' for rsp_content_item in rsp_content]
rsp_content = " ".join(rsp_content)
--
cgit v0.12
From c9148823b065e0892c7269dbbc4217d80bc40c51 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Sat, 19 Mar 2022 07:40:53 -0400
Subject: Replace environment variable names VS70COMNTOOLS and VS60COMNTOOLS
with VSCOMNTOOLS and MSDevDir, respectively. MSDevDir does not point to the
MSVS 6.0 common folder but is included as it is optionally defined by the
installer and is used for the VisualStudio definitions. With the definition
of VSCOMNTOOLS for 7.0, the traditional vcvars32.bat can be configured for
MSVC 7.1 and 7.0. Unify the batch file code for msvc 6.0-7.1 and suppress
the build argument as the native batch files do not process any arguments.
While not strictly necessary, this potentially is useful when caching is
enabled.
---
CHANGES.txt | 4 ++++
RELEASE.txt | 6 ++++++
SCons/Tool/MSCommon/common.py | 4 ++--
SCons/Tool/MSCommon/vc.py | 6 ++----
SCons/Tool/MSCommon/vs.py | 4 ++--
5 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index d36e911..ab23aa4 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -12,6 +12,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
From Joseph Brill:
- Verify that a user specified msvc script (via MSVC_USE_SCRIPT) exists and raise an exception
when the user specified msvc script does not exist.
+ - Replace non-existent MSVS environment variables VS70COMNTOOLS and VS60COMNTOOLS with
+ VSCOMNTOOLS and MSDevDir for MSVC 7.0 and 6.0, respectively. Change the MSVS 7.x batch
+ files used from vsvars32.bat to vcvars32.bat. Suppress passing arguments to the MSVS 6.0-7.1
+ batch files as no arguments are required.
From William Deegan:
- Fix check for unsupported Python version. It was broken. Also now the error message
diff --git a/RELEASE.txt b/RELEASE.txt
index 4f963b5..721573a 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -36,6 +36,10 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
- The change to "content" and "content-timestamp" Decider names is reflected
in the User Guide as well, since the hash function may be other than md5
(tidying up from earlier change)
+- The MSVC batch file names used for MSVC versions 6.0 to 7.1 were unified after the
+ requisite MSVC environment variable for version 7.0 was updated. The build argument
+ is now suppressed for the MSVC 6.0 to 7.1 batch files to be consistent with the expected
+ batch file usage.
FIXES
@@ -45,6 +49,8 @@ FIXES
with python 3.9 (or higher)
- Fixed crash in C scanner's dictify_CPPDEFINES() function which happens if
AppendUnique is called on CPPPATH. (Issue #4108).
+- The environment variable names used when constructing the MSVC 7.0 and 6.0 environments were
+ updated to be consistent with the names defined by the respective installers.
IMPROVEMENTS
diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py
index ee31b2d..29b63ae 100644
--- a/SCons/Tool/MSCommon/common.py
+++ b/SCons/Tool/MSCommon/common.py
@@ -253,8 +253,8 @@ def get_output(vcbat, args=None, env=None):
'VS90COMNTOOLS',
'VS80COMNTOOLS',
'VS71COMNTOOLS',
- 'VS70COMNTOOLS',
- 'VS60COMNTOOLS',
+ 'VSCOMNTOOLS',
+ 'MSDevDir',
'VSCMD_DEBUG', # enable logging and other debug aids
'VSCMD_SKIP_SENDTELEMETRY',
]
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index fe31cb3..c2314d3 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -512,12 +512,10 @@ def find_batch_file(env,msvc_version,host_arch,target_arch):
msvc_ver_numeric = get_msvc_version_numeric(msvc_version)
use_arg = True
vernum = float(msvc_ver_numeric)
- if 7 <= vernum < 8:
- pdir = os.path.join(pdir, os.pardir, "Common7", "Tools")
- batfilename = os.path.join(pdir, "vsvars32.bat")
- elif vernum < 7:
+ if vernum < 8:
pdir = os.path.join(pdir, "Bin")
batfilename = os.path.join(pdir, "vcvars32.bat")
+ use_arg = False
elif 8 <= vernum <= 14:
batfilename = os.path.join(pdir, "vcvarsall.bat")
else: # vernum >= 14.1 VS2017 and above
diff --git a/SCons/Tool/MSCommon/vs.py b/SCons/Tool/MSCommon/vs.py
index 35d21e0..08c3cf5 100644
--- a/SCons/Tool/MSCommon/vs.py
+++ b/SCons/Tool/MSCommon/vs.py
@@ -391,7 +391,7 @@ SupportedVSList = [
VisualStudio('7.0',
sdk_version='2003R2',
hkeys=[r'Microsoft\VisualStudio\7.0\Setup\VS\ProductDir'],
- common_tools_var='VS70COMNTOOLS',
+ common_tools_var='VSCOMNTOOLS',
executable_path=r'Common7\IDE\devenv.com',
batch_file_path=r'Common7\Tools\vsvars32.bat',
default_dirname='Microsoft Visual Studio .NET',
@@ -403,7 +403,7 @@ SupportedVSList = [
sdk_version='2003R1',
hkeys=[r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio\ProductDir',
'use_dir'],
- common_tools_var='VS60COMNTOOLS',
+ common_tools_var='MSDevDir',
executable_path=r'Common\MSDev98\Bin\MSDEV.COM',
batch_file_path=r'Common7\Tools\vsvars32.bat',
default_dirname='Microsoft Visual Studio',
--
cgit v0.12
From f7e544e5aac6656775993ab97bebfb2948e7e318 Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Fri, 18 Mar 2022 08:21:53 -0600
Subject: Guide: update Writing Builders chapter [skip appveyor]
Use entities more consistently. Reword a few things.
Text implied that site_init.py needs to be manually imported,
adjusted wording not to say so.
Signed-off-by: Mats Wichmann
---
doc/scons.mod | 24 +++----
doc/user/builders-writing.xml | 146 +++++++++++++++++++++---------------------
2 files changed, 85 insertions(+), 85 deletions(-)
diff --git a/doc/scons.mod b/doc/scons.mod
index 5579d4d..8a6df17 100644
--- a/doc/scons.mod
+++ b/doc/scons.mod
@@ -440,15 +440,15 @@
Dictionary">
-Emitter">
-emitter">
+Emitter">
+emitter">
-factory">
+factory">
-Generator">
-generator">
+Generator">
+generator">
-Nodes">
+Nodes">
content signature">
content signatures">
@@ -462,12 +462,12 @@
-action=">
-batch_key=">
-cmdstr=">
-exitstatfunc=">
-strfunction=">
-varlist=">
+action=">
+batch_key=">
+cmdstr=">
+exitstatfunc=">
+strfunction=">
+varlist=">
diff --git a/doc/user/builders-writing.xml b/doc/user/builders-writing.xml
index 5a8851d..a53e70e 100644
--- a/doc/user/builders-writing.xml
+++ b/doc/user/builders-writing.xml
@@ -2,7 +2,7 @@
%scons;
-
+
%builders-mod;
@@ -81,14 +81,14 @@
-bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+bld = Builder(action='foobuild < $SOURCE > $TARGET')
All the above line does is create a free-standing
&Builder; object.
- The next section will show us how to actually use it.
+ The next section will show how to actually use it.
@@ -105,7 +105,7 @@ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
for files to be built.
This is done through the &cv-link-BUILDERS;
&consvar; in an environment.
- The &cv-BUILDERS; variable is a Python dictionary
+ The &cv-link-BUILDERS; variable is a &Python; dictionary
that maps the names by which you want to call
various &Builder; objects to the objects themselves.
For example, if we want to call the
@@ -221,7 +221,7 @@ hello.c
To be able to use both our own defined &Builder; objects
and the default &Builder; objects in the same &consenv;,
- you can either add to the &cv-BUILDERS; variable
+ you can either add to the &cv-link-BUILDERS; variable
using the &Append; function:
@@ -296,8 +296,8 @@ env.Program('hello.c')
suffixes to the target and/or the source file.
For example, rather than having to specify
explicitly that you want the Foo
- &Builder; to build the file.foo
- target file from the file.input source file,
+ &Builder; to build the file.foo
+ target file from the file.input source file,
you can give the .foo
and .input suffixes to the &Builder;,
making for more compact and readable calls to
@@ -361,7 +361,7 @@ env.Foo('file2')
In &SCons;, you don't have to call an external command
to build a file.
- You can, instead, define a Python function
+ You can, instead, define a &Python; function
that a &Builder; object can invoke
to build your target file (or files).
Such a &buildfunc; definition looks like:
@@ -383,7 +383,7 @@ def build_function(target, source, env):
- target
+ target
@@ -392,14 +392,14 @@ def build_function(target, source, env):
the target or targets to be
built by this function.
The file names of these target(s)
- may be extracted using the Python &str; function.
+ may be extracted using the &Python; &str; function.
- source
+ source
@@ -408,21 +408,21 @@ def build_function(target, source, env):
the sources to be
used by this function to build the targets.
The file names of these source(s)
- may be extracted using the Python &str; function.
+ may be extracted using the &Python; &str; function.
- env
+ env
The &consenv; used for building the target(s).
The function may use any of the
- environment's construction variables
+ environment's &consvars;
in any way to affect how it builds the targets.
@@ -446,7 +446,7 @@ def build_function(target, source, env):
- Once you've defined the Python function
+ Once you've defined the &Python; function
that will build your target file,
defining a &Builder; object for it is as
simple as specifying the name of the function,
@@ -479,7 +479,7 @@ file.input
And notice that the output changes slightly,
- reflecting the fact that a Python function,
+ reflecting the fact that a &Python; function,
not an external command,
is now called to build the target file:
@@ -497,8 +497,8 @@ file.input
&SCons; Builder objects can create an action "on the fly"
- by using a function called a &generator;.
- (Note: this is not the same thing as a Python generator function
+ by using a function called a &Generator;.
+ (Note: this is not the same thing as a &Python; generator function
described in PEP 255)
This provides a great deal of flexibility to
construct just the right list of commands
@@ -521,7 +521,7 @@ def generate_actions(source, target, env, for_signature):
- source
+ source
@@ -531,7 +531,7 @@ def generate_actions(source, target, env, for_signature):
by the command or other action
generated by this function.
The file names of these source(s)
- may be extracted using the Python &str; function.
+ may be extracted using the &Python; &str; function.
@@ -539,7 +539,7 @@ def generate_actions(source, target, env, for_signature):
- target
+ target
@@ -549,7 +549,7 @@ def generate_actions(source, target, env, for_signature):
by the command or other action
generated by this function.
The file names of these target(s)
- may be extracted using the Python &str; function.
+ may be extracted using the &Python; &str; function.
@@ -557,14 +557,14 @@ def generate_actions(source, target, env, for_signature):
- env
+ env
The &consenv; used for building the target(s).
- The generator may use any of the
- environment's construction variables
+ The &generator; may use any of the
+ environment's &consvars;
in any way to determine what command
or other action to return.
@@ -574,13 +574,13 @@ def generate_actions(source, target, env, for_signature):
- for_signature
+ for_signature
A flag that specifies whether the
- generator is being called to contribute to a build signature,
+ &generator; is being called to contribute to a &buildsig;,
as opposed to actually executing the command.
@@ -604,8 +604,8 @@ def generate_actions(source, target, env, for_signature):
Once you've defined a &generator;,
you create a &Builder; to use it
- by specifying the generator keyword argument
- instead of action.
+ by specifying the generator keyword argument
+ instead of action.
@@ -652,9 +652,9 @@ env.Foo('file')
Note that it's illegal to specify both an
- action
+ action
and a
- generator
+ generator
for a &Builder;.
@@ -672,7 +672,7 @@ env.Foo('file')
that takes as its arguments
the list of the targets passed to the builder,
the list of the sources passed to the builder,
- and the construction environment.
+ and the &consenv;.
The emitter function should return the modified
lists of targets that should be built
and sources from which the targets will be built.
@@ -739,7 +739,7 @@ env.Foo('file')
-
+
And would yield the following output:
@@ -751,16 +751,15 @@ env.Foo('file')
One very flexible thing that you can do is
- use a construction variable to specify
- different emitter functions for different
- construction variable.
+ use a &consvar; to specify
+ different emitter functions for different &consenvs;.
To do this, specify a string
- containing a construction variable
+ containing a &consvar;
expansion as the emitter when you call
the &f-link-Builder; function,
- and set that construction variable to
+ and set that &consvar; to
the desired emitter function
- in different construction environments:
+ in different &consenvs;:
@@ -827,9 +826,9 @@ cat
is a powerful concept, but sometimes all you really want
is to be able to use an existing builder but change its
concept of what targets are created.
- In this case,
+ In this case,
trying to recreate the logic of an existing Builder to
- supply a special emitter can be a lot of work.
+ supply a special emitter can be a lot of work.
The typical case for this is when you want to use a compiler flag
that causes additional files to be generated.
For example the GNU linker accepts an option
@@ -844,12 +843,12 @@ cat
To help with this, &SCons; provides &consvars; which correspond
- to a few standard builders:
- &cv-link-PROGEMITTER; for &b-link-Program;;
- &cv-link-LIBEMITTER; for &b-link-Library;;
- &cv-link-SHLIBEMITTER; for &b-link-SharedLibrary; and
+ to a few standard builders:
+ &cv-link-PROGEMITTER; for &b-link-Program;;
+ &cv-link-LIBEMITTER; for &b-link-Library;;
+ &cv-link-SHLIBEMITTER; for &b-link-SharedLibrary; and
&cv-link-LDMODULEEMITTER; for &b-link-LoadableModule;;.
- Adding an emitter to one of these will cause it to be
+ Adding an emitter to one of these will cause it to be
invoked in addition to any existing emitter for the
corresponding builder.
@@ -944,10 +943,10 @@ main()
The site_scons directories give you a place to
- put Python modules and packages that you can import into your &SConscript; files
- (site_scons),
+ put &Python; modules and packages that you can import into your
+ &SConscript; files (at the top level)
add-on tools that can integrate into &SCons;
- (site_scons/site_tools),
+ (in a site_tools subdirectory),
and a site_scons/site_init.py file that
gets read before any &SConstruct; or &SConscript; file,
allowing you to change &SCons;'s default behavior.
@@ -957,8 +956,10 @@ main()
Each system type (Windows, Mac, Linux, etc.) searches a canonical
- set of directories for site_scons; see the man page for details.
- The top-level SConstruct's site_scons dir is always searched last,
+ set of directories for site_scons;
+ see the man page for details.
+ The top-level SConstruct's site_scons dir
+ (that is, the one in the project) is always searched last,
and its dir is placed first in the tool path so it overrides all
others.
@@ -969,8 +970,8 @@ main()
If you get a tool from somewhere (the &SCons; wiki or a third party,
for instance) and you'd like to use it in your project, a
site_scons dir is the simplest place to put it.
- Tools come in two flavors; either a Python function that operates on
- an &Environment; or a Python module or package containing two functions,
+ Tools come in two flavors; either a &Python; function that operates on
+ an &Environment; or a &Python; module or package containing two functions,
exists() and generate().
@@ -1023,7 +1024,7 @@ env.AddHeader('tgt', 'src')
-
is an internal &SCons; object
which automatically converts
- the options we specified as a string into a list.
+ the options you specify as a string into a list.
@@ -109,7 +111,7 @@ env = Environment()
env.Append(CPPPATH=['/include', '/usr/local/include', '/usr/include'])
flags = {'CPPPATH': ['/usr/opt/include', '/usr/local/include']}
env.MergeFlags(flags)
-print(env['CPPPATH'])
+print("CPPPATH:", env['CPPPATH'])
@@ -124,9 +126,9 @@ print(env['CPPPATH'])
[TODO: for when we make CLVar public]
is a Python list, not a CLVar,
-->
- is a normal Python list,
- so we must specify its values as a list
- in the dictionary we pass to the &MergeFlags; function.
+ is a normal &Python; list,
+ so you should give its values as a list
+ in the dictionary you pass to the &MergeFlags; function.
@@ -143,8 +145,8 @@ env = Environment()
env.Append(CCFLAGS='-option -O3 -O1')
env.Append(CPPPATH=['/include', '/usr/local/include', '/usr/include'])
env.MergeFlags('-whatever -I/usr/opt/include -O3 -I/usr/local/include')
-print(env['CCFLAGS'])
-print(env['CPPPATH'])
+print("CCFLAGS:", env['CCFLAGS'])
+print("CPPPATH:", env['CPPPATH'])
@@ -157,8 +159,8 @@ print(env['CPPPATH'])
In the combined example above,
&ParseFlags; has sorted the options into their corresponding variables
and returned a dictionary for &MergeFlags; to apply
- to the construction variables
- in the specified construction environment.
+ to the &consvars;
+ in the specified &consenv;.
diff --git a/doc/user/parseconfig.xml b/doc/user/parseconfig.xml
index a07201a..fc9a889 100644
--- a/doc/user/parseconfig.xml
+++ b/doc/user/parseconfig.xml
@@ -2,7 +2,7 @@
%scons;
-
+
%builders-mod;
@@ -21,7 +21,9 @@
-(dirs=subdirs, [name=script, exports, variant_dir, duplicate, must_exist])
-
+(dirs=subdirs, [name=scriptname, exports, variant_dir, duplicate, must_exist])
+
@@ -374,31 +374,27 @@ There are two ways to call the
-The first calling style
-is to explicitly specify one or more
-scripts
-as the first argument.
+The first calling style is to supply
+one or more SConscript file names
+as the first (positional) argument.
A single script may be specified as a string;
-multiple scripts must be specified as a list
+multiple scripts must be specified as a list of strings
(either explicitly or as created by
a function like
&f-link-Split;).
Examples:
-SConscript('SConscript') # run SConscript in the current directory
+SConscript('SConscript') # run SConscript in the current directory
SConscript('src/SConscript') # run SConscript in the src directory
SConscript(['src/SConscript', 'doc/SConscript'])
config = SConscript('MyConfig.py')
-The second way to call
-&f-SConscript;
-is to specify a list of directory names
-using the
-dirs=subdirs
-keyword argument.
+The other calling style is to omit the positional argument naming
+scripts and instead specify a list of directory names using the
+dirs keyword argument.
In this case,
&scons;
will
@@ -408,14 +404,13 @@ in each of the specified directories.
You may specify a name other than
&SConscript;
by supplying an optional
-name=script
-keyword argument.
+name keyword argument.
The first three examples below have the same effect
as the first three examples above:
-SConscript(dirs='.') # run SConscript in the current directory
-SConscript(dirs='src') # run SConscript in the src directory
+SConscript(dirs='.') # run SConscript in the current directory
+SConscript(dirs='src') # run SConscript in the src directory
SConscript(dirs=['src', 'doc'])
SConscript(dirs=['sub1', 'sub2'], name='MySConscript')
@@ -423,8 +418,12 @@ SConscript(dirs=['sub1', 'sub2'], name='MySConscript')
The optional
exports
-argument provides a string or list of strings representing
+keyword argument provides a string or list of strings representing
variable names, or a dictionary of named values, to export.
+For the first calling style only, a second positional argument
+will be interpreted as exports; the
+second calling style must use the keyword argument form
+for exports.
These variables are locally exported only to the called
SConscript file(s)
and do not affect the global pool of variables managed by the
@@ -585,11 +584,11 @@ SConscript('src/SConscript', variant_dir='build/ppc', duplicate=0)
&f-SConscript; returns the values of any variables
-named by the executed SConscript(s) in arguments
-to the &f-link-Return; function (see above for details).
+named by the executed SConscript file(s) in arguments
+to the &f-link-Return; function.
If a single &f-SConscript; call causes multiple scripts to
be executed, the return value is a tuple containing
-the returns of all of the scripts. If an executed
+the returns of each of the scripts. If an executed
script does not explicitly call &Return;, it returns
None.
--
cgit v0.12
From 45e9eec5a86adcd815a9bebfba536a44c7f9a756 Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Tue, 17 May 2022 09:01:11 -0600
Subject: Fix grammar error ("the the") [ci skip]
Signed-off-by: Mats Wichmann
---
SCons/Environment.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SCons/Environment.xml b/SCons/Environment.xml
index 3700a44..346fc75 100644
--- a/SCons/Environment.xml
+++ b/SCons/Environment.xml
@@ -3421,7 +3421,7 @@ Sets up a mapping to define a variant build directory in
src_dir may not be underneath
variant_dir.
A &f-VariantDir; mapping is global, even if called using the
-the &f-env-VariantDir; form.
+&f-env-VariantDir; form.
&f-VariantDir;
can be called multiple times with the same
src_dir
--
cgit v0.12
From b14c20e353712291e0321c0fa1e7557bf385ea04 Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Mon, 23 May 2022 08:18:51 -0600
Subject: Further tweak of VariantDir doc [skip appveyor]
Signed-off-by: Mats Wichmann
---
SCons/Environment.xml | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/SCons/Environment.xml b/SCons/Environment.xml
index 346fc75..294562d 100644
--- a/SCons/Environment.xml
+++ b/SCons/Environment.xml
@@ -3482,14 +3482,11 @@ The subsidiary SConscript file must be called as if it were in
variant_dir,
regardless of the value of
duplicate.
-When calling an SConscript file,
-you can pass an appropriately set up environment
-using the exports keyword
-argument so the SConscript can pick up the right settings
-for that variant build.
-This is how you tell
-&scons;
-which variant of a source directory to build:
+When calling an SConscript file, you can use the
+exports keyword argument
+to pass parameters (individually or as an appropriately set up environment)
+so the SConscript can pick up the right settings for that variant build.
+The SConscript must &f-link-Import; these to use them. Example:
@@ -3511,7 +3508,7 @@ in conjunction with calling a subsidiary SConscript file.
-Examples:
+More examples:
--
cgit v0.12
From cf2641d678eb6142f5f00e686f8e4d46959267ee Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Wed, 25 May 2022 13:31:38 -0600
Subject: Typo fix [ci skip]
Signed-off-by: Mats Wichmann
---
SCons/Environment.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/SCons/Environment.xml b/SCons/Environment.xml
index 294562d..5c4326c 100644
--- a/SCons/Environment.xml
+++ b/SCons/Environment.xml
@@ -318,7 +318,7 @@ Added methods propagate through &f-env-Clone; calls.
-Examples:
+More examples:
@@ -3477,7 +3477,7 @@ if it causes problems.
&f-VariantDir;
-works most naturally with used with a subsidiary SConscript file.
+works most naturally when used with a subsidiary SConscript file.
The subsidiary SConscript file must be called as if it were in
variant_dir,
regardless of the value of
--
cgit v0.12
From a4daaa6d49f7f5972e8d7d083d492068b1a5d362 Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Thu, 26 May 2022 06:41:09 -0600
Subject: SConscript doc tweaks per #4150 review comments [skip appveyor]
Signed-off-by: Mats Wichmann
---
SCons/Script/SConscript.xml | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/SCons/Script/SConscript.xml b/SCons/Script/SConscript.xml
index 17c9236..eb52acc 100644
--- a/SCons/Script/SConscript.xml
+++ b/SCons/Script/SConscript.xml
@@ -404,7 +404,8 @@ in each of the specified directories.
You may specify a name other than
&SConscript;
by supplying an optional
-name keyword argument.
+name=scriptname
+keyword argument.
The first three examples below have the same effect
as the first three examples above:
@@ -448,7 +449,7 @@ If the optional
variant_dir
argument is present, it causes an effect equivalent to the
&f-link-VariantDir; function,
-but in effect only during the execution of the SConscript file.
+but in effect only within the scope of the &f-SConscript; call.
The variant_dir
argument is interpreted relative to the directory of the
calling SConscript file.
--
cgit v0.12
From 9f6cfbdd5dc35a9711e5c5ab5ef3414b4a05e0ef Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Thu, 26 May 2022 22:58:55 -0500
Subject: collapsed related CHANGES comments
---
CHANGES.txt | 9 +++------
RELEASE.txt | 9 +++------
2 files changed, 6 insertions(+), 12 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 60e934f..d5aba9f 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -99,12 +99,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- Added user configurable setting of ninja depfile format via NINJA_DEPFILE_PARSE_FORMAT.
Now setting NINJA_DEPFILE_PARSE_FORMAT to [msvc,gcc,clang] can force the ninja expected
format. Compiler tools will also configure the variable automatically.
- - Added SHELL_ENV_GENERATOR construction variable. This variable allows the user to Define
- a function which will be called to generate or alter the execution environment which will
- be used in the shell command of some Action.
- - Updated SHELL_ENV_GENERATOR construction variable to SHELL_ENV_GENERATORS. This variable
- is now an iterable which will contain functions which each are called and each can customize
- the execution environment.
+ - Added SHELL_ENV_GENERATORS construction variable. This variable
+ is an iterable which will contain functions in which each are called and each can allow
+ the user a method to customize the execution environment.
- Updated ninja scons daemon scripts to output errors to stderr as well as the daemon log.
- Fix typo in ninja scons daemon startup which causes ConnectionRefusedError to not retry
to connect to the server during start up.
diff --git a/RELEASE.txt b/RELEASE.txt
index 38fbbd7..195e325 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -16,12 +16,9 @@ NEW FUNCTIONALITY
- Added MSVC_USE_SCRIPT_ARGS variable to pass arguments to MSVC_USE_SCRIPT.
- Added Configure.CheckMember() checker to check if struct/class has the specified member.
-- Added SHELL_ENV_GENERATOR construction variables. This variable allows the user to Define
- a function which will be called to generate or alter the execution environment which will
- be used in the shell command of some Action.
-- Updated SHELL_ENV_GENERATOR construction variable to SHELL_ENV_GENERATORS. This variable
- is now an iterable which will contain functions which each are called and each can customize
- the execution environment.
+- Added SHELL_ENV_GENERATORS construction variable. This variable
+ is an iterable which will contain functions in which each are called and each can allow
+ the user a method to customize the execution environment.
- Added MSVC_USE_SETTINGS variable to pass a dictionary to configure the msvc compiler
system environment as an alternative to bypassing Visual Studio autodetection entirely.
--
cgit v0.12
From f262fbd6bacf1c0d7f26d747d853b9ce909f4442 Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Thu, 26 May 2022 23:03:13 -0500
Subject: fix missing f string
---
SCons/Action.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SCons/Action.py b/SCons/Action.py
index 38570ce..e29c4e9 100644
--- a/SCons/Action.py
+++ b/SCons/Action.py
@@ -768,7 +768,7 @@ def _resolve_shell_env(env, target, source):
for generator in shell_gens:
ENV = generator(env, target, source, ENV)
if not isinstance(ENV, dict):
- raise SCons.Errors.UserError("SHELL_ENV_GENERATORS function: {generator} must return a dict.")
+ raise SCons.Errors.UserError(f"SHELL_ENV_GENERATORS function: {generator} must return a dict.")
return ENV
--
cgit v0.12
From 150197f2273fa639aa4815a68f9bcd38d3068a8d Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Mon, 30 May 2022 12:28:57 -0600
Subject: Fix some Py 3.11 depr warns in tests
A couple of unittest methods that a few SCons tests use have
been marked deprecated in Python 3.11, with replacements provided.
Partial fix for #4162, do not close just off this PR.
Signed-off-by: Mats Wichmann
---
CHANGES.txt | 2 ++
SCons/Scanner/ScannerTests.py | 4 +++-
SCons/Tool/ToolTests.py | 4 +++-
SCons/Tool/msvsTests.py | 4 +++-
SCons/cppTests.py | 4 +++-
testing/framework/TestCmdTests.py | 6 ++++--
testing/framework/TestCommonTests.py | 6 ++++--
7 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 9a6a26f..7d3eadb 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -93,6 +93,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
"dict" (avoid redefining builtin)
- Fix an old use-before-set bug in tex tool (issue #2888)
- Fix a test harness exception returning stderr if a wait_for timed out.
+ - Modernize a few tests that use now-deprecated unittest.getTestCaseNames
+ and unittest.makeSuite - Python itself suggests the replacements.
From Zhichang Yu:
- Added MSVC_USE_SCRIPT_ARGS variable to pass arguments to MSVC_USE_SCRIPT.
diff --git a/SCons/Scanner/ScannerTests.py b/SCons/Scanner/ScannerTests.py
index 68332a0..b9cb209 100644
--- a/SCons/Scanner/ScannerTests.py
+++ b/SCons/Scanner/ScannerTests.py
@@ -621,7 +621,9 @@ def suite():
ClassicCPPTestCase,
]
for tclass in tclasses:
- names = unittest.getTestCaseNames(tclass, 'test_')
+ loader = unittest.TestLoader()
+ loader.testMethodPrefix = 'test_'
+ names = loader.getTestCaseNames(tclass)
suite.addTests(list(map(tclass, names)))
return suite
diff --git a/SCons/Tool/ToolTests.py b/SCons/Tool/ToolTests.py
index 7cff7c8..9338c2d 100644
--- a/SCons/Tool/ToolTests.py
+++ b/SCons/Tool/ToolTests.py
@@ -116,7 +116,9 @@ class ToolTestCase(unittest.TestCase):
if __name__ == "__main__":
- suite = unittest.makeSuite(ToolTestCase, 'test_')
+ loader = unittest.TestLoader()
+ loader.testMethodPrefix = 'test_'
+ suite = loader.loadTestsFromTestCase(ToolTestCase)
TestUnit.run(suite)
# Local Variables:
diff --git a/SCons/Tool/msvsTests.py b/SCons/Tool/msvsTests.py
index 4cbaf0e..c4d2e98 100644
--- a/SCons/Tool/msvsTests.py
+++ b/SCons/Tool/msvsTests.py
@@ -982,7 +982,9 @@ if __name__ == "__main__":
if k in os.environ:
del os.environ[k]
- suite = unittest.makeSuite(test_class, 'test_')
+ loader = unittest.TestLoader()
+ loader.testMethodPrefix = 'test_'
+ suite = loader.loadTestsFromTestCase(test_class)
if not TestUnit.cli.get_runner()().run(suite).wasSuccessful():
exit_val = 1
finally:
diff --git a/SCons/cppTests.py b/SCons/cppTests.py
index a9aef9d..f20c302 100644
--- a/SCons/cppTests.py
+++ b/SCons/cppTests.py
@@ -876,7 +876,9 @@ if __name__ == '__main__':
fileTestCase,
]
for tclass in tclasses:
- names = unittest.getTestCaseNames(tclass, 'test_')
+ loader = unittest.TestLoader()
+ loader.testMethodPrefix = 'test_'
+ names = loader.getTestCaseNames(tclass)
try:
names = sorted(set(names))
except NameError:
diff --git a/testing/framework/TestCmdTests.py b/testing/framework/TestCmdTests.py
index 212c59a..3b29091 100644
--- a/testing/framework/TestCmdTests.py
+++ b/testing/framework/TestCmdTests.py
@@ -3389,8 +3389,10 @@ if __name__ == "__main__":
])
suite = unittest.TestSuite()
for tclass in tclasses:
- names = unittest.getTestCaseNames(tclass, 'test_')
- suite.addTests([ tclass(n) for n in names ])
+ loader = unittest.TestLoader()
+ loader.testMethodPrefix = 'test_'
+ names = loader.getTestCaseNames(tclass)
+ suite.addTests([tclass(n) for n in names])
if not unittest.TextTestRunner().run(suite).wasSuccessful():
sys.exit(1)
diff --git a/testing/framework/TestCommonTests.py b/testing/framework/TestCommonTests.py
index 03a5508..dff7a50 100644
--- a/testing/framework/TestCommonTests.py
+++ b/testing/framework/TestCommonTests.py
@@ -2429,8 +2429,10 @@ if __name__ == "__main__":
]
suite = unittest.TestSuite()
for tclass in tclasses:
- names = unittest.getTestCaseNames(tclass, 'test_')
- suite.addTests([ tclass(n) for n in names ])
+ loader = unittest.TestLoader()
+ loader.testMethodPrefix = 'test_'
+ names = loader.getTestCaseNames(tclass)
+ suite.addTests([tclass(n) for n in names])
if not unittest.TextTestRunner().run(suite).wasSuccessful():
sys.exit(1)
--
cgit v0.12
From c7272c7367f1c3f819645d996c6d93449cf14f5b Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Fri, 13 May 2022 08:18:22 -0600
Subject: Normalize use of test.sleep()
Some tests used time.sleep while others used the harness's test.sleep,
the ones that are done for file timestamp delay management now all use
test.sleep - with a consistent comment, so it's easier to spot.
There are still tests that use time.sleep with smaller intervals that
were left alone.
Affected tests were reformatted, in the spirit of gradually improving
tests that you otherwise touch...
Signed-off-by: Mats Wichmann
---
test/CacheDir/timestamp-match.py | 25 ++--
test/CacheDir/timestamp-newer.py | 24 ++--
test/Copy-Action.py | 70 +++++-----
test/Decider/MD5-timestamp-Repository.py | 20 +--
test/Decider/MD5-timestamp.py | 21 +--
test/Decider/timestamp.py | 23 +---
test/Dir/source.py | 85 +++++++------
test/Libs/LIBPATH.py | 71 +++++------
test/PharLap.py | 19 ++-
test/Program.py | 87 ++++++-------
test/Repository/no-SConsignFile.py | 14 +-
test/Repository/variants.py | 212 +++++++++++++++----------------
test/Touch.py | 35 +++--
test/chained-build.py | 62 +++++----
test/sconsign/script/Signatures.py | 26 ++--
test/sconsign/script/dblite.py | 45 +++----
16 files changed, 378 insertions(+), 461 deletions(-)
diff --git a/test/CacheDir/timestamp-match.py b/test/CacheDir/timestamp-match.py
index 4b64137..fd9c659 100644
--- a/test/CacheDir/timestamp-match.py
+++ b/test/CacheDir/timestamp-match.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Verify that CAcheDir() works when using 'timestamp-match' decisions.
@@ -41,21 +40,15 @@ Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE'))
test.write('file.in', "file.in\n")
-test.run(arguments = '--cache-show --debug=explain .')
-
+test.run(arguments='--cache-show --debug=explain .')
test.must_match('file.out', "file.in\n")
+test.up_to_date(options='--cache-show --debug=explain', arguments='.')
-test.up_to_date(options = '--cache-show --debug=explain', arguments = '.')
-
-test.sleep()
-
+test.sleep() # delay for timestamps
test.touch('file.in')
-
-test.not_up_to_date(options = '--cache-show --debug=explain', arguments = '.')
-
-test.up_to_date(options = '--cache-show --debug=explain', arguments = '.')
-
-test.up_to_date(options = '--cache-show --debug=explain', arguments = '.')
+test.not_up_to_date(options='--cache-show --debug=explain', arguments='.')
+test.up_to_date(options='--cache-show --debug=explain', arguments='.')
+test.up_to_date(options='--cache-show --debug=explain', arguments='.')
test.pass_test()
diff --git a/test/CacheDir/timestamp-newer.py b/test/CacheDir/timestamp-newer.py
index 618f467..567078e 100644
--- a/test/CacheDir/timestamp-newer.py
+++ b/test/CacheDir/timestamp-newer.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Verify that CAcheDir() works when using 'timestamp-newer' decisions.
@@ -41,21 +40,16 @@ Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE'))
test.write('file.in', "file.in\n")
-test.run(arguments = '--cache-show --debug=explain .')
-
+test.run(arguments='--cache-show --debug=explain .')
test.must_match('file.out', "file.in\n")
+test.up_to_date(options='--cache-show --debug=explain', arguments='.')
-test.up_to_date(options = '--cache-show --debug=explain', arguments = '.')
-
-test.sleep()
-
+test.sleep() # delay for timestamps
test.touch('file.in')
-test.not_up_to_date(options = '--cache-show --debug=explain', arguments = '.')
-
-test.up_to_date(options = '--cache-show --debug=explain', arguments = '.')
-
-test.up_to_date(options = '--cache-show --debug=explain', arguments = '.')
+test.not_up_to_date(options='--cache-show --debug=explain', arguments='.')
+test.up_to_date(options='--cache-show --debug=explain', arguments='.')
+test.up_to_date(options='--cache-show --debug=explain', arguments='.')
test.pass_test()
diff --git a/test/Copy-Action.py b/test/Copy-Action.py
index 4bfa0da..2179744 100644
--- a/test/Copy-Action.py
+++ b/test/Copy-Action.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Verify that the Copy() Action works, and preserves file modification
@@ -37,29 +36,32 @@ import TestSCons
test = TestSCons.TestSCons()
-test.write('SConstruct', """
+test.write('SConstruct', """\
Execute(Copy('f1.out', 'f1.in'))
Execute(Copy(File('d2.out'), 'd2.in'))
Execute(Copy('d3.out', File('f3.in')))
+
def cat(env, source, target):
target = str(target[0])
with open(target, "w") as f:
for src in source:
with open(str(src), "r") as ifp:
f.write(ifp.read())
+
Cat = Action(cat)
env = Environment()
-env.Command('bar.out', 'bar.in', [Cat,
- Copy("f4.out", "f4.in"),
- Copy("d5.out", "d5.in"),
- Copy("d6.out", "f6.in")])
-env = Environment(OUTPUT = 'f7.out', INPUT = 'f7.in')
+env.Command(
+ 'bar.out',
+ 'bar.in',
+ [Cat, Copy("f4.out", "f4.in"), Copy("d5.out", "d5.in"), Copy("d6.out", "f6.in")],
+)
+env = Environment(OUTPUT='f7.out', INPUT='f7.in')
env.Command('f8.out', 'f8.in', [Copy('$OUTPUT', '$INPUT'), Cat])
env.Command('f9.out', 'f9.in', [Cat, Copy('${TARGET}-Copy', '$SOURCE')])
-env.CopyTo( 'd4', 'f10.in' )
-env.CopyAs( 'd4/f11.out', 'f11.in')
-env.CopyAs( 'd4/f12.out', 'd5/f12.in')
+env.CopyTo('d4', 'f10.in')
+env.CopyAs('d4/f11.out', 'f11.in')
+env.CopyAs('d4/f12.out', 'd5/f12.in')
env.Command('f 13.out', 'f 13.in', Copy('$TARGET', '$SOURCE'))
""")
@@ -87,19 +89,20 @@ test.write('f 13.in', "f 13.in\n")
os.chmod('f1.in', 0o646)
os.chmod('f4.in', 0o644)
-test.sleep()
+test.sleep() # delay for timestamps
d4_f10_in = os.path.join('d4', 'f10.in')
d4_f11_out = os.path.join('d4', 'f11.out')
d4_f12_out = os.path.join('d4', 'f12.out')
d5_f12_in = os.path.join('d5', 'f12.in')
-expect = test.wrap_stdout(read_str = """\
+expect = test.wrap_stdout(
+ read_str="""\
Copy("f1.out", "f1.in")
Copy("d2.out", "d2.in")
Copy("d3.out", "f3.in")
""",
- build_str = """\
+ build_str="""\
cat(["bar.out"], ["bar.in"])
Copy("f4.out", "f4.in")
Copy("d5.out", "d5.in")
@@ -112,9 +115,10 @@ Copy("f7.out", "f7.in")
cat(["f8.out"], ["f8.in"])
cat(["f9.out"], ["f9.in"])
Copy("f9.out-Copy", "f9.in")
-""" % locals())
+""" % locals(),
+)
-test.run(options = '-n', arguments = '.', stdout = expect)
+test.run(options='-n', arguments='.', stdout=expect)
test.must_not_exist('f1.out')
test.must_not_exist('d2.out')
@@ -162,23 +166,21 @@ def must_be_same(f1, f2):
for value in ['ST_MODE', 'ST_MTIME']:
v = getattr(stat, value)
if s1[v] != s2[v]:
- msg = '%s[%s] %s != %s[%s] %s\n' % \
- (repr(f1), value, s1[v],
- repr(f2), value, s2[v],)
+ msg = f"{f1!r}[{value}] {s1[v1]} != {f2!r}[{value}] {s2[v]}\n"
sys.stderr.write(msg)
- errors = errors + 1
-
-must_be_same('f1.out', 'f1.in')
-must_be_same(['d2.out', 'file'], ['d2.in', 'file'])
-must_be_same(['d3.out', 'f3.in'], 'f3.in')
-must_be_same('f4.out', 'f4.in')
-must_be_same(['d5.out', 'file'], ['d5.in', 'file'])
-must_be_same(['d6.out', 'f6.in'], 'f6.in')
-must_be_same('f7.out', 'f7.in')
-must_be_same(['d4', 'f10.in'], 'f10.in')
-must_be_same(['d4', 'f11.out'], 'f11.in')
-must_be_same(['d4', 'f12.out'], ['d5', 'f12.in'])
-must_be_same('f 13.out', 'f 13.in')
+ errors += 1
+
+must_be_same('f1.out', 'f1.in')
+must_be_same(['d2.out', 'file'], ['d2.in', 'file'])
+must_be_same(['d3.out', 'f3.in'], 'f3.in')
+must_be_same('f4.out', 'f4.in')
+must_be_same(['d5.out', 'file'], ['d5.in', 'file'])
+must_be_same(['d6.out', 'f6.in'], 'f6.in')
+must_be_same('f7.out', 'f7.in')
+must_be_same(['d4', 'f10.in'], 'f10.in')
+must_be_same(['d4', 'f11.out'], 'f11.in')
+must_be_same(['d4', 'f12.out'], ['d5', 'f12.in'])
+must_be_same('f 13.out', 'f 13.in')
if errors:
test.fail_test()
diff --git a/test/Decider/MD5-timestamp-Repository.py b/test/Decider/MD5-timestamp-Repository.py
index 1826f68..201bdfe 100644
--- a/test/Decider/MD5-timestamp-Repository.py
+++ b/test/Decider/MD5-timestamp-Repository.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Verify behavior of the MD5-timestamp Decider() setting when combined with Repository() usage
@@ -38,13 +37,11 @@ test = TestSCons.TestSCons()
test.subdir('Repository', 'work')
repository = test.workpath('Repository')
-
test.write(['Repository','content1.in'], "content1.in 1\n")
test.write(['Repository','content2.in'], "content2.in 1\n")
test.write(['Repository','content3.in'], "content3.in 1\n")
# test.writable('Repository', 0)
-
test.write(['work','SConstruct'], """\
Repository(r'%s')
DefaultEnvironment(tools=[])
@@ -53,18 +50,14 @@ m.Decider('MD5-timestamp')
m.Command('content1.out', 'content1.in', Copy('$TARGET', '$SOURCE'))
m.Command('content2.out', 'content2.in', Copy('$TARGET', '$SOURCE'))
m.Command('content3.out', 'content3.in', Copy('$TARGET', '$SOURCE'))
-"""%repository)
+""" % repository)
test.run(chdir='work',arguments='.')
-
test.up_to_date(chdir='work',arguments='.')
-test.sleep()
-
+test.sleep() # delay for timestamps
test.write(['Repository','content1.in'], "content1.in 2\n")
-
test.touch(['Repository','content2.in'])
-
time_content = os.stat(os.path.join(repository,'content3.in'))[stat.ST_MTIME]
test.write(['Repository','content3.in'], "content3.in 2\n")
test.touch(['Repository','content3.in'], time_content)
@@ -76,10 +69,9 @@ test.touch(['Repository','content3.in'], time_content)
expect = test.wrap_stdout("""\
Copy("content1.out", "%s")
-"""%os.path.join(repository,'content1.in'))
+""" % os.path.join(repository, 'content1.in'))
test.run(chdir='work', arguments='.', stdout=expect)
-
test.up_to_date(chdir='work', arguments='.')
test.pass_test()
diff --git a/test/Decider/MD5-timestamp.py b/test/Decider/MD5-timestamp.py
index 6fcdb42..3815639 100644
--- a/test/Decider/MD5-timestamp.py
+++ b/test/Decider/MD5-timestamp.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Verify behavior of the MD5-timestamp Decider() setting.
@@ -49,15 +48,10 @@ test.write('content2.in', "content2.in 1\n")
test.write('content3.in', "content3.in 1\n")
test.run(arguments = '.')
-
test.up_to_date(arguments = '.')
-
-
-test.sleep()
-
+test.sleep() # delay for timestamps
test.write('content1.in', "content1.in 2\n")
-
test.touch('content2.in')
time_content = os.stat('content3.in')[stat.ST_MTIME]
@@ -73,11 +67,8 @@ expect = test.wrap_stdout("""\
Copy("content1.out", "content1.in")
""")
-test.run(arguments = '.', stdout=expect)
-
-test.up_to_date(arguments = '.')
-
-
+test.run(arguments='.', stdout=expect)
+test.up_to_date(arguments='.')
test.pass_test()
diff --git a/test/Decider/timestamp.py b/test/Decider/timestamp.py
index e528d77..d713a62 100644
--- a/test/Decider/timestamp.py
+++ b/test/Decider/timestamp.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Verify various interactions of the timestamp-match and timestamp-newer
@@ -54,27 +53,21 @@ test.write('newer1.in', "newer1.in\n")
test.write('newer2.in', "newer2.in\n")
test.run(arguments = '.')
-
test.up_to_date(arguments = '.')
time_match = os.stat('match2.out')[stat.ST_MTIME]
time_newer = os.stat('newer2.out')[stat.ST_MTIME]
-
-
# Now make all the source files newer than (different timestamps from)
# the last time the targets were built, and touch the target files
# of match1.out and newer1.out to see the different effects.
-
-test.sleep()
-
+test.sleep() # delay for timestamps
test.touch('match1.in')
test.touch('newer1.in')
test.touch('match2.in')
test.touch('newer2.in')
-test.sleep()
-
+test.sleep() # delay for timestamps
test.touch('match1.out')
test.touch('newer1.out')
@@ -90,7 +83,7 @@ Copy("match2.out", "match2.in")
Copy("newer2.out", "newer2.in")
""")
-test.run(arguments = '.', stdout=expect)
+test.run(arguments='.', stdout=expect)
# Now, for the somewhat pathological case, reset the match2.out and
# newer2.out timestamps to the older timestamp when the targets were
@@ -107,9 +100,7 @@ expect = test.wrap_stdout("""\
Copy("newer2.out", "newer2.in")
""")
-test.run(arguments = '.', stdout=expect)
-
-
+test.run(arguments='.', stdout=expect)
test.pass_test()
diff --git a/test/Dir/source.py b/test/Dir/source.py
index c35d169..a0ba987 100644
--- a/test/Dir/source.py
+++ b/test/Dir/source.py
@@ -42,15 +42,16 @@ test.subdir('tstamp', [ 'tstamp', 'subdir' ],
test.write('SConstruct', """\
DefaultEnvironment(tools=[])
+
def writeTarget(target, source, env):
- f=open(str(target[0]), 'w')
+ f = open(str(target[0]), 'w')
f.write("stuff\\n")
f.close()
return 0
-test_bld_dir = Builder(action=writeTarget,
- source_factory=Dir,
- source_scanner=DirScanner)
+test_bld_dir = Builder(
+ action=writeTarget, source_factory=Dir, source_scanner=DirScanner
+)
test_bld_file = Builder(action=writeTarget)
env = Environment(tools=[])
env['BUILDERS']['TestDir'] = test_bld_dir
@@ -61,34 +62,36 @@ env_tstamp.Decider('timestamp-newer')
env_tstamp.TestFile(source='junk.txt', target='tstamp/junk.out')
env_tstamp.TestDir(source='tstamp', target='tstamp.out')
env_tstamp.Command('cmd-tstamp-noscan.out', 'cmd-tstamp', writeTarget)
-env_tstamp.Command('cmd-tstamp.out', 'cmd-tstamp', writeTarget,
- source_scanner=DirScanner)
+env_tstamp.Command(
+ 'cmd-tstamp.out', 'cmd-tstamp', writeTarget, source_scanner=DirScanner
+)
env_content = env.Clone()
env_content.Decider('content')
env_content.TestFile(source='junk.txt', target='content/junk.out')
env_content.TestDir(source='content', target='content.out')
env_content.Command('cmd-content-noscan.out', 'cmd-content', writeTarget)
-env_content.Command('cmd-content.out', 'cmd-content', writeTarget,
- source_scanner=DirScanner)
+env_content.Command(
+ 'cmd-content.out', 'cmd-content', writeTarget, source_scanner=DirScanner
+)
""")
-test.write([ 'tstamp', 'foo.txt' ], 'foo.txt 1\n')
-test.write([ 'tstamp', '#hash.txt' ], 'hash.txt 1\n')
-test.write([ 'tstamp', 'subdir', 'bar.txt'], 'bar.txt 1\n')
-test.write([ 'tstamp', 'subdir', '#hash.txt'], 'hash.txt 1\n')
-test.write([ 'content', 'foo.txt' ], 'foo.txt 1\n')
-test.write([ 'content', '#hash.txt' ], 'hash.txt 1\n')
-test.write([ 'content', 'subdir', 'bar.txt' ], 'bar.txt 1\n')
-test.write([ 'content', 'subdir', '#hash.txt' ], 'hash.txt 1\n')
-test.write([ 'cmd-tstamp', 'foo.txt' ], 'foo.txt 1\n')
-test.write([ 'cmd-tstamp', '#hash.txt' ], 'hash.txt 1\n')
-test.write([ 'cmd-tstamp', 'subdir', 'bar.txt' ], 'bar.txt 1\n')
-test.write([ 'cmd-tstamp', 'subdir', '#hash.txt' ], 'hash.txt 1\n')
-test.write([ 'cmd-content', 'foo.txt' ], 'foo.txt 1\n')
-test.write([ 'cmd-content', '#hash.txt' ], '#hash.txt 1\n')
-test.write([ 'cmd-content', 'subdir', 'bar.txt' ], 'bar.txt 1\n')
-test.write([ 'cmd-content', 'subdir', '#hash.txt' ], 'hash.txt 1\n')
+test.write(['tstamp', 'foo.txt'], 'foo.txt 1\n')
+test.write(['tstamp', '#hash.txt'], 'hash.txt 1\n')
+test.write(['tstamp', 'subdir', 'bar.txt'], 'bar.txt 1\n')
+test.write(['tstamp', 'subdir', '#hash.txt'], 'hash.txt 1\n')
+test.write(['content', 'foo.txt'], 'foo.txt 1\n')
+test.write(['content', '#hash.txt'], 'hash.txt 1\n')
+test.write(['content', 'subdir', 'bar.txt'], 'bar.txt 1\n')
+test.write(['content', 'subdir', '#hash.txt'], 'hash.txt 1\n')
+test.write(['cmd-tstamp', 'foo.txt'], 'foo.txt 1\n')
+test.write(['cmd-tstamp', '#hash.txt'], 'hash.txt 1\n')
+test.write(['cmd-tstamp', 'subdir', 'bar.txt'], 'bar.txt 1\n')
+test.write(['cmd-tstamp', 'subdir', '#hash.txt'], 'hash.txt 1\n')
+test.write(['cmd-content', 'foo.txt'], 'foo.txt 1\n')
+test.write(['cmd-content', '#hash.txt'], '#hash.txt 1\n')
+test.write(['cmd-content', 'subdir', 'bar.txt'], 'bar.txt 1\n')
+test.write(['cmd-content', 'subdir', '#hash.txt'], 'hash.txt 1\n')
test.write('junk.txt', 'junk.txt\n')
test.run(arguments=".", stderr=None)
@@ -106,61 +109,61 @@ test.up_to_date(arguments='cmd-content.out')
test.up_to_date(arguments='cmd-tstamp-noscan.out')
test.up_to_date(arguments='cmd-content-noscan.out')
-test.sleep()
+test.sleep() # delay for timestamps
-test.write([ 'tstamp', 'foo.txt' ], 'foo.txt 2\n')
+test.write(['tstamp', 'foo.txt'], 'foo.txt 2\n')
test.not_up_to_date(arguments='tstamp.out')
-test.write([ 'tstamp', 'new.txt' ], 'new.txt\n')
+test.write(['tstamp', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='tstamp.out')
-test.write([ 'content', 'foo.txt' ], 'foo.txt 2\n')
+test.write(['content', 'foo.txt'], 'foo.txt 2\n')
test.not_up_to_date(arguments='content.out')
-test.write([ 'content', 'new.txt' ], 'new.txt\n')
+test.write(['content', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='content.out')
-test.write([ 'cmd-tstamp', 'foo.txt' ], 'foo.txt 2\n')
+test.write(['cmd-tstamp', 'foo.txt'], 'foo.txt 2\n')
test.not_up_to_date(arguments='cmd-tstamp.out')
test.up_to_date(arguments='cmd-tstamp-noscan.out')
-test.write([ 'cmd-tstamp', 'new.txt' ], 'new.txt\n')
+test.write(['cmd-tstamp', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='cmd-tstamp.out')
test.up_to_date(arguments='cmd-tstamp-noscan.out')
-test.write([ 'cmd-content', 'foo.txt' ], 'foo.txt 2\n')
+test.write(['cmd-content', 'foo.txt'], 'foo.txt 2\n')
test.not_up_to_date(arguments='cmd-content.out')
test.up_to_date(arguments='cmd-content-noscan.out')
-test.write([ 'cmd-content', 'new.txt' ], 'new.txt\n')
+test.write(['cmd-content', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='cmd-content.out')
test.up_to_date(arguments='cmd-content-noscan.out')
-test.write([ 'tstamp', 'subdir', 'bar.txt' ], 'bar.txt 2\n')
+test.write(['tstamp', 'subdir', 'bar.txt'], 'bar.txt 2\n')
test.not_up_to_date(arguments='tstamp.out')
-test.write([ 'tstamp', 'subdir', 'new.txt' ], 'new.txt\n')
+test.write(['tstamp', 'subdir', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='tstamp.out')
-test.write([ 'content', 'subdir', 'bar.txt' ], 'bar.txt 2\n')
+test.write(['content', 'subdir', 'bar.txt'], 'bar.txt 2\n')
test.not_up_to_date(arguments='content.out')
-test.write([ 'content', 'subdir', 'new.txt' ], 'new.txt\n')
+test.write(['content', 'subdir', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='content.out')
-test.write([ 'cmd-tstamp', 'subdir', 'bar.txt' ], 'bar.txt 2\n')
+test.write(['cmd-tstamp', 'subdir', 'bar.txt'], 'bar.txt 2\n')
test.not_up_to_date(arguments='cmd-tstamp.out')
test.up_to_date(arguments='cmd-tstamp-noscan.out')
-test.write([ 'cmd-tstamp', 'subdir', 'new.txt' ], 'new.txt\n')
+test.write(['cmd-tstamp', 'subdir', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='cmd-tstamp.out')
test.up_to_date(arguments='cmd-tstamp-noscan.out')
-test.write([ 'cmd-content', 'subdir', 'bar.txt' ], 'bar.txt 2\n')
+test.write(['cmd-content', 'subdir', 'bar.txt'], 'bar.txt 2\n')
test.not_up_to_date(arguments='cmd-content.out')
test.up_to_date(arguments='cmd-content-noscan.out')
-test.write([ 'cmd-content', 'subdir', 'new.txt' ], 'new.txt\n')
+test.write(['cmd-content', 'subdir', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='cmd-content.out')
test.up_to_date(arguments='cmd-content-noscan.out')
diff --git a/test/Libs/LIBPATH.py b/test/Libs/LIBPATH.py
index b5a1b54..d663e56 100644
--- a/test/Libs/LIBPATH.py
+++ b/test/Libs/LIBPATH.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,7 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os.path
import time
@@ -40,20 +40,17 @@ test.subdir('lib1', 'lib2')
prog1 = test.workpath('prog') + _exe
prog2 = test.workpath(dll_ + 'shlib') + _dll
-test.write('SConstruct', """
-env1 = Environment(LIBS = [ 'foo1' ],
- LIBPATH = [ '$FOO' ],
- FOO='./lib1')
+test.write('SConstruct', """\
+env1 = Environment(LIBS=['foo1'], LIBPATH=['$FOO'], FOO='./lib1')
f1 = env1.SharedObject('f1', 'f1.c')
-env1.Program(target = 'prog', source = 'prog.c')
-env1.Library(target = './lib1/foo1', source = f1)
+env1.Program(target='prog', source='prog.c')
+env1.Library(target='./lib1/foo1', source=f1)
-env2 = Environment(LIBS = 'foo2',
- LIBPATH = '.')
-env2.SharedLibrary(target = 'shlib', source = 'shlib.c', no_import_lib = 1)
-env2.Library(target = 'foo2', source = f1)
+env2 = Environment(LIBS='foo2', LIBPATH='.')
+env2.SharedLibrary(target='shlib', source='shlib.c', no_import_lib=1)
+env2.Library(target='foo2', source=f1)
""")
test.write('f1.c', r"""
@@ -99,8 +96,8 @@ test.run(program = prog1,
oldtime1 = os.path.getmtime(prog1)
oldtime2 = os.path.getmtime(prog2)
-time.sleep(2)
-test.run(arguments = '.')
+test.sleep() # delay for timestamps
+test.run(arguments='.')
test.fail_test(oldtime1 != os.path.getmtime(prog1))
test.fail_test(oldtime2 != os.path.getmtime(prog2))
@@ -115,30 +112,25 @@ f1(void)
}
""")
-test.run(arguments = '.',
- stderr=TestSCons.noisy_ar,
- match=TestSCons.match_re_dotall)
-test.run(program = prog1,
- stdout = "f1.c 1\nprog.c\n")
+test.run(arguments='.', stderr=TestSCons.noisy_ar, match=TestSCons.match_re_dotall)
+test.run(program=prog1, stdout="f1.c 1\nprog.c\n")
test.fail_test(oldtime2 == os.path.getmtime(prog2))
#test.up_to_date(arguments = '.')
# Change LIBPATH and make sure we don't rebuild because of it.
-test.write('SConstruct', """
-env1 = Environment(LIBS = [ 'foo1' ],
- LIBPATH = [ './lib1', './lib2' ])
+test.write('SConstruct', """\
+env1 = Environment(LIBS=['foo1'], LIBPATH=['./lib1', './lib2'])
f1 = env1.SharedObject('f1', 'f1.c')
-env1.Program(target = 'prog', source = 'prog.c')
-env1.Library(target = './lib1/foo1', source = f1)
+env1.Program(target='prog', source='prog.c')
+env1.Library(target='./lib1/foo1', source=f1)
-env2 = Environment(LIBS = 'foo2',
- LIBPATH = Split('. ./lib2'))
-env2.SharedLibrary(target = 'shlib', source = 'shlib.c', no_import_lib = 1)
-env2.Library(target = 'foo2', source = f1)
+env2 = Environment(LIBS='foo2', LIBPATH=Split('. ./lib2'))
+env2.SharedLibrary(target='shlib', source='shlib.c', no_import_lib=1)
+env2.Library(target='foo2', source=f1)
""")
-test.up_to_date(arguments = '.', stderr=None)
+test.up_to_date(arguments='.', stderr=None)
test.write('f1.c', r"""
#include
@@ -150,27 +142,22 @@ f1(void)
}
""")
-test.run(arguments = '.',
- stderr=TestSCons.noisy_ar,
- match=TestSCons.match_re_dotall)
-test.run(program = prog1,
- stdout = "f1.c 2\nprog.c\n")
+test.run(arguments='.', stderr=TestSCons.noisy_ar, match=TestSCons.match_re_dotall)
+test.run(program=prog1, stdout="f1.c 2\nprog.c\n")
-test.up_to_date(arguments = '.')
+test.up_to_date(arguments='.')
# We need at least one file for some implementations of the Library
# builder, notably the SGI one.
test.write('empty.c', 'int a=0;\n')
# Check that a null-string LIBPATH doesn't blow up.
-test.write('SConstruct', """
-env = Environment(LIBPATH = '')
-env.Library('foo', source = 'empty.c')
+test.write('SConstruct', """\
+env = Environment(LIBPATH='')
+env.Library('foo', source='empty.c')
""")
-test.run(arguments = '.',
- stderr=TestSCons.noisy_ar,
- match=TestSCons.match_re_dotall)
+test.run(arguments='.', stderr=TestSCons.noisy_ar, match=TestSCons.match_re_dotall)
test.pass_test()
diff --git a/test/PharLap.py b/test/PharLap.py
index 8e56f21..0a54151 100644
--- a/test/PharLap.py
+++ b/test/PharLap.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os
import sys
@@ -284,10 +283,10 @@ test.write([ "baz", "bar.lnk"],"""
@asm.emb
""")
-test.write("SConstruct", """
-env=Environment(tools = [ 'linkloc', '386asm' ],
- ASFLAGS='-twocase -cvsym',
- LINKFLAGS='@foo.lnk')
+test.write("SConstruct", """\
+env = Environment(
+ tools=['linkloc', '386asm'], ASFLAGS='-twocase -cvsym', LINKFLAGS='@foo.lnk'
+)
env.Program(target='minasm', source='minasm.asm')
""")
@@ -304,8 +303,8 @@ test.write([ "baz", "bar.lnk"],"""
""")
oldtime = os.path.getmtime(test.workpath('minasm.exe'))
-time.sleep(2) # Give the time stamp time to change
-test.run(arguments = '.')
+test.sleep() # delay for timestamps
+test.run(arguments='.')
test.fail_test(oldtime == os.path.getmtime(test.workpath('minasm.exe')))
test.pass_test()
diff --git a/test/Program.py b/test/Program.py
index 15fd0c3..640787f 100644
--- a/test/Program.py
+++ b/test/Program.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os.path
import time
@@ -40,13 +39,13 @@ foo4 = test.workpath('foo4' + _exe)
foo5 = test.workpath('foo5' + _exe)
foo_args = 'foo1%s foo2%s foo3%s foo4%s foo5%s' % (_exe, _exe, _exe, _exe, _exe)
-test.write('SConstruct', """
+test.write('SConstruct', """\
env = Environment()
-env.Program(target = 'foo1', source = 'f1.c')
-env.Program(target = 'foo2', source = Split('f2a.c f2b.c f2c.c'))
+env.Program(target='foo1', source='f1.c')
+env.Program(target='foo2', source=Split('f2a.c f2b.c f2c.c'))
f3a = File('f3a.c')
f3b = File('f3b.c')
-Program(target = 'foo3', source = [f3a, [f3b, 'f3c.c']])
+Program(target='foo3', source=[f3a, [f3b, 'f3c.c']])
env.Program('foo4', 'f4.c')
env.Program('foo5.c')
""")
@@ -156,15 +155,14 @@ main(int argc, char *argv[])
}
""")
-test.run(arguments = '.')
+test.run(arguments='.')
-test.run(program = foo1, stdout = "f1.c\n")
-test.run(program = foo2, stdout = "f2a.c\nf2b.c\nf2c.c\n")
-test.run(program = foo3, stdout = "f3a.c\nf3b.c\nf3c.c\n")
-test.run(program = foo4, stdout = "f4.c\n")
-test.run(program = foo5, stdout = "foo5.c\n")
-
-test.up_to_date(arguments = '.')
+test.run(program=foo1, stdout="f1.c\n")
+test.run(program=foo2, stdout="f2a.c\nf2b.c\nf2c.c\n")
+test.run(program=foo3, stdout="f3a.c\nf3b.c\nf3c.c\n")
+test.run(program=foo4, stdout="f4.c\n")
+test.run(program=foo5, stdout="foo5.c\n")
+test.up_to_date(arguments='.')
test.write('f1.c', r"""
#include
@@ -211,15 +209,14 @@ main(int argc, char *argv[])
}
""")
-test.run(arguments = '.')
-
-test.run(program = foo1, stdout = "f1.c X\n")
-test.run(program = foo2, stdout = "f2a.c\nf2b.c\nf2c.c\n")
-test.run(program = foo3, stdout = "f3a.c\nf3b.c X\nf3c.c\n")
-test.run(program = foo4, stdout = "f4.c X\n")
-test.run(program = foo5, stdout = "foo5.c X\n")
+test.run(arguments='.')
-test.up_to_date(arguments = '.')
+test.run(program=foo1, stdout="f1.c X\n")
+test.run(program=foo2, stdout="f2a.c\nf2b.c\nf2c.c\n")
+test.run(program=foo3, stdout="f3a.c\nf3b.c X\nf3c.c\n")
+test.run(program=foo4, stdout="f4.c X\n")
+test.run(program=foo5, stdout="foo5.c X\n")
+test.up_to_date(arguments='.')
# make sure the programs didn't get rebuilt, because nothing changed:
oldtime1 = os.path.getmtime(foo1)
@@ -228,10 +225,8 @@ oldtime3 = os.path.getmtime(foo3)
oldtime4 = os.path.getmtime(foo4)
oldtime5 = os.path.getmtime(foo5)
-time.sleep(2) # introduce a small delay, to make the test valid
-
-test.run(arguments = foo_args)
-
+test.sleep() # delay for timestamps
+test.run(arguments=foo_args)
test.fail_test(oldtime1 != os.path.getmtime(foo1))
test.fail_test(oldtime2 != os.path.getmtime(foo2))
test.fail_test(oldtime3 != os.path.getmtime(foo3))
@@ -284,15 +279,14 @@ main(int argc, char *argv[])
}
""")
-test.run(arguments = foo_args)
-
-test.run(program = foo1, stdout = "f1.c Y\n")
-test.run(program = foo2, stdout = "f2a.c\nf2b.c\nf2c.c\n")
-test.run(program = foo3, stdout = "f3a.c\nf3b.c Y\nf3c.c\n")
-test.run(program = foo4, stdout = "f4.c Y\n")
-test.run(program = foo5, stdout = "foo5.c Y\n")
+test.run(arguments=foo_args)
-test.up_to_date(arguments = foo_args)
+test.run(program=foo1, stdout="f1.c Y\n")
+test.run(program=foo2, stdout="f2a.c\nf2b.c\nf2c.c\n")
+test.run(program=foo3, stdout="f3a.c\nf3b.c Y\nf3c.c\n")
+test.run(program=foo4, stdout="f4.c Y\n")
+test.run(program=foo5, stdout="foo5.c Y\n")
+test.up_to_date(arguments=foo_args)
test.write('f1.c', r"""
#include
@@ -340,15 +334,14 @@ main(int argc, char *argv[])
}
""")
-test.run(arguments = foo_args)
-
-test.run(program = foo1, stdout = "f1.c Z\n")
-test.run(program = foo2, stdout = "f2a.c\nf2b.c\nf2c.c\n")
-test.run(program = foo3, stdout = "f3a.c\nf3b.c Z\nf3c.c\n")
-test.run(program = foo4, stdout = "f4.c Z\n")
-test.run(program = foo5, stdout = "foo5.c Z\n")
+test.run(arguments=foo_args)
-test.up_to_date(arguments = foo_args)
+test.run(program=foo1, stdout="f1.c Z\n")
+test.run(program=foo2, stdout="f2a.c\nf2b.c\nf2c.c\n")
+test.run(program=foo3, stdout="f3a.c\nf3b.c Z\nf3c.c\n")
+test.run(program=foo4, stdout="f4.c Z\n")
+test.run(program=foo5, stdout="foo5.c Z\n")
+test.up_to_date(arguments=foo_args)
# make sure the programs didn't get rebuilt, because nothing changed:
oldtime1 = os.path.getmtime(foo1)
@@ -357,10 +350,8 @@ oldtime3 = os.path.getmtime(foo3)
oldtime4 = os.path.getmtime(foo4)
oldtime5 = os.path.getmtime(foo5)
-time.sleep(2) # introduce a small delay, to make the test valid
-
-test.run(arguments = foo_args)
-
+test.sleep() # delay for timestamps
+test.run(arguments=foo_args)
test.fail_test(not (oldtime1 == os.path.getmtime(foo1)))
test.fail_test(not (oldtime2 == os.path.getmtime(foo2)))
test.fail_test(not (oldtime3 == os.path.getmtime(foo3)))
diff --git a/test/Repository/no-SConsignFile.py b/test/Repository/no-SConsignFile.py
index d7f570c..6b5b3e2 100644
--- a/test/Repository/no-SConsignFile.py
+++ b/test/Repository/no-SConsignFile.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Test that using Repository() works even when the Repository has no
@@ -63,13 +62,10 @@ test.write(['src', 'foo.h'], """\
# Make sure it's past the max_drift time,
# so the source file signatures get saved.
-test.sleep(2)
+test.sleep() # delay for timestamps
test.run(chdir='build', arguments='.')
-
-test.run(program=test.workpath('build', 'foo'),
- stdout="src/foo.h\nsrc/foo.c\n")
-
+test.run(program=test.workpath('build', 'foo'), stdout="src/foo.h\nsrc/foo.c\n")
test.up_to_date(chdir='build', arguments='.')
test.pass_test()
diff --git a/test/Repository/variants.py b/test/Repository/variants.py
index c95e853..a07d7a0 100644
--- a/test/Repository/variants.py
+++ b/test/Repository/variants.py
@@ -31,20 +31,22 @@ from TestSCons import TestSCons, _exe, _obj
test = TestSCons()
-test.subdir('repository',
- ['repository', 'src1'],
- ['repository', 'src2'],
- ['repository', 'src2', 'include'],
- ['repository', 'src2', 'xxx'],
- ['repository', 'build2'],
- ['repository', 'build2', 'foo'],
- ['repository', 'build2', 'bar'],
- 'work1',
- ['work1', 'src1'],
- 'work2',
- ['work2', 'src2'],
- ['work2', 'src2', 'include'],
- ['work2', 'src2', 'xxx'])
+test.subdir(
+ 'repository',
+ ['repository', 'src1'],
+ ['repository', 'src2'],
+ ['repository', 'src2', 'include'],
+ ['repository', 'src2', 'xxx'],
+ ['repository', 'build2'],
+ ['repository', 'build2', 'foo'],
+ ['repository', 'build2', 'bar'],
+ 'work1',
+ ['work1', 'src1'],
+ 'work2',
+ ['work2', 'src2'],
+ ['work2', 'src2', 'include'],
+ ['work2', 'src2', 'xxx'],
+)
aaa_obj = 'aaa' + _obj
bbb_obj = 'bbb' + _obj
@@ -56,14 +58,18 @@ repository_build1_foo_xxx = test.workpath('repository', 'build1', 'foo', 'xxx')
work1_build1_foo_xxx = test.workpath('work1', 'build1', 'foo', 'xxx')
work1_build1_bar_xxx = test.workpath('work1', 'build1', 'bar', 'xxx')
-repository_build2_foo_src2_xxx_xxx = test.workpath('repository', 'build2',
- 'foo', 'src2', 'xxx', 'xxx')
-repository_build2_bar_src2_xxx_xxx = test.workpath('repository', 'build2',
- 'bar', 'src2', 'xxx', 'xxx')
-work2_build2_foo_src2_xxx_xxx = test.workpath('work2', 'build2',
- 'foo', 'src2', 'xxx', 'xxx')
-work2_build2_bar_src2_xxx_xxx = test.workpath('work2', 'build2',
- 'bar', 'src2', 'xxx', 'xxx')
+repository_build2_foo_src2_xxx_xxx = test.workpath(
+ 'repository', 'build2', 'foo', 'src2', 'xxx', 'xxx'
+)
+repository_build2_bar_src2_xxx_xxx = test.workpath(
+ 'repository', 'build2', 'bar', 'src2', 'xxx', 'xxx'
+)
+work2_build2_foo_src2_xxx_xxx = test.workpath(
+ 'work2', 'build2', 'foo', 'src2', 'xxx', 'xxx'
+)
+work2_build2_bar_src2_xxx_xxx = test.workpath(
+ 'work2', 'build2', 'bar', 'src2', 'xxx', 'xxx'
+)
opts = "-Y " + test.workpath('repository')
@@ -73,12 +79,13 @@ OS = ARGUMENTS.get('OS', '')
build1_os = "#build1/" + OS
default = Environment()
ccflags = {
- '' : '',
- 'foo' : '-DFOO',
- 'bar' : '-DBAR',
+ '': '',
+ 'foo': '-DFOO',
+ 'bar': '-DBAR',
}
-env1 = Environment(CCFLAGS = default.subst('$CCFLAGS %s' % ccflags[OS]),
- CPPPATH = build1_os)
+env1 = Environment(
+ CCFLAGS=default.subst('$CCFLAGS %s' % ccflags[OS]), CPPPATH=build1_os
+)
VariantDir(build1_os, 'src1')
SConscript(build1_os + '/SConscript', "env1")
@@ -95,8 +102,9 @@ test.write(['repository', 'build2', 'foo', 'SConscript'], r"""
VariantDir('src2', '#src2')
default = Environment()
-env2 = Environment(CCFLAGS = default.subst('$CCFLAGS -DFOO'),
- CPPPATH = ['#src2/xxx', '#src2/include'])
+env2 = Environment(
+ CCFLAGS=default.subst('$CCFLAGS -DFOO'), CPPPATH=['#src2/xxx', '#src2/include']
+)
SConscript('src2/xxx/SConscript', "env2")
""")
@@ -105,8 +113,9 @@ test.write(['repository', 'build2', 'bar', 'SConscript'], r"""
VariantDir('src2', '#src2')
default = Environment()
-env2 = Environment(CCFLAGS = default.subst('$CCFLAGS -DBAR'),
- CPPPATH = ['#src2/xxx', '#src2/include'])
+env2 = Environment(
+ CCFLAGS=default.subst('$CCFLAGS -DBAR'), CPPPATH=['#src2/xxx', '#src2/include']
+)
SConscript('src2/xxx/SConscript', "env2")
""")
@@ -216,12 +225,9 @@ repository/src1/bbb.c: REPOSITORY_FOO
repository/src1/main.c: REPOSITORY_FOO
""")
-database_name=test.get_sconsignname()
-
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+database_name = test.get_sconsignname()
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
@@ -237,10 +243,8 @@ repository/src2/xxx/include.h: BAR
repository/src2/xxx/main.c: BAR
""")
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
@@ -251,15 +255,10 @@ test.writable('repository', 0)
#
test.up_to_date(chdir='work1', options=opts + " OS=foo", arguments='build1')
-test.fail_test(os.path.exists(
- test.workpath('work1', 'build1', 'foo', aaa_obj)))
-test.fail_test(os.path.exists(
- test.workpath('work1', 'build1', 'foo', bbb_obj)))
-test.fail_test(os.path.exists(test.workpath(
- 'work1', 'build1', 'foo', main_obj)))
-
-test.fail_test(os.path.exists(
- test.workpath('work1', 'build1', 'foo', xxx_exe)))
+test.fail_test(os.path.exists(test.workpath('work1', 'build1', 'foo', aaa_obj)))
+test.fail_test(os.path.exists(test.workpath('work1', 'build1', 'foo', bbb_obj)))
+test.fail_test(os.path.exists(test.workpath('work1', 'build1', 'foo', main_obj)))
+test.fail_test(os.path.exists(test.workpath('work1', 'build1', 'foo', xxx_exe)))
#
test.run(chdir='work1', options=opts, arguments='OS=bar .')
@@ -271,18 +270,13 @@ repository/src1/bbb.c: REPOSITORY_BAR
repository/src1/main.c: REPOSITORY_BAR
""")
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
-
test.up_to_date(chdir='work1', options=opts + " OS=bar", arguments='build1')
-# Ensure file time stamps will be newer.
-time.sleep(2)
-
+test.sleep() # delay for timestamps
test.write(['work1', 'src1', 'iii.h'], r"""
#ifdef FOO
#define STRING "WORK_FOO"
@@ -302,13 +296,10 @@ repository/src1/bbb.c: WORK_BAR
repository/src1/main.c: WORK_BAR
""")
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
-
test.up_to_date(chdir='work1', options=opts + " OS=bar", arguments='build1')
#
@@ -320,38 +311,39 @@ repository/src1/bbb.c: WORK_FOO
repository/src1/main.c: WORK_FOO
""")
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
-
test.up_to_date(chdir='work1', options=opts + " OS=foo", arguments='build1')
-
-#
test.up_to_date(chdir='work2', options=opts, arguments='build2')
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'foo', 'src2', 'xxx', aaa_obj)))
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'foo', 'src2', 'xxx', bbb_obj)))
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'foo', 'src2', 'xxx', main_obj)))
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'foo', 'src2', 'xxx', xxx_exe)))
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'bar', 'src2', 'xxx', aaa_obj)))
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'bar', 'src2', 'xxx', bbb_obj)))
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'bar', 'src2', 'xxx', main_obj)))
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'bar', 'src2', 'xxx', xxx_exe)))
-
-# Ensure file time stamps will be newer.
-time.sleep(2)
-
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'foo', 'src2', 'xxx', aaa_obj))
+)
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'foo', 'src2', 'xxx', bbb_obj))
+)
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'foo', 'src2', 'xxx', main_obj))
+)
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'foo', 'src2', 'xxx', xxx_exe))
+)
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'bar', 'src2', 'xxx', aaa_obj))
+)
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'bar', 'src2', 'xxx', bbb_obj))
+)
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'bar', 'src2', 'xxx', main_obj))
+)
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'bar', 'src2', 'xxx', xxx_exe))
+)
+
+test.sleep() # delay for timestamps
test.write(['work2', 'src2', 'include', 'my_string.h'], r"""
#ifdef FOO
#define INCLUDE_OS "FOO"
@@ -362,7 +354,6 @@ test.write(['work2', 'src2', 'include', 'my_string.h'], r"""
#define INCLUDE_STRING "work2/src2/include/my_string.h: %s\n"
""")
-#
test.run(chdir='work2', options=opts, arguments='build2')
test.run(program=work2_build2_foo_src2_xxx_xxx, stdout="""\
@@ -377,16 +368,12 @@ repository/src2/xxx/include.h: BAR
repository/src2/xxx/main.c: BAR
""")
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
-# Ensure file time stamps will be newer.
-time.sleep(2)
-
+test.sleep() # delay for timestamps
test.write(['work2', 'src2', 'xxx', 'include.h'], r"""
#include
#ifdef FOO
@@ -412,38 +399,37 @@ work2/src2/xxx/include.h: BAR
repository/src2/xxx/main.c: BAR
""")
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
-#
test.unlink(['work2', 'src2', 'include', 'my_string.h'])
-
test.run(chdir='work2', options=opts, arguments='build2')
-test.run(program=work2_build2_foo_src2_xxx_xxx, stdout="""\
+test.run(
+ program=work2_build2_foo_src2_xxx_xxx,
+ stdout="""\
repository/src2/include/my_string.h: FOO
work2/src2/xxx/include.h: FOO
repository/src2/xxx/main.c: FOO
-""")
+""",
+)
-test.run(program=work2_build2_bar_src2_xxx_xxx, stdout="""\
+test.run(
+ program=work2_build2_bar_src2_xxx_xxx,
+ stdout="""\
repository/src2/include/my_string.h: BAR
work2/src2/xxx/include.h: BAR
repository/src2/xxx/main.c: BAR
-""")
+""",
+)
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
-#
test.pass_test()
# Local Variables:
diff --git a/test/Touch.py b/test/Touch.py
index 431cd6c..3538c7d 100644
--- a/test/Touch.py
+++ b/test/Touch.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Verify that the Touch() Action works.
@@ -34,30 +33,29 @@ import TestSCons
test = TestSCons.TestSCons()
-test.write('SConstruct', """
+test.write('SConstruct', """\
Execute(Touch('f1'))
Execute(Touch(File('f1-File')))
+
def cat(env, source, target):
target = str(target[0])
with open(target, "wb") as f:
for src in source:
with open(str(src), "rb") as ifp:
f.write(ifp.read())
+
Cat = Action(cat)
env = Environment()
env.Command('f2.out', 'f2.in', [Cat, Touch("f3")])
env = Environment(FILE='f4')
env.Command('f5.out', 'f5.in', [Touch("$FILE"), Cat])
-env.Command('f6.out', 'f6.in', [Cat,
- Touch("Touch-$SOURCE"),
- Touch("$TARGET-Touch")])
+env.Command('f6.out', 'f6.in', [Cat, Touch("Touch-$SOURCE"), Touch("$TARGET-Touch")])
# Make sure Touch works with a list of arguments
env = Environment()
-env.Command('f7.out', 'f7.in', [Cat,
- Touch(["Touch-$SOURCE",
- "$TARGET-Touch",
- File("f8")])])
+env.Command(
+ 'f7.out', 'f7.in', [Cat, Touch(["Touch-$SOURCE", "$TARGET-Touch", File("f8")])]
+)
""")
test.write('f1', "f1\n")
@@ -70,11 +68,12 @@ test.write('f7.in', "f7.in\n")
old_f1_time = os.path.getmtime(test.workpath('f1'))
old_f1_File_time = os.path.getmtime(test.workpath('f1-File'))
-expect = test.wrap_stdout(read_str = """\
+expect = test.wrap_stdout(
+ read_str="""\
Touch("f1")
Touch("f1-File")
""",
- build_str = """\
+ build_str="""\
cat(["f2.out"], ["f2.in"])
Touch("f3")
Touch("f4")
@@ -84,11 +83,11 @@ Touch("Touch-f6.in")
Touch("f6.out-Touch")
cat(["f7.out"], ["f7.in"])
Touch(["Touch-f7.in", "f7.out-Touch", "f8"])
-""")
-test.run(options = '-n', arguments = '.', stdout = expect)
-
-test.sleep(2)
+""",
+)
+test.run(options='-n', arguments='.', stdout=expect)
+test.sleep() # delay for timestamps
new_f1_time = os.path.getmtime(test.workpath('f1'))
test.fail_test(old_f1_time != new_f1_time)
new_f1_File_time = os.path.getmtime(test.workpath('f1-File'))
diff --git a/test/chained-build.py b/test/chained-build.py
index 871a593..10d0b46 100644
--- a/test/chained-build.py
+++ b/test/chained-build.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import TestSCons
@@ -58,23 +57,20 @@ test.write(['w1', 'SConstruct1'], SConstruct1_contents)
test.write(['w1', 'SConstruct2'], SConstruct2_contents)
test.write(['w1', 'foo.in'], "foo.in 1")
-test.run(chdir='w1',
- arguments="--max-drift=0 -f SConstruct1 foo.mid",
- stdout = test.wrap_stdout('build(["foo.mid"], ["foo.in"])\n'))
-
-test.run(chdir='w1',
- arguments="--max-drift=0 -f SConstruct2 foo.out",
- stdout = test.wrap_stdout('build(["foo.out"], ["foo.mid"])\n'))
-
-test.up_to_date(chdir='w1',
- options="--max-drift=0 -f SConstruct1",
- arguments="foo.mid")
-
-test.up_to_date(chdir='w1',
- options="--max-drift=0 -f SConstruct2",
- arguments="foo.out")
-
-test.sleep() # make sure foo.in rewrite has new mod-time
+test.run(
+ chdir='w1',
+ arguments="--max-drift=0 -f SConstruct1 foo.mid",
+ stdout=test.wrap_stdout('build(["foo.mid"], ["foo.in"])\n'),
+)
+test.run(
+ chdir='w1',
+ arguments="--max-drift=0 -f SConstruct2 foo.out",
+ stdout=test.wrap_stdout('build(["foo.out"], ["foo.mid"])\n'),
+)
+test.up_to_date(chdir='w1', options="--max-drift=0 -f SConstruct1", arguments="foo.mid")
+test.up_to_date(chdir='w1', options="--max-drift=0 -f SConstruct2", arguments="foo.out")
+
+test.sleep() # delay for timestamps
test.write(['w1', 'foo.in'], "foo.in 2")
# Because we're using --max-drift=0, we use the cached csig value
@@ -86,17 +82,19 @@ test.up_to_date(chdir='w1',
# Now try with --max-drift disabled. The build of foo.out should still
# be considered up-to-date, but the build of foo.mid now detects the
# change and rebuilds, too, which then causes a rebuild of foo.out.
-test.up_to_date(chdir='w1',
- options="--max-drift=-1 -f SConstruct2",
- arguments="foo.out")
-
-test.run(chdir='w1',
- arguments="--max-drift=-1 -f SConstruct1 foo.mid",
- stdout = test.wrap_stdout('build(["foo.mid"], ["foo.in"])\n'))
-
-test.run(chdir='w1',
- arguments="--max-drift=-1 -f SConstruct2 foo.out",
- stdout = test.wrap_stdout('build(["foo.out"], ["foo.mid"])\n'))
+test.up_to_date(
+ chdir='w1', options="--max-drift=-1 -f SConstruct2", arguments="foo.out"
+)
+test.run(
+ chdir='w1',
+ arguments="--max-drift=-1 -f SConstruct1 foo.mid",
+ stdout=test.wrap_stdout('build(["foo.mid"], ["foo.in"])\n'),
+)
+test.run(
+ chdir='w1',
+ arguments="--max-drift=-1 -f SConstruct2 foo.out",
+ stdout=test.wrap_stdout('build(["foo.out"], ["foo.mid"])\n'),
+)
test.pass_test()
diff --git a/test/sconsign/script/Signatures.py b/test/sconsign/script/Signatures.py
index ef8bac0..2225f83 100644
--- a/test/sconsign/script/Signatures.py
+++ b/test/sconsign/script/Signatures.py
@@ -44,8 +44,8 @@ if NEED_HELPER:
# in the expected output because paths in the .sconsign files are
# canonicalized to use / as the separator.
-sub1_hello_c = 'sub1/hello.c'
-sub1_hello_obj = 'sub1/hello.obj'
+sub1_hello_c = 'sub1/hello.c'
+sub1_hello_obj = 'sub1/hello.obj'
test.subdir('sub1', 'sub2')
@@ -135,7 +135,7 @@ env2.Program('sub2/hello.c')
""",
)
# TODO in the above, we would normally want to run a python program
-# using "our python" like this:
+# using "our Python" like this:
# CCCOM=[[r'{_python_}', r'{fake_cc_py}', 'sub2', '$TARGET', '$SOURCE']],
# LINKCOM=[[r'{_python_}', r'{fake_link_py}', '$TARGET', '$SOURCE']],
# however we're looking at dependencies with sconsign, so that breaks things.
@@ -160,17 +160,16 @@ test.write(['sub2', 'inc2.h'], r"""\
#define STRING2 "inc2.h"
""")
-test.sleep()
-
+test.sleep() # delay for timestamps
test.run(arguments = '. --max-drift=1')
sig_re = r'[0-9a-fA-F]{32,64}'
date_re = r'\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d'
-
database_name = test.get_sconsignname()
-test.run_sconsign(arguments = f"-e hello.exe -e hello.obj sub1/{database_name}",
- stdout = r"""hello.exe: %(sig_re)s \d+ \d+
+test.run_sconsign(
+ arguments=f"-e hello.exe -e hello.obj sub1/{database_name}",
+ stdout=r"""hello.exe: %(sig_re)s \d+ \d+
%(sub1_hello_obj)s: %(sig_re)s \d+ \d+
fake_link\.py: None \d+ \d+
%(sig_re)s \[.*\]
@@ -178,10 +177,12 @@ hello.obj: %(sig_re)s \d+ \d+
%(sub1_hello_c)s: None \d+ \d+
fake_cc\.py: None \d+ \d+
%(sig_re)s \[.*\]
-""" % locals())
+""" % locals(),
+)
-test.run_sconsign(arguments = f"-e hello.exe -e hello.obj -r sub1/{database_name}",
- stdout = r"""hello.exe: %(sig_re)s '%(date_re)s' \d+
+test.run_sconsign(
+ arguments=f"-e hello.exe -e hello.obj -r sub1/{database_name}",
+ stdout=r"""hello.exe: %(sig_re)s '%(date_re)s' \d+
%(sub1_hello_obj)s: %(sig_re)s '%(date_re)s' \d+
fake_link\.py: None '%(date_re)s' \d+
%(sig_re)s \[.*\]
@@ -189,7 +190,8 @@ hello.obj: %(sig_re)s '%(date_re)s' \d+
%(sub1_hello_c)s: None '%(date_re)s' \d+
fake_cc\.py: None '%(date_re)s' \d+
%(sig_re)s \[.*\]
-""" % locals())
+""" % locals(),
+)
test.pass_test()
diff --git a/test/sconsign/script/dblite.py b/test/sconsign/script/dblite.py
index 1fcf8c0..24d8403 100644
--- a/test/sconsign/script/dblite.py
+++ b/test/sconsign/script/dblite.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Verify that various ways of getting at a an sconsign file written with
@@ -97,17 +96,11 @@ main(int argc, char *argv[])
}
""")
-test.write(['sub2', 'inc1.h'], r"""\
-#define STRING1 "inc1.h"
-""")
-
-test.write(['sub2', 'inc2.h'], r"""\
-#define STRING2 "inc2.h"
-""")
-
-test.sleep()
+test.write(['sub2', 'inc1.h'], r'#define STRING1 "inc1.h"')
+test.write(['sub2', 'inc2.h'], r'#define STRING2 "inc2.h"')
-test.run(arguments = '. --max-drift=1')
+test.sleep() # delay for timestamps
+test.run(arguments='. --max-drift=1')
sig_re = r'[0-9a-fA-F]{32,64}'
date_re = r'\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d'
@@ -146,23 +139,23 @@ hello%(_obj)s: %(sig_re)s '%(date_re)s' \d+
common_flags = '-e hello%(_exe)s -e hello%(_obj)s -d sub1' % locals()
-test.run_sconsign(arguments = "%s my_sconsign" % common_flags,
- stdout = expect)
+test.run_sconsign(arguments="%s my_sconsign" % common_flags, stdout=expect)
-test.run_sconsign(arguments = "%s my_sconsign.dblite" % common_flags,
- stdout = expect)
+test.run_sconsign(arguments="%s my_sconsign.dblite" % common_flags, stdout=expect)
-test.run_sconsign(arguments = "%s -f dblite my_sconsign" % common_flags,
- stdout = expect)
+test.run_sconsign(arguments="%s -f dblite my_sconsign" % common_flags, stdout=expect)
-test.run_sconsign(arguments = "%s -f dblite my_sconsign.dblite" % common_flags,
- stdout = expect)
+test.run_sconsign(
+ arguments="%s -f dblite my_sconsign.dblite" % common_flags, stdout=expect
+)
-test.run_sconsign(arguments = "%s -r -f dblite my_sconsign" % common_flags,
- stdout = expect_r)
+test.run_sconsign(
+ arguments="%s -r -f dblite my_sconsign" % common_flags, stdout=expect_r
+)
-test.run_sconsign(arguments = "%s -r -f dblite my_sconsign.dblite" % common_flags,
- stdout = expect_r)
+test.run_sconsign(
+ arguments="%s -r -f dblite my_sconsign.dblite" % common_flags, stdout=expect_r
+)
test.pass_test()
--
cgit v0.12
From 53e33adc2a7cbd7d68020350060f73ea5977f002 Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Tue, 31 May 2022 09:11:42 -0600
Subject: Fix sider-detected typo in test change
Signed-off-by: Mats Wichmann
---
test/Copy-Action.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/Copy-Action.py b/test/Copy-Action.py
index 2179744..e135a9f 100644
--- a/test/Copy-Action.py
+++ b/test/Copy-Action.py
@@ -166,7 +166,7 @@ def must_be_same(f1, f2):
for value in ['ST_MODE', 'ST_MTIME']:
v = getattr(stat, value)
if s1[v] != s2[v]:
- msg = f"{f1!r}[{value}] {s1[v1]} != {f2!r}[{value}] {s2[v]}\n"
+ msg = f"{f1!r}[{value}] {s1[v]} != {f2!r}[{value}] {s2[v]}\n"
sys.stderr.write(msg)
errors += 1
--
cgit v0.12
From c9c678683a3944b0500a8ce68c80d7c5d48c9230 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Wed, 1 Jun 2022 14:28:03 -0700
Subject: Updated CHANGES/RELEASE to be a bit simpler. Fixed typo in docs for
SHELL_ENV_GENERATORS.
---
CHANGES.txt | 9 ++++++---
RELEASE.txt | 9 ++++++---
SCons/Action.py | 10 ++++++++--
SCons/Action.xml | 8 ++++----
test/Actions/subst_shell_env.py | 2 +-
5 files changed, 25 insertions(+), 13 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index d5aba9f..84ec06b 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -99,9 +99,12 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- Added user configurable setting of ninja depfile format via NINJA_DEPFILE_PARSE_FORMAT.
Now setting NINJA_DEPFILE_PARSE_FORMAT to [msvc,gcc,clang] can force the ninja expected
format. Compiler tools will also configure the variable automatically.
- - Added SHELL_ENV_GENERATORS construction variable. This variable
- is an iterable which will contain functions in which each are called and each can allow
- the user a method to customize the execution environment.
+ - Added SHELL_ENV_GENERATORS construction variable. This variable should be set to a list
+ (or an iterable) which contains functions to be called in order
+ when constructing the execution environment (Generally this is the shell environment
+ variables). This allows the user to customize how (for example) PATH is constructed.
+ Note that these are called for every build command run by SCons. It could have considerable
+ performance impact if not used carefully.
- Updated ninja scons daemon scripts to output errors to stderr as well as the daemon log.
- Fix typo in ninja scons daemon startup which causes ConnectionRefusedError to not retry
to connect to the server during start up.
diff --git a/RELEASE.txt b/RELEASE.txt
index 195e325..e7fa31c 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -16,9 +16,12 @@ NEW FUNCTIONALITY
- Added MSVC_USE_SCRIPT_ARGS variable to pass arguments to MSVC_USE_SCRIPT.
- Added Configure.CheckMember() checker to check if struct/class has the specified member.
-- Added SHELL_ENV_GENERATORS construction variable. This variable
- is an iterable which will contain functions in which each are called and each can allow
- the user a method to customize the execution environment.
+- Added SHELL_ENV_GENERATORS construction variable. This variable should be set to a list
+ (or an iterable) which contains functions to be called in order
+ when constructing the execution environment (Generally this is the shell environment
+ variables). This allows the user to customize how (for example) PATH is constructed.
+ Note that these are called for every build command run by SCons. It could have considerable
+ performance impact if not used carefully.
- Added MSVC_USE_SETTINGS variable to pass a dictionary to configure the msvc compiler
system environment as an alternative to bypassing Visual Studio autodetection entirely.
diff --git a/SCons/Action.py b/SCons/Action.py
index e29c4e9..6e67c7f 100644
--- a/SCons/Action.py
+++ b/SCons/Action.py
@@ -756,9 +756,15 @@ def get_default_ENV(env):
def _resolve_shell_env(env, target, source):
+ """
+ First get default environment.
+ Then if SHELL_ENV_GENERATORS is set and is iterable,
+ call each callable in that list to allow it to alter
+ the created execution environment.
+ """
ENV = get_default_ENV(env)
- shell_gen = env.get('SHELL_ENV_GENERATORS')
- if shell_gen is not None:
+ shell_gen = env.get('SHELL_ENV_GENERATORS')
+ if shell_gen:
try:
shell_gens = iter(shell_gen)
except TypeError:
diff --git a/SCons/Action.xml b/SCons/Action.xml
index 8994adb..1346db2 100644
--- a/SCons/Action.xml
+++ b/SCons/Action.xml
@@ -203,7 +203,7 @@ in which the command should be executed.
-Must be an iterable containing functions where each function generates or
+Must be a list (or an iterable) containing functions where each function generates or
alters the environment dictionary which will be used
when executing the &cv-link-SPAWN; function. The functions will initially
be passed a reference of the current execution environment (e.g. env['ENV']),
@@ -214,14 +214,14 @@ values.
This primary purpose of this construction variable is to give the user the ability
to substitute execution environment variables based on env, targets, and sources.
-If desired, the user can completly customize the execution environment for particular
+If desired, the user can completely customize the execution environment for particular
targets.
def custom_shell_env(env, target, source, shell_env):
- # customize shell_env if desired
- if str(target[0]) == 'special_target'is:
+ """customize shell_env if desired"""
+ if str(target[0]) == 'special_target':
shell_env['SPECIAL_VAR'] = env.subst('SOME_VAR', target=target, source=source)
return shell_env
diff --git a/test/Actions/subst_shell_env.py b/test/Actions/subst_shell_env.py
index d26f0ae..02ba640 100644
--- a/test/Actions/subst_shell_env.py
+++ b/test/Actions/subst_shell_env.py
@@ -25,7 +25,7 @@
"""
Verify that shell environment variables can be expanded per target/source
-when exectuting actions on the command line.
+when executing actions on the command line.
"""
import os
--
cgit v0.12
From 00dd82bf9a1f0b3332449a0add3a5f3d5c78c8c2 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Thu, 2 Jun 2022 10:25:48 -0700
Subject: Move logic which waits for a process to die into SCons.Util
wait_for_process_to_die(pid)
---
SCons/Tool/ninja/NinjaState.py | 29 ++---------------------------
SCons/Util.py | 35 +++++++++++++++++++++++++++++++++++
2 files changed, 37 insertions(+), 27 deletions(-)
diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py
index 5f8d5c7..770da07 100644
--- a/SCons/Tool/ninja/NinjaState.py
+++ b/SCons/Tool/ninja/NinjaState.py
@@ -37,7 +37,7 @@ import hashlib
import SCons
from SCons.Script import COMMAND_LINE_TARGETS
-from SCons.Util import is_List
+from SCons.Util import is_List, wait_for_process_to_die
from SCons.Errors import InternalError
from .Globals import COMMAND_TYPES, NINJA_RULES, NINJA_POOLS, \
NINJA_CUSTOM_HANDLERS, NINJA_DEFAULT_TARGETS
@@ -644,32 +644,7 @@ class NinjaState:
pass
# wait for the server process to fully killed
- try:
- import psutil
- while True:
- if pid not in [proc.pid for proc in psutil.process_iter()]:
- break
- else:
- time.sleep(0.1)
- except ImportError:
- # if psutil is not installed we can do this the hard way
- while True:
- if sys.platform == 'win32':
- import ctypes
- PROCESS_QUERY_INFORMATION = 0x1000
- processHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, 0,pid)
- if processHandle == 0:
- break
- else:
- ctypes.windll.kernel32.CloseHandle(processHandle)
- time.sleep(0.1)
- else:
- try:
- os.kill(pid, 0)
- except OSError:
- break
- else:
- time.sleep(0.1)
+ wait_for_process_to_die(pid)
if os.path.exists(scons_daemon_dirty):
os.unlink(scons_daemon_dirty)
diff --git a/SCons/Util.py b/SCons/Util.py
index 093ca41..0fe42c8 100644
--- a/SCons/Util.py
+++ b/SCons/Util.py
@@ -29,6 +29,7 @@ import os
import pprint
import re
import sys
+import time
from collections import UserDict, UserList, UserString, OrderedDict
from collections.abc import MappingView
from contextlib import suppress
@@ -2123,6 +2124,40 @@ def print_time():
from SCons.Script.Main import print_time
return print_time
+
+def wait_for_process_to_die(pid):
+ """
+ Wait for specified process to die, or alternatively kill it
+ NOTE: This function operates best with psutil pypi package
+ """
+ # wait for the process to fully killed
+ try:
+ import psutil
+ while True:
+ if pid not in [proc.pid for proc in psutil.process_iter()]:
+ break
+ else:
+ time.sleep(0.1)
+ except ImportError:
+ # if psutil is not installed we can do this the hard way
+ while True:
+ if sys.platform == 'win32':
+ import ctypes
+ PROCESS_QUERY_INFORMATION = 0x1000
+ processHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, 0,pid)
+ if processHandle == 0:
+ break
+ else:
+ ctypes.windll.kernel32.CloseHandle(processHandle)
+ time.sleep(0.1)
+ else:
+ try:
+ os.kill(pid, 0)
+ except OSError:
+ break
+ else:
+ time.sleep(0.1)
+
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
--
cgit v0.12
From a2dffb888472f55fad92e539d24b929b292fcb07 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Thu, 2 Jun 2022 11:27:25 -0700
Subject: Updated CHANGES/RELEASE contents
---
CHANGES.txt | 18 +++++++++---------
RELEASE.txt | 13 ++++++++-----
2 files changed, 17 insertions(+), 14 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index d21aa60..08fdd34 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -81,8 +81,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
only used for ninja)
- Ninja: Fix issue where Configure files weren't being properly processed when build run
via ninja.
- - Added ninja mingw support and improved ninja CommandGeneratorAction support.
- - Update ninja file generation to only create response files for build commands
+ - Ninja: Added ninja mingw support and improved ninja CommandGeneratorAction support.
+ - Ninja: Update ninja file generation to only create response files for build commands
which exceed MAXLINELENGTH
- Ninja: Added NINJA_GENERATED_SOURCE_ALIAS_NAME which allows user to specify an
Alias() which the ninja tool can use to determine which files are generated sources.
@@ -93,16 +93,16 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
required by other build targets. Code contributed by MongoDB
The downstream commit is here:
https://github.com/mongodb/mongo/commit/2fef432fa6e7cf3fd4f22ba3b193222c2887f14f
- - Added special case for ninja scons daemon to work in win32 python3.6 environments.
+ - Ninja: Added special case for ninja scons daemon to work in win32 python3.6 environments.
This particular environment does a bad job managing popen standard file handles, so
some special workarounds are needed.
- - Added user configurable setting of ninja depfile format via NINJA_DEPFILE_PARSE_FORMAT.
+ - Ninja:Added user configurable setting of ninja depfile format via NINJA_DEPFILE_PARSE_FORMAT.
Now setting NINJA_DEPFILE_PARSE_FORMAT to [msvc,gcc,clang] can force the ninja expected
format. Compiler tools will also configure the variable automatically.
- - Made ninja tool force the ninja file as the only target. Also improved the default
- targets setup and made sure there is always a default target for
+ - Ninja: Made ninja tool force the ninja file as the only target.
+ - Ninja: Improved the default targets setup and made sure there is always a default target for
the ninja file, which excludes targets that start and stop the daemon.
- - Update ninja tool so targets passed to SCons are propgated to ninja when scons
+ - Ninja: Update ninja tool so targets passed to SCons are propagated to ninja when scons
automatically executes ninja.
- Small refactor of scons daemons using a shared StateInfo class for communication
between the scons interactive thread and the http server thread. Added error handling
@@ -110,8 +110,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- Added SHELL_ENV_GENERATOR construction variables. This variable allows the user to Define
a function which will be called to generate or alter the execution environment which will
be used in the shell command of some Action.
- - Updated ninja scons daemon scripts to output errors to stderr as well as the daemon log.
- - Fix typo in ninja scons daemon startup which causes ConnectionRefusedError to not retry
+ - Ninja: Updated ninja scons daemon scripts to output errors to stderr as well as the daemon log.
+ - Ninja: Fix typo in ninja scons daemon startup which causes ConnectionRefusedError to not retry
to connect to the server during start up.
From Mats Wichmann:
diff --git a/RELEASE.txt b/RELEASE.txt
index ce2b8e5..832698d 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -58,17 +58,17 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
".scons_msvc_cache" so there should be no transition problem if using the
default; if using a custom cache file name, the cache should still be
manually removed if there are problems to transition to the new style.
-- Update ninja file generation to only create response files for build commands
+- Ninja: Update ninja file generation to only create response files for build commands
which exceed MAXLINELENGTH
- Update the debug output written to stdout for MSVC initialization which is enabled
by setting SCONS_MSCOMMON_DEBUG=- to use the logging module. Also changed the debug
output format written to stdout to include more information about the source for each
message of MSVC initialization debugging output. A single space was added before the
message for all debugging output records written to stdout and to files.
-- Made ninja tool force the ninja file as the only target. Also improved the default
+- Ninja: Made ninja tool force the ninja file as the only target. Also improved the default
targets setup and made sure there is always a default target for
the ninja file, which excludes targets that start and stop the daemon.
-- Update ninja tool so targets passed to SCons are propgated to ninja when scons
+- Ninja: Update ninja tool so targets passed to SCons are propgated to ninja when scons
automatically executes ninja.
- Add JavaScanner to include JAVACLASSPATH as a dependency when using the Java tool.
- The build argument (i.e., x86) is no longer passed to the MSVC 6.0 to 7.1 batch
@@ -138,7 +138,7 @@ IMPROVEMENTS
- Verify that a user specified msvc script (via MSVC_USE_SCRIPT) exists and raise an
exception immediately when the user specified msvc script does not exist.
- Add cache-debug messages for push failures.
-- Added ninja mingw support and improved ninja CommandGeneratorAction support.
+- Ninja: Added ninja mingw support and improved ninja CommandGeneratorAction support.
- Command-line help is now sensitive to the size of the terminal window: the
width of the help text will scale for terminals other than 80 chars wide.
- Refactor the msvc code so that the same data structures are used during initial msvc detection
@@ -148,7 +148,10 @@ IMPROVEMENTS
- Small refactor of scons daemons using a shared StateInfo class for communication
between the scons interactive thread and the http server thread. Added error handling
for scons interactive failing to startup.
-- Updated ninja scons daemon scripts to output errors to stderr as well as the daemon log.
+- Ninja: Updated ninja scons daemon scripts to output errors to stderr as well as the daemon log.
+- Ninja: Ensure that all targets set as default via Default() in SConstruct/SConscripts are
+ default targets in the generated ninja.build file.
+
PACKAGING
---------
--
cgit v0.12
From b5ebe82c9867ad9da154e57c70d1f63f2d98d3a1 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Thu, 2 Jun 2022 12:11:10 -0700
Subject: Updated ninja docs in users guilde.
---
SCons/Util.py | 2 +-
doc/user/external.xml | 54 ++++++++++++++++++++++++++++++++++++++-------------
2 files changed, 42 insertions(+), 14 deletions(-)
diff --git a/SCons/Util.py b/SCons/Util.py
index 0fe42c8..0eeef2a 100644
--- a/SCons/Util.py
+++ b/SCons/Util.py
@@ -2138,7 +2138,7 @@ def wait_for_process_to_die(pid):
break
else:
time.sleep(0.1)
- except ImportError:
+ except ImportError:
# if psutil is not installed we can do this the hard way
while True:
if sys.platform == 'win32':
diff --git a/doc/user/external.xml b/doc/user/external.xml
index b483196..e15f998 100644
--- a/doc/user/external.xml
+++ b/doc/user/external.xml
@@ -303,25 +303,45 @@
Ninja Build Generator
-
-
-
+
+
This is an experimental new feature.
It is subject to change and/or removal without a depreciation cycle.
-
+
+
+
+
+ Loading the &t-link-ninja; tool into SCons will make significant changes
+ in SCons' normal functioning.
+
+
+
- To enable this feature you'll need to use one of the following:
+ SCons will no longer execute any commands directly and will only create the build.ninja and
+ run ninja.
-
-
-# On the command line
---experimental=ninja
+
+
+
+ Any targets specified on the command line will be passed along to &ninja;
+
+
+
+
+
+ To enable this feature you'll need to use one of the following:
+
+
+
+# On the command line --experimental=ninja
# Or in your SConstruct
SetOption('experimental', 'ninja')
-
-
+
+
+
+
Ninja is a small build system that tries to be fast
@@ -366,8 +386,10 @@ conda install -c conda-forge ninja
It is not expected that the &b-link-Ninja; builder will work for all builds at this point. It is still under active
- development. If you find that your build doesn't work with &ninja; please bring this to the users mailing list
- or #scons-help channel on our Discord server.
+ development. If you find that your build doesn't work with &ninja; please bring this to the users mailing list
+ or
+ #scons-help
+ channel on our Discord server.
@@ -377,6 +399,12 @@ conda install -c conda-forge ninja
implement those actions via shell commands in the &ninja; build file.
+
+ When ninja runs the generated &ninja; build file, ninja will launch &scons; as a daemon and feed commands
+ to that &scons; process which &ninja; is unable to build directly. This daemon will stay alive until
+ explicitly killed or it times out. The timeout is set by &cv-link-NINJA_SCONS_DAEMON_KEEP_ALIVE; .
+
+
See:
--
cgit v0.12
From a3d7d83d64a711b8b1738091c7eb6eebe4666716 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Thu, 2 Jun 2022 13:42:36 -0700
Subject: Fix sider complaints
---
SCons/Tool/ninja/NinjaState.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py
index 770da07..8f7e04b 100644
--- a/SCons/Tool/ninja/NinjaState.py
+++ b/SCons/Tool/ninja/NinjaState.py
@@ -29,7 +29,6 @@ import signal
import tempfile
import shutil
import sys
-import time
from os.path import splitext
from tempfile import NamedTemporaryFile
import ninja
@@ -37,7 +36,7 @@ import hashlib
import SCons
from SCons.Script import COMMAND_LINE_TARGETS
-from SCons.Util import is_List, wait_for_process_to_die
+from SCons.Util import wait_for_process_to_die
from SCons.Errors import InternalError
from .Globals import COMMAND_TYPES, NINJA_RULES, NINJA_POOLS, \
NINJA_CUSTOM_HANDLERS, NINJA_DEFAULT_TARGETS
--
cgit v0.12
From b511d618966ff82b2200824123793301039cf579 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Fri, 3 Jun 2022 10:30:20 -0700
Subject: [ci skip] Add note to handle processes not dying properly by raising
exception and handling in calling function
---
SCons/Tool/ninja/NinjaState.py | 2 ++
SCons/Util.py | 1 +
2 files changed, 3 insertions(+)
diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py
index c9cacce..c168c7f 100644
--- a/SCons/Tool/ninja/NinjaState.py
+++ b/SCons/Tool/ninja/NinjaState.py
@@ -643,6 +643,8 @@ class NinjaState:
pass
# wait for the server process to fully killed
+ # TODO: update wait_for_process_to_die() to handle timeout and then catch exception
+ # here and do something smart.
wait_for_process_to_die(pid)
if os.path.exists(scons_daemon_dirty):
diff --git a/SCons/Util.py b/SCons/Util.py
index 0eeef2a..49a3a0f 100644
--- a/SCons/Util.py
+++ b/SCons/Util.py
@@ -2129,6 +2129,7 @@ def wait_for_process_to_die(pid):
"""
Wait for specified process to die, or alternatively kill it
NOTE: This function operates best with psutil pypi package
+ TODO: Add timeout which raises exception
"""
# wait for the process to fully killed
try:
--
cgit v0.12
From ae46e4569da500e0a9270fda61dd983384f68a3a Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Fri, 3 Jun 2022 10:53:50 -0700
Subject: [ci skip] Added blurb about ninja restarting the scons daemon if it
detects need to regenerate the build.ninja
---
doc/user/external.xml | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/doc/user/external.xml b/doc/user/external.xml
index e15f998..0eab32e 100644
--- a/doc/user/external.xml
+++ b/doc/user/external.xml
@@ -400,9 +400,14 @@ conda install -c conda-forge ninja
- When ninja runs the generated &ninja; build file, ninja will launch &scons; as a daemon and feed commands
+ When &ninja; runs the generated &ninja; build file, &ninja; will launch &scons; as a daemon and feed commands
to that &scons; process which &ninja; is unable to build directly. This daemon will stay alive until
- explicitly killed or it times out. The timeout is set by &cv-link-NINJA_SCONS_DAEMON_KEEP_ALIVE; .
+ explicitly killed, or it times out. The timeout is set by &cv-link-NINJA_SCONS_DAEMON_KEEP_ALIVE; .
+
+
+
+ The daemon will be restarted if any &SConscript; file(s) change or the build changes in a way that &ninja; determines
+ it needs to regenerate the build.ninja file
See:
--
cgit v0.12
From 48d31b80ce66afba4b495c1dcad44d9d38d2c242 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Fri, 3 Jun 2022 14:22:21 -0700
Subject: If no newline at end of message supplied to skip_test(), then we
write one to stdout after the original message
---
testing/framework/TestCommon.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py
index 1999f74..e8045ca 100644
--- a/testing/framework/TestCommon.py
+++ b/testing/framework/TestCommon.py
@@ -782,6 +782,8 @@ class TestCommon(TestCmd):
"""
if message:
sys.stdout.write(message)
+ if not message.endswith('\n'):
+ sys.stdout.write('\n')
sys.stdout.flush()
pass_skips = os.environ.get('TESTCOMMON_PASS_SKIPS')
if pass_skips in [None, 0, '0']:
--
cgit v0.12
From 23a35cec7896e567ff81a8afaab99ab87ed92b8b Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Sat, 4 Jun 2022 07:24:05 -0600
Subject: Update ActionTests for Python 3.11
Required all new bytecode strings.
Some reformatting done (this is not a full formatting run).
Partial fix for #4162, do not close only for this PR.
Signed-off-by: Mats Wichmann
---
SCons/ActionTests.py | 344 +++++++++++++++++++++++++++------------------------
1 file changed, 185 insertions(+), 159 deletions(-)
diff --git a/SCons/ActionTests.py b/SCons/ActionTests.py
index 29ed4a7..0a7f25b 100644
--- a/SCons/ActionTests.py
+++ b/SCons/ActionTests.py
@@ -71,16 +71,16 @@ with open(sys.argv[1], 'w') as f:
if 'ACTPY_PIPE' in os.environ:
if 'PIPE_STDOUT_FILE' in os.environ:
- with open(os.environ['PIPE_STDOUT_FILE'], 'r') as f:
- stdout_msg = f.read()
+ with open(os.environ['PIPE_STDOUT_FILE'], 'r') as f:
+ stdout_msg = f.read()
else:
- stdout_msg = "act.py: stdout: executed act.py %s\\n" % ' '.join(sys.argv[1:])
- sys.stdout.write( stdout_msg )
+ stdout_msg = "act.py: stdout: executed act.py %s\\n" % ' '.join(sys.argv[1:])
+ sys.stdout.write(stdout_msg)
if 'PIPE_STDERR_FILE' in os.environ:
- with open(os.environ['PIPE_STDERR_FILE'], 'r') as f:
- stderr_msg = f.read()
+ with open(os.environ['PIPE_STDERR_FILE'], 'r') as f:
+ stderr_msg = f.read()
else:
- stderr_msg = "act.py: stderr: executed act.py %s\\n" % ' '.join(sys.argv[1:])
+ stderr_msg = "act.py: stderr: executed act.py %s\\n" % ' '.join(sys.argv[1:])
sys.stderr.write(stderr_msg)
sys.exit(0)
""")
@@ -113,8 +113,9 @@ class CmdStringHolder:
return self.literal
def escape(self, escape_func):
- """Escape the string with the supplied function. The
- function is expected to take an arbitrary string, then
+ """Escape the string with the supplied function.
+
+ The function is expected to take an arbitrary string, then
return it with all special characters escaped and ready
for passing to the command interpreter.
@@ -251,8 +252,8 @@ def test_varlist(pos_call, str_call, cmd, cmdstrfunc, **kw):
def test_positional_args(pos_callback, cmd, **kw):
- """Test that Action() returns the expected type and that positional args work.
- """
+ """Test that Action returns the expected type and positional args work."""
+
act = SCons.Action.Action(cmd, **kw)
pos_callback(act)
assert act.varlist == (), act.varlist
@@ -293,7 +294,7 @@ def test_positional_args(pos_callback, cmd, **kw):
test_varlist(pos_callback, none, cmd, None, **kw)
- """Test handling of bad cmdstrfunc arguments """
+ # Test handling of bad cmdstrfunc arguments
try:
a = SCons.Action.Action(cmd, [], **kw)
except SCons.Errors.UserError as e:
@@ -310,8 +311,7 @@ class ActionTestCase(unittest.TestCase):
"""Test the Action() factory function"""
def test_FunctionAction(self):
- """Test the Action() factory's creation of FunctionAction objects
- """
+ """Test the Action() factory's creation of FunctionAction objects."""
def foo():
pass
@@ -325,8 +325,7 @@ class ActionTestCase(unittest.TestCase):
test_positional_args(func_action, [foo])
def test_CommandAction(self):
- """Test the Action() factory's creation of CommandAction objects
- """
+ """Test the Action() factory's creation of CommandAction objects."""
def cmd_action(a):
assert isinstance(a, SCons.Action.CommandAction), a
@@ -343,8 +342,8 @@ class ActionTestCase(unittest.TestCase):
test_positional_args(line_action, [["explicit", "command", "line"]])
def test_ListAction(self):
- """Test the Action() factory's creation of ListAction objects
- """
+ """Test the Action() factory's creation of ListAction objects."""
+
a1 = SCons.Action.Action(["x", "y", "z", ["a", "b", "c"]])
assert isinstance(a1, SCons.Action.ListAction), a1
assert a1.varlist == (), a1.varlist
@@ -401,8 +400,7 @@ class ActionTestCase(unittest.TestCase):
assert a5.list[1].strfunction == foo, a5.list[1].strfunction
def test_CommandGeneratorAction(self):
- """Test the Action() factory's creation of CommandGeneratorAction objects
- """
+ """Test the Action factory's creation of CommandGeneratorAction objects."""
def foo(): pass
@@ -413,8 +411,7 @@ class ActionTestCase(unittest.TestCase):
test_positional_args(gen_action, foo, generator=1)
def test_LazyCmdGeneratorAction(self):
- """Test the Action() factory's creation of lazy CommandGeneratorAction objects
- """
+ """Test the Action factory's creation of lazy CommandGeneratorAction objects."""
def lazy_action(a):
assert isinstance(a, SCons.Action.LazyAction), a
@@ -425,8 +422,8 @@ class ActionTestCase(unittest.TestCase):
test_positional_args(lazy_action, "${FOO}")
def test_no_action(self):
- """Test when the Action() factory can't create an action object
- """
+ """Test when the Action() factory can't create an action object."""
+
try:
a5 = SCons.Action.Action(1)
except TypeError:
@@ -435,8 +432,8 @@ class ActionTestCase(unittest.TestCase):
assert 0, "Should have thrown a TypeError creating Action from an int."
def test_reentrance(self):
- """Test the Action() factory when the action is already an Action object
- """
+ """Test the Action factory when the action is already an Action object."""
+
a1 = SCons.Action.Action("foo")
a2 = SCons.Action.Action(a1)
assert a2 is a1, a2
@@ -445,8 +442,7 @@ class ActionTestCase(unittest.TestCase):
class _ActionActionTestCase(unittest.TestCase):
def test__init__(self):
- """Test creation of _ActionAction objects
- """
+ """Test creation of _ActionAction objects."""
def func1():
pass
@@ -519,8 +515,7 @@ class _ActionActionTestCase(unittest.TestCase):
assert a.varlist is t, a.varlist
def test_dup_keywords(self):
- """Test handling of both cmdstr and strfunction arguments
- """
+ """Test handling of both cmdstr and strfunction arguments."""
def func():
pass
@@ -535,8 +530,8 @@ class _ActionActionTestCase(unittest.TestCase):
raise Exception("did not catch expected UserError")
def test___cmp__(self):
- """Test Action comparison
- """
+ """Test Action comparison."""
+
a1 = SCons.Action.Action("x")
a2 = SCons.Action.Action("x")
assert a1 == a2
@@ -545,8 +540,8 @@ class _ActionActionTestCase(unittest.TestCase):
assert a2 != a3
def test_print_cmd_lines(self):
- """Test the print_cmd_lines() method
- """
+ """Test the print_cmd_lines() method."""
+
save_stdout = sys.stdout
try:
@@ -565,8 +560,8 @@ class _ActionActionTestCase(unittest.TestCase):
sys.stdout = save_stdout
def test___call__(self):
- """Test calling an Action
- """
+ """Test calling an Action."""
+
save_stdout = sys.stdout
save_print_actions = SCons.Action.print_actions
@@ -753,8 +748,8 @@ class _ActionActionTestCase(unittest.TestCase):
SCons.Action.execute_actions = save_execute_actions
def test_presub_lines(self):
- """Test the presub_lines() method
- """
+ """Test the presub_lines() method."""
+
env = Environment()
a = SCons.Action.Action("x")
s = a.presub_lines(env)
@@ -875,8 +870,8 @@ class _ActionActionTestCase(unittest.TestCase):
class CommandActionTestCase(unittest.TestCase):
def test___init__(self):
- """Test creation of a command Action
- """
+ """Test creation of a command Action."""
+
a = SCons.Action.CommandAction(["xyzzy"])
assert a.cmd_list == ["xyzzy"], a.cmd_list
assert a.cmdstr is _null, a.cmdstr
@@ -886,8 +881,8 @@ class CommandActionTestCase(unittest.TestCase):
assert a.cmdstr == "cadabra", a.cmdstr
def test___str__(self):
- """Test fetching the pre-substitution string for command Actions
- """
+ """Test fetching the pre-substitution string for command Actions."""
+
env = Environment()
act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE')
s = str(act)
@@ -900,8 +895,7 @@ class CommandActionTestCase(unittest.TestCase):
assert s == "xyzzy $TARGET $SOURCE $TARGETS $SOURCES", s
def test_genstring(self):
- """Test the genstring() method for command Actions
- """
+ """Test the genstring() method for command Actions."""
env = Environment()
t1 = DummyNode('t1')
@@ -938,8 +932,7 @@ class CommandActionTestCase(unittest.TestCase):
assert s == expect, s
def test_strfunction(self):
- """Test fetching the string representation of command Actions
- """
+ """Test fetching the string representation of command Actions."""
env = Environment()
t1 = DummyNode('t1')
@@ -1090,9 +1083,8 @@ class CommandActionTestCase(unittest.TestCase):
assert s == "foo bar", s
def test_execute(self):
- """Test execution of command Actions
+ """Test execution of command Actions."""
- """
try:
env = self.env
except AttributeError:
@@ -1247,8 +1239,7 @@ class CommandActionTestCase(unittest.TestCase):
assert r == 0, r
def test_set_handler(self):
- """Test setting the command handler...
- """
+ """Test setting the command handler..."""
class Test:
def __init__(self):
@@ -1299,8 +1290,7 @@ class CommandActionTestCase(unittest.TestCase):
assert t.executed == ['**xyzzy**'], t.executed
def test_get_contents(self):
- """Test fetching the contents of a command Action
- """
+ """Test fetching the contents of a command Action."""
def CmdGen(target, source, env, for_signature):
assert for_signature
@@ -1373,6 +1363,7 @@ class CommandActionTestCase(unittest.TestCase):
def test_get_implicit_deps(self):
"""Test getting the implicit dependencies of a command Action."""
+
class SpecialEnvironment(Environment):
def WhereIs(self, prog):
return prog
@@ -1413,11 +1404,11 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
def factory(self, act, **kw):
"""Pass any keywords as a dict"""
+
return SCons.Action.CommandGeneratorAction(act, kw)
def test___init__(self):
- """Test creation of a command generator Action
- """
+ """Test creation of a command generator Action."""
def f(target, source, env):
pass
@@ -1426,8 +1417,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
assert a.generator == f
def test___str__(self):
- """Test the pre-substitution strings for command generator Actions
- """
+ """Test the pre-substitution strings for command generator Actions."""
def f(target, source, env, for_signature, self=self):
# See if "env" is really a construction environment (or
@@ -1444,8 +1434,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
assert s == 'FOO', s
def test_genstring(self):
- """Test the command generator Action genstring() method
- """
+ """Test the command generator Action genstring() method."""
def f(target, source, env, for_signature, self=self):
dummy = env['dummy']
@@ -1459,8 +1448,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
assert s == "$FOO $TARGET $SOURCE $TARGETS $SOURCES", s
def test_execute(self):
- """Test executing a command generator Action
- """
+ """Test executing a command generator Action."""
def f(target, source, env, for_signature, self=self):
dummy = env['dummy']
@@ -1519,8 +1507,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
assert self.rfile_called
def test_get_contents(self):
- """Test fetching the contents of a command generator Action
- """
+ """Test fetching the contents of a command generator Action."""
def f(target, source, env, for_signature):
foo = env['foo']
@@ -1540,8 +1527,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
assert c == b"guux FFF BBB test", c
def test_get_contents_of_function_action(self):
- """Test contents of a CommandGeneratorAction-generated FunctionAction
- """
+ """Test contents of a CommandGeneratorAction-generated FunctionAction."""
def LocalFunc():
pass
@@ -1554,6 +1540,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
(3, 8): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 9): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 10): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
+ (3, 11): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'),
}
meth_matches = [
@@ -1571,12 +1558,12 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
a = self.factory(f_global)
c = a.get_contents(target=[], source=[], env=env)
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + repr(
+ assert c == func_matches[sys.version_info[:2]], f"Got\n{c!r}\nExpected\n" + repr(
func_matches[sys.version_info[:2]])
a = self.factory(f_local)
c = a.get_contents(target=[], source=[], env=env)
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + repr(
+ assert c == func_matches[sys.version_info[:2]], f"Got\n{c!r}\nExpected\n" + repr(
func_matches[sys.version_info[:2]])
def f_global2(target, source, env, for_signature):
@@ -1599,8 +1586,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
class FunctionActionTestCase(unittest.TestCase):
def test___init__(self):
- """Test creation of a function Action
- """
+ """Test creation of a function Action."""
def func1():
pass
@@ -1623,8 +1609,7 @@ class FunctionActionTestCase(unittest.TestCase):
assert a.strfunction == func3, a.strfunction
def test___str__(self):
- """Test the __str__() method for function Actions
- """
+ """Test the __str__() method for function Actions."""
def func1():
pass
@@ -1642,8 +1627,8 @@ class FunctionActionTestCase(unittest.TestCase):
assert s == "class1(target, source, env)", s
def test_execute(self):
- """Test executing a function Action
- """
+ """Test executing a function Action."""
+
self.inc = 0
def f(target, source, env):
@@ -1721,8 +1706,7 @@ class FunctionActionTestCase(unittest.TestCase):
assert self.string_it
def test_get_contents(self):
- """Test fetching the contents of a function Action
- """
+ """Test fetching the contents of a function Action."""
def LocalFunc():
pass
@@ -1734,6 +1718,7 @@ class FunctionActionTestCase(unittest.TestCase):
(3, 8): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 9): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 10): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
+ (3, 11): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'),
}
@@ -1744,6 +1729,7 @@ class FunctionActionTestCase(unittest.TestCase):
(3, 8): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 9): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 10): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'),
+ (3, 11): bytearray(b'1, 1, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'),
}
def factory(act, **kw):
@@ -1751,20 +1737,23 @@ class FunctionActionTestCase(unittest.TestCase):
a = factory(GlobalFunc)
c = a.get_contents(target=[], source=[], env=Environment())
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- func_matches[sys.version_info[:2]])
+ assert (
+ c == func_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(func_matches[sys.version_info[:2]])
a = factory(LocalFunc)
c = a.get_contents(target=[], source=[], env=Environment())
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- func_matches[sys.version_info[:2]])
+ assert (
+ c == func_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(func_matches[sys.version_info[:2]])
matches_foo = func_matches[sys.version_info[:2]] + b'foo'
a = factory(GlobalFunc, varlist=['XYZ'])
c = a.get_contents(target=[], source=[], env=Environment())
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- func_matches[sys.version_info[:2]])
+ assert (
+ c == func_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(func_matches[sys.version_info[:2]])
# assert c in func_matches, repr(c)
c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo'))
@@ -1775,8 +1764,9 @@ class FunctionActionTestCase(unittest.TestCase):
a = factory(GlobalFunc, varlist='XYZ')
c = a.get_contents(target=[], source=[], env=Environment())
# assert c in func_matches, repr(c)
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- func_matches[sys.version_info[:2]])
+ assert (
+ c == func_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(func_matches[sys.version_info[:2]])
c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo'))
assert c in matches_foo, repr(c)
@@ -1796,12 +1786,12 @@ class FunctionActionTestCase(unittest.TestCase):
lc = LocalClass()
a = factory(lc.LocalMethod)
c = a.get_contents(target=[], source=[], env=Environment())
- assert c == meth_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- meth_matches[sys.version_info[:2]])
+ assert (
+ c == meth_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(meth_matches[sys.version_info[:2]])
def test_strfunction(self):
- """Test the FunctionAction.strfunction() method
- """
+ """Test the FunctionAction.strfunction() method."""
def func():
pass
@@ -1825,8 +1815,7 @@ class FunctionActionTestCase(unittest.TestCase):
class ListActionTestCase(unittest.TestCase):
def test___init__(self):
- """Test creation of a list of subsidiary Actions
- """
+ """Test creation of a list of subsidiary Actions."""
def func():
pass
@@ -1838,8 +1827,7 @@ class ListActionTestCase(unittest.TestCase):
assert a.list[2].list[0].cmd_list == 'y'
def test___str__(self):
- """Test the __str__() method for a list of subsidiary Actions
- """
+ """Test the __str__() method for a list of subsidiary Actions."""
def f(target, source, env):
pass
@@ -1852,8 +1840,7 @@ class ListActionTestCase(unittest.TestCase):
assert s == "f(target, source, env)\ng(target, source, env)\nXXX\nf(target, source, env)", s
def test_genstring(self):
- """Test the genstring() method for a list of subsidiary Actions
- """
+ """Test the genstring() method for a list of subsidiary Actions."""
def f(target, source, env):
pass
@@ -1867,8 +1854,7 @@ class ListActionTestCase(unittest.TestCase):
assert s == "f(target, source, env)\ngenerated foo.x bar.y\nXXX\nf(target, source, env)", s
def test_execute(self):
- """Test executing a list of subsidiary Actions
- """
+ """Test executing a list of subsidiary Actions."""
self.inc = 0
def f(target, source, env):
@@ -1904,8 +1890,8 @@ class ListActionTestCase(unittest.TestCase):
assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c
def test_get_contents(self):
- """Test fetching the contents of a list of subsidiary Actions
- """
+ """Test fetching the contents of a list of subsidiary Actions."""
+
self.foo = 0
def gen(target, source, env, for_signature):
@@ -1923,8 +1909,8 @@ class ListActionTestCase(unittest.TestCase):
class LazyActionTestCase(unittest.TestCase):
def test___init__(self):
- """Test creation of a lazy-evaluation Action
- """
+ """Test creation of a lazy-evaluation Action."""
+
# Environment variable references should create a special type
# of LazyAction that lazily evaluates the variable for whether
# it's a string or something else before doing anything.
@@ -1937,8 +1923,7 @@ class LazyActionTestCase(unittest.TestCase):
assert a10.var == 'FOO', a10.var
def test_genstring(self):
- """Test the lazy-evaluation Action genstring() method
- """
+ """Test the lazy-evaluation Action genstring() method."""
def f(target, source, env):
pass
@@ -1952,8 +1937,7 @@ class LazyActionTestCase(unittest.TestCase):
assert s == 'xxx', s
def test_execute(self):
- """Test executing a lazy-evaluation Action
- """
+ """Test executing a lazy-evaluation Action."""
def f(target, source, env):
s = env['s']
@@ -1969,16 +1953,15 @@ class LazyActionTestCase(unittest.TestCase):
assert c == "act.py: 'lazy'\n", c
def test_get_contents(self):
- """Test fetching the contents of a lazy-evaluation Action
- """
+ """Test fetching the contents of a lazy-evaluation Action."""
+
a = SCons.Action.Action("${FOO}")
env = Environment(FOO=[["This", "is", "a", "test"]])
c = a.get_contents(target=[], source=[], env=env)
assert c == b"This is a test", c
def test_get_contents_of_function_action(self):
- """Test fetching the contents of a lazy-evaluation FunctionAction
- """
+ """Test fetching the contents of a lazy-evaluation FunctionAction."""
def LocalFunc():
pass
@@ -1990,6 +1973,7 @@ class LazyActionTestCase(unittest.TestCase):
(3, 8): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 9): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 10): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
+ (3, 11): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'),
}
meth_matches = [
@@ -2005,21 +1989,24 @@ class LazyActionTestCase(unittest.TestCase):
env = Environment(FOO=factory(GlobalFunc))
c = a.get_contents(target=[], source=[], env=env)
# assert c in func_matches, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(f) for f in func_matches])
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- func_matches[sys.version_info[:2]])
+ assert (
+ c == func_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(func_matches[sys.version_info[:2]])
env = Environment(FOO=factory(LocalFunc))
c = a.get_contents(target=[], source=[], env=env)
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- func_matches[sys.version_info[:2]])
+ assert (
+ c == func_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(func_matches[sys.version_info[:2]])
# matches_foo = [x + b"foo" for x in func_matches]
matches_foo = func_matches[sys.version_info[:2]] + b'foo'
env = Environment(FOO=factory(GlobalFunc, varlist=['XYZ']))
c = a.get_contents(target=[], source=[], env=env)
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- func_matches[sys.version_info[:2]])
+ assert (
+ c == func_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(func_matches[sys.version_info[:2]])
env['XYZ'] = 'foo'
c = a.get_contents(target=[], source=[], env=env)
@@ -2029,6 +2016,7 @@ class LazyActionTestCase(unittest.TestCase):
class ActionCallerTestCase(unittest.TestCase):
def test___init__(self):
"""Test creation of an ActionCaller"""
+
ac = SCons.Action.ActionCaller(1, [2, 3], {'FOO': 4, 'BAR': 5})
assert ac.parent == 1, ac.parent
assert ac.args == [2, 3], ac.args
@@ -2050,20 +2038,24 @@ class ActionCallerTestCase(unittest.TestCase):
(3, 8): b'd\x00S\x00',
(3, 9): b'd\x00S\x00',
(3, 10): b'd\x00S\x00',
-
+ (3, 11): b'\x97\x00d\x00S\x00',
}
af = SCons.Action.ActionFactory(GlobalFunc, strfunc)
ac = SCons.Action.ActionCaller(af, [], {})
c = ac.get_contents([], [], Environment())
- assert c == matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- matches[sys.version_info[:2]])
+ assert c == matches[sys.version_info[:2]], (
+ f"Got\n{c!r}\nExpected one of \n"
+ + repr(matches[sys.version_info[:2]])
+ )
af = SCons.Action.ActionFactory(LocalFunc, strfunc)
ac = SCons.Action.ActionCaller(af, [], {})
c = ac.get_contents([], [], Environment())
- assert c == matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- matches[sys.version_info[:2]])
+ assert c == matches[sys.version_info[:2]], (
+ f"Got\n{c!r}\nExpected one of \n"
+ + repr(matches[sys.version_info[:2]])
+ )
class LocalActFunc:
def __call__(self):
@@ -2072,14 +2064,18 @@ class ActionCallerTestCase(unittest.TestCase):
af = SCons.Action.ActionFactory(GlobalActFunc(), strfunc)
ac = SCons.Action.ActionCaller(af, [], {})
c = ac.get_contents([], [], Environment())
- assert c == matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- matches[sys.version_info[:2]])
+ assert c == matches[sys.version_info[:2]], (
+ f"Got\n{c!r}\nExpected one of \n"
+ + repr(matches[sys.version_info[:2]])
+ )
af = SCons.Action.ActionFactory(LocalActFunc(), strfunc)
ac = SCons.Action.ActionCaller(af, [], {})
c = ac.get_contents([], [], Environment())
- assert c == matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- matches[sys.version_info[:2]])
+ assert c == matches[sys.version_info[:2]], (
+ f"Got\n{c!r}\nExpected one of \n"
+ + repr(matches[sys.version_info[:2]])
+ )
matches = [
b"",
@@ -2089,13 +2085,12 @@ class ActionCallerTestCase(unittest.TestCase):
af = SCons.Action.ActionFactory(str, strfunc)
ac = SCons.Action.ActionCaller(af, [], {})
c = ac.get_contents([], [], Environment())
- assert c == "" or \
- c == "" or \
- c == "", repr(c)
+ assert c in ("", "", ""), repr(c)
# ^^ class str for python3
def test___call__(self):
"""Test calling an ActionCaller"""
+
actfunc_args = []
def actfunc(a1, a2, a3, args=actfunc_args):
@@ -2123,6 +2118,7 @@ class ActionCallerTestCase(unittest.TestCase):
def test_strfunction(self):
"""Test calling the ActionCaller strfunction() method"""
+
strfunc_args = []
def actfunc(a1, a2, a3, a4):
@@ -2159,6 +2155,7 @@ class ActionFactoryTestCase(unittest.TestCase):
def test___call__(self):
"""Test calling whatever's returned from an ActionFactory"""
+
actfunc_args = []
strfunc_args = []
@@ -2175,12 +2172,12 @@ class ActionFactoryTestCase(unittest.TestCase):
class ActionCompareTestCase(unittest.TestCase):
-
def test_1_solo_name(self):
"""Test Lazy Cmd Generator Action get_name alone.
Basically ensures we can locate the builder, comparing it to
itself along the way."""
+
bar = SCons.Builder.Builder(action={})
env = Environment(BUILDERS={'BAR': bar})
name = bar.get_name(env)
@@ -2191,12 +2188,12 @@ class ActionCompareTestCase(unittest.TestCase):
Ensure that we can compare builders (and thereby actions) to
each other safely."""
+
foo = SCons.Builder.Builder(action='$FOO', suffix='.foo')
bar = SCons.Builder.Builder(action={})
assert foo != bar
assert foo.action != bar.action
- env = Environment(BUILDERS={'FOO': foo,
- 'BAR': bar})
+ env = Environment(BUILDERS={'FOO': foo, 'BAR': bar})
name = foo.get_name(env)
assert name == 'FOO', name
name = bar.get_name(env)
@@ -2214,10 +2211,7 @@ class ActionCompareTestCase(unittest.TestCase):
bar = SCons.Builder.Builder(action={}, suffix={None: '.bar'})
bar.add_action('.cow', "$MOO")
dog = SCons.Builder.Builder(suffix='.bar')
-
- env = Environment(BUILDERS={'FOO': foo,
- 'BAR': bar,
- 'DOG': dog})
+ env = Environment(BUILDERS={'FOO': foo, 'BAR': bar, 'DOG': dog})
assert foo.get_name(env) == 'FOO', foo.get_name(env)
assert bar.get_name(env) == 'BAR', bar.get_name(env)
@@ -2236,7 +2230,6 @@ class TestClass:
class ObjectContentsTestCase(unittest.TestCase):
-
def test_function_contents(self):
"""Test that Action._function_contents works"""
@@ -2244,20 +2237,25 @@ class ObjectContentsTestCase(unittest.TestCase):
"""A test function"""
return a
- # Since the python bytecode has per version differences, we need different expected results per version
+ # Since the python bytecode has per version differences,
+ # we need different expected results per version
expected = {
(3, 5): (bytearray(b'3, 3, 0, 0,(),(),(|\x00\x00S),(),()')),
(3, 6): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()')),
(3, 7): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()')),
(3, 8): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()')),
(3, 9): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()')),
- (3, 10): (bytearray(b'3, 3, 0, 0,(N.),(),(|\x00S\x00),(),()'),
- bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()')) # 3.10.1, 3.10.2
+ (3, 10): (
+ bytearray(b'3, 3, 0, 0,(N.),(),(|\x00S\x00),(),()'),
+ bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'),
+ ), # 3.10.1, 3.10.2
+ (3, 11): bytearray(b'3, 3, 0, 0,(),(),(\x97\x00|\x00S\x00),(),()'),
}
c = SCons.Action._function_contents(func1)
- assert c in expected[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + "\n" + repr(
- expected[sys.version_info[:2]])
+ assert c in expected[sys.version_info[:2]], f"Got\n{c!r}\nExpected\n" + repr(
+ expected[sys.version_info[:2]]
+ )
def test_object_contents(self):
"""Test that Action._object_contents works"""
@@ -2268,24 +2266,34 @@ class ObjectContentsTestCase(unittest.TestCase):
# c = SCons.Action._object_instance_content(o)
- # Since the python bytecode has per version differences, we need different expected results per version
+ # Since the python bytecode has per version differences,
+ # we need different expected results per version
expected = {
(3, 5): bytearray(
- b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"),
+ b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"
+ ),
(3, 6): bytearray(
- b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"),
+ b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"
+ ),
(3, 7): bytearray(
- b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"),
+ b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"
+ ),
(3, 8): bytearray(
- b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"),
+ b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"
+ ),
(3, 9): bytearray(
- b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"),
+ b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"
+ ),
(3, 10): bytearray(
- b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"),
+ b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"
+ ),
+ (3, 11): bytearray(
+ b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x97\x00d\x01|\x00_\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x02|\x00_\x01\x00\x00\x00\x00\x00\x00\x00\x00d\x00S\x00),(),(),2, 2, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()}}{{{a=a,b=b}}}"),
}
- assert c == expected[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + "\n" + repr(
- expected[sys.version_info[:2]])
+ assert c == expected[sys.version_info[:2]], f"Got\n{c!r}\nExpected\n" + repr(
+ expected[sys.version_info[:2]]
+ )
def test_code_contents(self):
"""Test that Action._code_contents works"""
@@ -2295,25 +2303,43 @@ class ObjectContentsTestCase(unittest.TestCase):
# Since the python bytecode has per version differences, we need different expected results per version
expected = {
- (3, 5): bytearray(b'0, 0, 0, 0,(Hello, World!),(print),(e\x00\x00d\x00\x00\x83\x01\x00\x01d\x01\x00S)'),
- (3, 6): bytearray(b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'),
- (3, 7): bytearray(b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'),
- (3, 8): bytearray(b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'),
- (3, 9): bytearray(b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'),
- (3, 10): bytearray(b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'),
+ (3, 5): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(e\x00\x00d\x00\x00\x83\x01\x00\x01d\x01\x00S)'
+ ),
+ (3, 6): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'
+ ),
+ (3, 7): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'
+ ),
+ (3, 8): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'
+ ),
+ (3, 9): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'
+ ),
+ (3, 10): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'
+ ),
+ (3, 11): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(\x97\x00\x02\x00e\x00d\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00d\x01S\x00)'),
}
- assert c == expected[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + "\n" + repr(expected[
- sys.version_info[:2]])
+ assert c == expected[sys.version_info[:2]], f"Got\n{c!r}\nExpected\n" + repr(
+ expected[sys.version_info[:2]]
+ )
def test_uncaught_exception_bubbles(self):
"""Test that _subproc bubbles uncaught exceptions"""
+
try:
- pobj = SCons.Action._subproc(Environment(),
- None,
- stdin='devnull',
- stderr='devnull',
- stdout=subprocess.PIPE)
+ pobj = SCons.Action._subproc(
+ Environment(),
+ None,
+ stdin='devnull',
+ stderr='devnull',
+ stdout=subprocess.PIPE,
+ )
pobj.wait()
except EnvironmentError:
pass
--
cgit v0.12
From eed3b5845a6a1258cd979d39b3f47b7133053552 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Sun, 5 Jun 2022 18:23:01 -0700
Subject: Added test for invalid env['MSVC_NOTFOUND_POLICY'] value
---
test/MSVC/msvc_badversion.py | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/test/MSVC/msvc_badversion.py b/test/MSVC/msvc_badversion.py
index a36bd2b..65dc789 100644
--- a/test/MSVC/msvc_badversion.py
+++ b/test/MSVC/msvc_badversion.py
@@ -44,25 +44,35 @@ installed_msvc_versions = msvc.get_installed_vcs()
# skip_if_not_msvc() function would have skipped the test
test.write('SConstruct', """\
+DefaultEnvironment(tools=[])
env = Environment(MSVC_VERSION='12.9')
""")
test.run(arguments='-Q -s', stdout='')
test.write('SConstruct', """\
+DefaultEnvironment(tools=[])
env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='ignore')
""")
test.run(arguments='-Q -s', stdout='')
test.write('SConstruct', """\
+DefaultEnvironment(tools=[])
env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='warning')
""")
test.run(arguments='-Q -s', stdout='')
test.write('SConstruct', """\
+DefaultEnvironment(tools=[])
env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='error')
""")
test.run(arguments='-Q -s', status=2, stderr=r"^.*MSVCVersionNotFound.+", match=TestSCons.match_re_dotall)
+test.write('SConstruct', """\
+env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='bad_value')
+""")
+test.run(arguments='-Q -s', status=2, stderr=r"^.* Value specified for MSVC_NOTFOUND_POLICY.+", match=TestSCons.match_re_dotall)
+
+
test.pass_test()
# Local Variables:
--
cgit v0.12
From de86183d3307c32b7b917e8b2dc989921b632ec7 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Sun, 5 Jun 2022 18:29:46 -0700
Subject: Add Blurb for MSVC_NOTFOUND_POLICY
---
SCons/Tool/msvc.xml | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml
index 4a45d26..59a285e 100644
--- a/SCons/Tool/msvc.xml
+++ b/SCons/Tool/msvc.xml
@@ -74,6 +74,7 @@ Sets construction variables for the Microsoft Visual C/C++ compiler.
PCHPCHSTOPPDB
+MSVC_NOTFOUND_POLICY
@@ -578,5 +579,28 @@ and also before &f-link-env-Tool; is called to ininitialize any of those tools:
+
+
+
+Specify how to handle missing MSVC.
+
+
+
+ The MSVC_NOTFOUND_POLICY specifies how &scons; should handle when there is no installed
+ MSVC or if a specific version is requested, that version is not available.
+
+
+
+Valid values for MSVC_NOTFOUND_POLICY are listed here. Note that each group below is equivalent.
+
+
+'error', 'Error', 'ERROR', 'exception', 'Exception', 'EXCEPTION'
+'warning', 'Warning', 'WARNING', 'warn', 'Warn', 'WARN'
+'suppress', 'Suppress', 'SUPPRESS'
+
+
+
+
+
--
cgit v0.12
From 294629587a8cadfa1b6e409d2a4751a7be4880bf Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Mon, 6 Jun 2022 09:55:50 -0400
Subject: Update MSVC_NOTFOUND_POLICY documentation
---
SCons/Tool/msvc.xml | 44 +++++++++++++++++++++++++++++++++++---------
1 file changed, 35 insertions(+), 9 deletions(-)
diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml
index 59a285e..84c0e90 100644
--- a/SCons/Tool/msvc.xml
+++ b/SCons/Tool/msvc.xml
@@ -582,22 +582,48 @@ and also before &f-link-env-Tool; is called to ininitialize any of those tools:
-Specify how to handle missing MSVC.
+Specify the &scons; action to take when an msvc installation is not found.
- The MSVC_NOTFOUND_POLICY specifies how &scons; should handle when there is no installed
- MSVC or if a specific version is requested, that version is not available.
+ The MSVC_NOTFOUND_POLICY specifies the action &scons; should take when there are no
+ msvc versions installed or when the requested msvc version is not installed.
-Valid values for MSVC_NOTFOUND_POLICY are listed here. Note that each group below is equivalent.
+The valid values for MSVC_NOTFOUND_POLICY and the corresponding &scons; action taken are:
-
-'error', 'Error', 'ERROR', 'exception', 'Exception', 'EXCEPTION'
-'warning', 'Warning', 'WARNING', 'warn', 'Warn', 'WARN'
-'suppress', 'Suppress', 'SUPPRESS'
-
+
+
+
+
+'error', 'Error', 'ERROR', 'exception', 'Exception', 'EXCEPTION'
+
+
+Raise an exception when there are no msvc versions installed or when the requested msvc version is not installed.
+
+
+
+
+
+'warning', 'Warning', 'WARNING', 'warn', 'Warn', 'WARN'
+
+
+Issue a warning when there are no msvc versions installed or when the requested msvc version is not installed.
+
+
+
+
+
+'ignore', 'Ignore', 'IGNORE', 'suppress', 'Suppress', 'SUPPRESS'
+
+
+Take no action and continue when there are no msvc versions installed or when the requested msvc version is not installed. Depending on usage, this could result in build failure(s).
+
+
+
+
+
--
cgit v0.12
From 69d6b9acceeda73780592d1b8e09b3aa4f2c3813 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Mon, 6 Jun 2022 10:11:51 -0400
Subject: Update MSVC_NOTFOUND_POLICY documentation
---
SCons/Tool/msvc.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml
index 84c0e90..bb484c5 100644
--- a/SCons/Tool/msvc.xml
+++ b/SCons/Tool/msvc.xml
@@ -582,11 +582,11 @@ and also before &f-link-env-Tool; is called to ininitialize any of those tools:
-Specify the &scons; action to take when an msvc installation is not found.
+Specify the action taken when the Microsoft Visual C/C++ compiler is not found.
- The MSVC_NOTFOUND_POLICY specifies the action &scons; should take when there are no
+ The MSVC_NOTFOUND_POLICY specifies the &scons; action taken when there are no
msvc versions installed or when the requested msvc version is not installed.
--
cgit v0.12
From e7c4d5bb062d979da2a27560db7a658cdef9c262 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Mon, 6 Jun 2022 11:56:37 -0400
Subject: Update MSVC_NOTFOUND_POLICY documentation
---
SCons/Tool/msvc.xml | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml
index bb484c5..187f3b5 100644
--- a/SCons/Tool/msvc.xml
+++ b/SCons/Tool/msvc.xml
@@ -597,7 +597,7 @@ The valid values for MSVC_NOTFOUND_POLICY and the corresponding &
-'error', 'Error', 'ERROR', 'exception', 'Exception', 'EXCEPTION'
+'Error' or 'Exception'
Raise an exception when there are no msvc versions installed or when the requested msvc version is not installed.
@@ -606,25 +606,36 @@ Raise an exception when there are no msvc versions installed or when the request
-'warning', 'Warning', 'WARNING', 'warn', 'Warn', 'WARN'
+'Warning' or 'Warn'
-Issue a warning when there are no msvc versions installed or when the requested msvc version is not installed.
+Issue a warning and continue when there are no msvc versions installed or when the requested msvc version is not installed.
+Depending on usage, this could result in build failure(s).
-'ignore', 'Ignore', 'IGNORE', 'suppress', 'Suppress', 'SUPPRESS'
+'Ignore' or 'Suppress'
-Take no action and continue when there are no msvc versions installed or when the requested msvc version is not installed. Depending on usage, this could result in build failure(s).
+Take no action and continue when there are no msvc versions installed or when the requested msvc version is not installed.
+Depending on usage, this could result in build failure(s).
+
+Note: in addition to the camel case values shown above, lower case and upper case values are accepted as well.
+
+
+
+The default scons action taken when there are no msvc versions installed or when the requested msvc version is
+not installed is to issue a warning and continue. This may change in the future.
+
+
--
cgit v0.12
From 1d3e0e3b0edd4b723d868434c49997215da45fc1 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Mon, 6 Jun 2022 13:00:20 -0400
Subject: Update MSVC_NOTFOUND_POLICY documentation
---
SCons/Tool/msvc.xml | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml
index 187f3b5..59b8944 100644
--- a/SCons/Tool/msvc.xml
+++ b/SCons/Tool/msvc.xml
@@ -582,16 +582,16 @@ and also before &f-link-env-Tool; is called to ininitialize any of those tools:
-Specify the action taken when the Microsoft Visual C/C++ compiler is not found.
+Specify the scons behavior when the Microsoft Visual C/C++ compiler is not detected.
- The MSVC_NOTFOUND_POLICY specifies the &scons; action taken when there are no
- msvc versions installed or when the requested msvc version is not installed.
+ The MSVC_NOTFOUND_POLICY specifies the &scons; behavior when no msvc versions are detected or
+ when the requested msvc version is not detected.
-The valid values for MSVC_NOTFOUND_POLICY and the corresponding &scons; action taken are:
+The valid values for MSVC_NOTFOUND_POLICY and the corresponding &scons; behavior are:
@@ -600,7 +600,7 @@ The valid values for MSVC_NOTFOUND_POLICY and the corresponding &
'Error' or 'Exception'
-Raise an exception when there are no msvc versions installed or when the requested msvc version is not installed.
+Raise an exception when no msvc versions are detected or when the requested msvc version is not detected.
@@ -609,7 +609,7 @@ Raise an exception when there are no msvc versions installed or when the request
'Warning' or 'Warn'
-Issue a warning and continue when there are no msvc versions installed or when the requested msvc version is not installed.
+Issue a warning and continue when no msvc versions are detected or when the requested msvc version is not detected.
Depending on usage, this could result in build failure(s).
@@ -619,7 +619,7 @@ Depending on usage, this could result in build failure(s).
'Ignore' or 'Suppress'
-Take no action and continue when there are no msvc versions installed or when the requested msvc version is not installed.
+Take no action and continue when no msvc versions are detected or when the requested msvc version is not detected.
Depending on usage, this could result in build failure(s).
@@ -632,8 +632,8 @@ Note: in addition to the camel case values shown above, lower case and upper cas
-The default scons action taken when there are no msvc versions installed or when the requested msvc version is
-not installed is to issue a warning and continue. This may change in the future.
+The default scons behavior when no msvc versions are detected or when the requested msvc version is not detected
+is to issue a warning and continue. This may change in the future.
--
cgit v0.12
From bd3cc3b1f0218643a6c8b610bc1328e169f45c6c Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Mon, 6 Jun 2022 16:11:28 -0500
Subject: Updated lex emitter to respect escaped spaces when climbing out of a
the SCosncript dir
---
CHANGES.txt | 1 +
RELEASE.txt | 1 +
SCons/Tool/lex.py | 2 +-
test/LEX/lex_headerfile.py | 44 ++++++++++++++++++++++
test/LEX/lex_headerfile/spaced path/SConstruct | 2 +
test/LEX/lex_headerfile/spaced path/src/SConscript | 10 +++++
test/LEX/lex_headerfile/spaced path/src/lexer.l | 1 +
test/LEX/lex_headerfile/spaced path/src/lexer2.l | 1 +
8 files changed, 61 insertions(+), 1 deletion(-)
create mode 100644 test/LEX/lex_headerfile.py
create mode 100644 test/LEX/lex_headerfile/spaced path/SConstruct
create mode 100644 test/LEX/lex_headerfile/spaced path/src/SConscript
create mode 100644 test/LEX/lex_headerfile/spaced path/src/lexer.l
create mode 100644 test/LEX/lex_headerfile/spaced path/src/lexer2.l
diff --git a/CHANGES.txt b/CHANGES.txt
index 03692d6..efea669 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -116,6 +116,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
Note that these are called for every build command run by SCons. It could have considerable
performance impact if not used carefully.
to connect to the server during start up.
+ - Updated lex emitter to respect escaped spaces when climbing out of a the SCosncript dir
From Mats Wichmann:
- Tweak the way default site_scons paths on Windows are expressed to
diff --git a/RELEASE.txt b/RELEASE.txt
index cfa8afc..a393a36 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -134,6 +134,7 @@ FIXES
- The system environment variable names imported for MSVC 7.0 and 6.0 were updated to be
consistent with the variables names defined by their respective installers. This fixes an
error caused when bypassing MSVC detection by specifying the MSVC 7.0 batch file directly.
+- Updated lex emitter to respect escaped spaces when climbing out of a the SCosncript dir
IMPROVEMENTS
------------
diff --git a/SCons/Tool/lex.py b/SCons/Tool/lex.py
index d8d8de4..c33d0fa 100644
--- a/SCons/Tool/lex.py
+++ b/SCons/Tool/lex.py
@@ -58,7 +58,7 @@ def lexEmitter(target, source, env):
# Different options that are used to trigger the creation of extra files.
fileGenOptions = ["--header-file=", "--tables-file="]
- lexflags = env.subst("$LEXFLAGS", target=target, source=source)
+ lexflags = env.subst_list("$LEXFLAGS", target=target, source=source)
for option in SCons.Util.CLVar(lexflags):
for fileGenOption in fileGenOptions:
l = len(fileGenOption)
diff --git a/test/LEX/lex_headerfile.py b/test/LEX/lex_headerfile.py
new file mode 100644
index 0000000..c2e2e8b
--- /dev/null
+++ b/test/LEX/lex_headerfile.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+#
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""
+Test the headerfile option for lex tool.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.dir_fixture('lex_headerfile')
+
+test.run(chdir='spaced path', arguments='.')
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/LEX/lex_headerfile/spaced path/SConstruct b/test/LEX/lex_headerfile/spaced path/SConstruct
new file mode 100644
index 0000000..aa4aca0
--- /dev/null
+++ b/test/LEX/lex_headerfile/spaced path/SConstruct
@@ -0,0 +1,2 @@
+DefaultEnvironment(tools=[])
+SConscript("src/SConscript")
\ No newline at end of file
diff --git a/test/LEX/lex_headerfile/spaced path/src/SConscript b/test/LEX/lex_headerfile/spaced path/src/SConscript
new file mode 100644
index 0000000..a3f4bfd
--- /dev/null
+++ b/test/LEX/lex_headerfile/spaced path/src/SConscript
@@ -0,0 +1,10 @@
+env = Environment(tools=['lex'])
+
+def make_header_path(env, target, source, for_signature):
+ return target[1]
+
+env.Replace(LEX_HEADER_FILE_GEN=make_header_path)
+env.Append(LEXFLAGS=['--header-file=$LEX_HEADER_FILE_GEN'])
+
+env.CFile(target=['#gen_src/lexer.c', '#gen_src/lexer.l.h'], source='lexer.l')
+env.CFile(target=['#gen_src/lexer2.c', '#gen_src/lexer2.l.h'], source='lexer2.l')
\ No newline at end of file
diff --git a/test/LEX/lex_headerfile/spaced path/src/lexer.l b/test/LEX/lex_headerfile/spaced path/src/lexer.l
new file mode 100644
index 0000000..66b82a4
--- /dev/null
+++ b/test/LEX/lex_headerfile/spaced path/src/lexer.l
@@ -0,0 +1 @@
+%%
\ No newline at end of file
diff --git a/test/LEX/lex_headerfile/spaced path/src/lexer2.l b/test/LEX/lex_headerfile/spaced path/src/lexer2.l
new file mode 100644
index 0000000..66b82a4
--- /dev/null
+++ b/test/LEX/lex_headerfile/spaced path/src/lexer2.l
@@ -0,0 +1 @@
+%%
\ No newline at end of file
--
cgit v0.12
From 0ed5eea9af9358a0bfbeefa42507775947fe2d5f Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Tue, 7 Jun 2022 12:30:50 -0500
Subject: Added option to allow scons to determine if it should skip ninja
regeneration.
---
CHANGES.txt | 2 +
RELEASE.txt | 2 +
SCons/Script/SConsOptions.py | 3 +-
SCons/Tool/ninja/NinjaState.py | 119 ++++++++++++++-------
SCons/Tool/ninja/Utils.py | 25 ++++-
SCons/Tool/ninja/__init__.py | 11 +-
test/ninja/ninja_file_deterministic.py | 105 ++++++++++++++++++
.../sconstruct_ninja_determinism | 20 ++++
8 files changed, 241 insertions(+), 46 deletions(-)
create mode 100644 test/ninja/ninja_file_deterministic.py
create mode 100644 test/ninja/ninja_test_sconscripts/sconstruct_ninja_determinism
diff --git a/CHANGES.txt b/CHANGES.txt
index 03692d6..4541168 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -116,6 +116,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
Note that these are called for every build command run by SCons. It could have considerable
performance impact if not used carefully.
to connect to the server during start up.
+ - Ninja: added option to skip ninja regeneration if scons can determine the ninja file does
+ not need to be regenerated.
From Mats Wichmann:
- Tweak the way default site_scons paths on Windows are expressed to
diff --git a/RELEASE.txt b/RELEASE.txt
index cfa8afc..0dae549 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -88,6 +88,8 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
require delayed expansion to be enabled which is currently not supported and is
typically not enabled by default on the host system. The batch files may also require
environment variables that are not included by default in the msvc environment.
+- Ninja: added option to skip ninja regeneration if scons can determine the ninja file does
+ not need to be regenerated.
FIXES
-----
diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py
index e2631fb..0210b79 100644
--- a/SCons/Script/SConsOptions.py
+++ b/SCons/Script/SConsOptions.py
@@ -151,7 +151,8 @@ class SConsValues(optparse.Values):
# Requested setable flag in : https://github.com/SCons/scons/issues/3983
# From experimental ninja
'disable_execute_ninja',
- 'disable_ninja'
+ 'disable_ninja',
+ 'skip_ninja_regen'
]
def set_option(self, name, value):
diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py
index c168c7f..c75c966 100644
--- a/SCons/Tool/ninja/NinjaState.py
+++ b/SCons/Tool/ninja/NinjaState.py
@@ -29,6 +29,8 @@ import signal
import tempfile
import shutil
import sys
+import random
+import filecmp
from os.path import splitext
from tempfile import NamedTemporaryFile
import ninja
@@ -42,7 +44,7 @@ from .Globals import COMMAND_TYPES, NINJA_RULES, NINJA_POOLS, \
NINJA_CUSTOM_HANDLERS, NINJA_DEFAULT_TARGETS
from .Rules import _install_action_function, _mkdir_action_function, _lib_symlink_action_function, _copy_action_function
from .Utils import get_path, alias_to_ninja_build, generate_depfile, ninja_noop, get_order_only, \
- get_outputs, get_inputs, get_dependencies, get_rule, get_command_env, to_escaped_list
+ get_outputs, get_inputs, get_dependencies, get_rule, get_command_env, to_escaped_list, ninja_sorted_build
from .Methods import get_command
@@ -82,7 +84,26 @@ class NinjaState:
# to make the SCONS_INVOCATION variable properly quoted for things
# like CCFLAGS
scons_escape = env.get("ESCAPE", lambda x: x)
- scons_daemon_port = int(env.get('NINJA_SCONS_DAEMON_PORT',-1))
+
+ # The daemon port should be the same across runs, unless explicitly set
+ # or if the portfile is deleted. This ensures the ninja file is deterministic
+ # across regen's if nothings changed. The construction var should take preference,
+ # then portfile is next, and then otherwise create a new random port to persist in
+ # use.
+ scons_daemon_port = None
+ os.makedirs(get_path(self.env.get("NINJA_DIR")), exist_ok=True)
+ scons_daemon_port_file = str(pathlib.Path(get_path(self.env.get("NINJA_DIR"))) / "scons_daemon_portfile")
+
+ if env.get('NINJA_SCONS_DAEMON_PORT') is not None:
+ scons_daemon_port = int(env.get('NINJA_SCONS_DAEMON_PORT'))
+ elif os.path.exists(scons_daemon_port_file):
+ with open(scons_daemon_port_file) as f:
+ scons_daemon_port = int(f.read())
+ else:
+ scons_daemon_port = random.randint(10000, 60000)
+
+ with open(scons_daemon_port_file, 'w') as f:
+ f.write(str(scons_daemon_port))
# if SCons was invoked from python, we expect the first arg to be the scons.py
# script, otherwise scons was invoked from the scons script
@@ -381,13 +402,13 @@ class NinjaState:
ninja.variable("builddir", get_path(self.env.Dir(self.env['NINJA_DIR']).path))
- for pool_name, size in self.pools.items():
+ for pool_name, size in sorted(self.pools.items()):
ninja.pool(pool_name, min(self.env.get('NINJA_MAX_JOBS', size), size))
- for var, val in self.variables.items():
+ for var, val in sorted(self.variables.items()):
ninja.variable(var, val)
- for rule, kwargs in self.rules.items():
+ for rule, kwargs in sorted(self.rules.items()):
if self.env.get('NINJA_MAX_JOBS') is not None and 'pool' not in kwargs:
kwargs['pool'] = 'local_pool'
ninja.rule(rule, **kwargs)
@@ -529,8 +550,9 @@ class NinjaState:
)
if remaining_outputs:
- ninja.build(
- outputs=sorted(remaining_outputs), rule="phony", implicit=first_output,
+ ninja_sorted_build(
+ ninja,
+ outputs=remaining_outputs, rule="phony", implicit=first_output,
)
build["outputs"] = first_output
@@ -548,12 +570,18 @@ class NinjaState:
if "inputs" in build:
build["inputs"].sort()
- ninja.build(**build)
+ ninja_sorted_build(
+ ninja,
+ **build
+ )
scons_daemon_dirty = str(pathlib.Path(get_path(self.env.get("NINJA_DIR"))) / "scons_daemon_dirty")
for template_builder in template_builders:
template_builder["implicit"] += [scons_daemon_dirty]
- ninja.build(**template_builder)
+ ninja_sorted_build(
+ ninja,
+ **template_builder
+ )
# We have to glob the SCons files here to teach the ninja file
# how to regenerate itself. We'll never see ourselves in the
@@ -563,17 +591,19 @@ class NinjaState:
ninja_file_path = self.env.File(self.ninja_file).path
regenerate_deps = to_escaped_list(self.env, self.env['NINJA_REGENERATE_DEPS'])
- ninja.build(
- ninja_file_path,
+ ninja_sorted_build(
+ ninja,
+ outputs=ninja_file_path,
rule="REGENERATE",
implicit=regenerate_deps,
variables={
- "self": ninja_file_path,
+ "self": ninja_file_path
}
)
- ninja.build(
- regenerate_deps,
+ ninja_sorted_build(
+ ninja,
+ outputs=regenerate_deps,
rule="phony",
variables={
"self": ninja_file_path,
@@ -584,8 +614,9 @@ class NinjaState:
# If we ever change the name/s of the rules that include
# compile commands (i.e. something like CC) we will need to
# update this build to reflect that complete list.
- ninja.build(
- "compile_commands.json",
+ ninja_sorted_build(
+ ninja,
+ outputs="compile_commands.json",
rule="CMD",
pool="console",
implicit=[str(self.ninja_file)],
@@ -601,12 +632,14 @@ class NinjaState:
},
)
- ninja.build(
- "compiledb", rule="phony", implicit=["compile_commands.json"],
+ ninja_sorted_build(
+ ninja,
+ outputs="compiledb", rule="phony", implicit=["compile_commands.json"],
)
- ninja.build(
- ["run_ninja_scons_daemon_phony", scons_daemon_dirty],
+ ninja_sorted_build(
+ ninja,
+ outputs=["run_ninja_scons_daemon_phony", scons_daemon_dirty],
rule="SCONS_DAEMON",
)
@@ -620,39 +653,45 @@ class NinjaState:
if len(all_targets) == 0:
all_targets = ["phony_default"]
- ninja.build(
+ ninja_sorted_build(
+ ninja,
outputs=all_targets,
rule="phony",
)
ninja.default([self.ninja_syntax.escape_path(path) for path in sorted(all_targets)])
- daemon_dir = pathlib.Path(tempfile.gettempdir()) / ('scons_daemon_' + str(hashlib.md5(str(get_path(self.env["NINJA_DIR"])).encode()).hexdigest()))
- pidfile = None
- if os.path.exists(scons_daemon_dirty):
- pidfile = scons_daemon_dirty
- elif os.path.exists(daemon_dir / 'pidfile'):
- pidfile = daemon_dir / 'pidfile'
-
- if pidfile:
- with open(pidfile) as f:
- pid = int(f.readline())
- try:
- os.kill(pid, signal.SIGINT)
- except OSError:
- pass
+ with NamedTemporaryFile(delete=False, mode='w') as temp_ninja_file:
+ temp_ninja_file.write(content.getvalue())
+
+ if self.env.GetOption('skip_ninja_regen') and os.path.exists(ninja_file_path) and filecmp.cmp(temp_ninja_file.name, ninja_file_path):
+ os.unlink(temp_ninja_file.name)
+ else:
+
+ daemon_dir = pathlib.Path(tempfile.gettempdir()) / ('scons_daemon_' + str(hashlib.md5(str(get_path(self.env["NINJA_DIR"])).encode()).hexdigest()))
+ pidfile = None
+ if os.path.exists(scons_daemon_dirty):
+ pidfile = scons_daemon_dirty
+ elif os.path.exists(daemon_dir / 'pidfile'):
+ pidfile = daemon_dir / 'pidfile'
+
+ if pidfile:
+ with open(pidfile) as f:
+ pid = int(f.readline())
+ try:
+ os.kill(pid, signal.SIGINT)
+ except OSError:
+ pass
# wait for the server process to fully killed
# TODO: update wait_for_process_to_die() to handle timeout and then catch exception
# here and do something smart.
wait_for_process_to_die(pid)
- if os.path.exists(scons_daemon_dirty):
- os.unlink(scons_daemon_dirty)
+ if os.path.exists(scons_daemon_dirty):
+ os.unlink(scons_daemon_dirty)
- with NamedTemporaryFile(delete=False, mode='w') as temp_ninja_file:
- temp_ninja_file.write(content.getvalue())
- shutil.move(temp_ninja_file.name, ninja_file_path)
+ shutil.move(temp_ninja_file.name, ninja_file_path)
self.__generated = True
diff --git a/SCons/Tool/ninja/Utils.py b/SCons/Tool/ninja/Utils.py
index b2c3cd3..dec6d2c 100644
--- a/SCons/Tool/ninja/Utils.py
+++ b/SCons/Tool/ninja/Utils.py
@@ -23,6 +23,7 @@
import os
import shutil
from os.path import join as joinpath
+from collections import OrderedDict
import SCons
from SCons.Action import get_default_ENV, _string_from_cmd_list
@@ -52,6 +53,15 @@ def ninja_add_command_line_options():
help='Disable ninja generation and build with scons even if tool is loaded. '+
'Also used by ninja to build targets which only scons can build.')
+ AddOption('--skip-ninja-regen',
+ dest='skip_ninja_regen',
+ metavar='BOOL',
+ action="store_true",
+ default=False,
+ help='Allow scons to skip regeneration of the ninja file and restarting of the daemon. ' +
+ 'Care should be taken in cases where Glob is in use or generated files are used in ' +
+ 'command lines.')
+
def is_valid_dependent_node(node):
"""
@@ -126,7 +136,7 @@ def get_dependencies(node, skip_sources=False):
get_path(src_file(child))
for child in filter_ninja_nodes(node.children())
if child not in node.sources
- ]
+ ]
return [get_path(src_file(child)) for child in filter_ninja_nodes(node.children())]
@@ -261,6 +271,19 @@ def ninja_noop(*_args, **_kwargs):
"""
return None
+def ninja_recursive_sorted_dict(build):
+ sorted_dict = OrderedDict()
+ for key, val in sorted(build.items()):
+ if isinstance(val, dict):
+ sorted_dict[key] = ninja_recursive_sorted_dict(val)
+ else:
+ sorted_dict[key] = val
+ return sorted_dict
+
+
+def ninja_sorted_build(ninja, **build):
+ sorted_dict = ninja_recursive_sorted_dict(build)
+ ninja.build(**sorted_dict)
def get_command_env(env, target, source):
"""
diff --git a/SCons/Tool/ninja/__init__.py b/SCons/Tool/ninja/__init__.py
index 04a7abb..ad75978 100644
--- a/SCons/Tool/ninja/__init__.py
+++ b/SCons/Tool/ninja/__init__.py
@@ -26,7 +26,7 @@
import importlib
import os
-import random
+import traceback
import subprocess
import sys
@@ -66,8 +66,12 @@ def ninja_builder(env, target, source):
print("Generating:", str(target[0]))
generated_build_ninja = target[0].get_abspath()
- NINJA_STATE.generate()
-
+ try:
+ NINJA_STATE.generate()
+ except Exception as e:
+ raise SCons.Errors.BuildError(
+ errstr=f"ERROR: an excetion occurred while generating the ninja file:\n{traceback.format_exc()}",
+ node=target)
if env["PLATFORM"] == "win32":
# TODO: Is this necessary as you set env variable in the ninja build file per target?
# this is not great, its doesn't consider specific
@@ -194,7 +198,6 @@ def generate(env):
env["NINJA_ALIAS_NAME"] = env.get("NINJA_ALIAS_NAME", "generate-ninja")
env['NINJA_DIR'] = env.Dir(env.get("NINJA_DIR", '#/.ninja'))
env["NINJA_SCONS_DAEMON_KEEP_ALIVE"] = env.get("NINJA_SCONS_DAEMON_KEEP_ALIVE", 180000)
- env["NINJA_SCONS_DAEMON_PORT"] = env.get('NINJA_SCONS_DAEMON_PORT', random.randint(10000, 60000))
if GetOption("disable_ninja"):
env.SConsignFile(os.path.join(str(env['NINJA_DIR']),'.ninja.sconsign'))
diff --git a/test/ninja/ninja_file_deterministic.py b/test/ninja/ninja_file_deterministic.py
new file mode 100644
index 0000000..2ac5e1a
--- /dev/null
+++ b/test/ninja/ninja_file_deterministic.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+import os
+import shutil
+import filecmp
+
+import TestSCons
+from TestCmd import IS_WINDOWS
+
+test = TestSCons.TestSCons()
+
+try:
+ import ninja
+except ImportError:
+ test.skip_test("Could not find module in python")
+
+_python_ = TestSCons._python_
+_exe = TestSCons._exe
+
+ninja_bin = os.path.abspath(os.path.join(
+ ninja.BIN_DIR,
+ 'ninja' + _exe))
+
+test.dir_fixture('ninja-fixture')
+
+test.file_fixture('ninja_test_sconscripts/sconstruct_ninja_determinism', 'SConstruct')
+
+# generate simple build
+test.run(stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
+test.must_contain_all(test.stdout(), 'Executing:')
+test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals())
+test.must_exist([test.workpath('out1.txt'), test.workpath('out2.txt')])
+shutil.copyfile(test.workpath('build.ninja'), test.workpath('build.ninja.orig'))
+
+# generate same build again
+test.run(stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja', 'ninja: no work to do.'])
+test.must_contain_all(test.stdout(), 'Executing:')
+test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals())
+test.must_exist([test.workpath('out1.txt'), test.workpath('out2.txt')])
+
+# make sure the ninja file was deterministic
+if not filecmp.cmp(test.workpath('build.ninja'), test.workpath('build.ninja.orig')):
+ test.fail_test()
+
+# clean build and ninja files
+os.unlink(test.workpath('build.ninja.orig'))
+test.run(arguments='-c', stdout=None)
+
+# only generate the ninja file
+test.run(arguments='--disable-execute-ninja', stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
+test.must_not_exist([test.workpath('out1.txt'), test.workpath('out2.txt')])
+
+# run ninja independently
+program = test.workpath('run_ninja_env.bat') if IS_WINDOWS else ninja_bin
+test.run(program=program, stdout=None)
+test.must_exist([test.workpath('out1.txt'), test.workpath('out2.txt')])
+shutil.copyfile(test.workpath('build.ninja'), test.workpath('build.ninja.orig'))
+
+# only generate the ninja file again
+test.run(arguments='--disable-execute-ninja', stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
+test.must_exist([test.workpath('out1.txt'), test.workpath('out2.txt')])
+
+# run ninja independently again
+program = test.workpath('run_ninja_env.bat') if IS_WINDOWS else ninja_bin
+test.run(program=program, stdout=None)
+test.must_contain_all_lines(test.stdout(), ['ninja: no work to do.'])
+test.must_exist([test.workpath('out1.txt'), test.workpath('out2.txt')])
+
+# make sure the ninja file was deterministic
+if not filecmp.cmp(test.workpath('build.ninja'), test.workpath('build.ninja.orig')):
+ test.fail_test()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/ninja/ninja_test_sconscripts/sconstruct_ninja_determinism b/test/ninja/ninja_test_sconscripts/sconstruct_ninja_determinism
new file mode 100644
index 0000000..6bd7c26
--- /dev/null
+++ b/test/ninja/ninja_test_sconscripts/sconstruct_ninja_determinism
@@ -0,0 +1,20 @@
+import SCons
+import random
+SetOption('experimental','ninja')
+SetOption('skip_ninja_regen', True)
+DefaultEnvironment(tools=[])
+
+env = Environment(tools=[])
+
+env.Tool('ninja')
+
+# make the dependency list vary in order. Ninja tool should sort them to be deterministic.
+for i in range(1, 10):
+ node = env.Command(f'out{i}.txt', 'foo.c', 'echo test > $TARGET')
+ deps = list(range(1, i))
+ random.shuffle(deps)
+ for j in deps:
+ if j == i:
+ continue
+ env.Depends(node, f'out{j}.txt')
+
--
cgit v0.12
From 60b6724b28a909f63fa99f970bc39111f039024d Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Tue, 7 Jun 2022 12:35:08 -0500
Subject: improve help text slightly
---
SCons/Tool/ninja/Utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SCons/Tool/ninja/Utils.py b/SCons/Tool/ninja/Utils.py
index dec6d2c..583301e 100644
--- a/SCons/Tool/ninja/Utils.py
+++ b/SCons/Tool/ninja/Utils.py
@@ -59,7 +59,7 @@ def ninja_add_command_line_options():
action="store_true",
default=False,
help='Allow scons to skip regeneration of the ninja file and restarting of the daemon. ' +
- 'Care should be taken in cases where Glob is in use or generated files are used in ' +
+ 'Care should be taken in cases where Glob is in use or SCons generated files are used in ' +
'command lines.')
--
cgit v0.12
From 97a265e6da13a6a3827c599c23271b60aadef176 Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Tue, 7 Jun 2022 14:23:10 -0500
Subject: Added new alias 'shutdown-ninja-scons-daemon' to allow ninja to
shutdown the daemon
---
CHANGES.txt | 2 +
RELEASE.txt | 2 +
SCons/Tool/ninja/NinjaState.py | 11 +++++
SCons/Tool/ninja/__init__.py | 1 +
SCons/Tool/ninja/ninja_daemon_build.py | 14 +++++-
SCons/Tool/ninja/ninja_scons_daemon.py | 5 ++-
test/ninja/shutdown_scons_daemon.py | 79 ++++++++++++++++++++++++++++++++++
testing/framework/TestCmd.py | 32 ++++++++++++++
8 files changed, 144 insertions(+), 2 deletions(-)
create mode 100644 test/ninja/shutdown_scons_daemon.py
diff --git a/CHANGES.txt b/CHANGES.txt
index 03692d6..9db19a8 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -116,6 +116,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
Note that these are called for every build command run by SCons. It could have considerable
performance impact if not used carefully.
to connect to the server during start up.
+ -Ninja: Added new alias "shutdown-ninja-scons-daemon" to allow ninja to shutdown the daemon.
+ Also added cleanup to test framework to kill ninja scons daemons and clean ip daemon logs.
From Mats Wichmann:
- Tweak the way default site_scons paths on Windows are expressed to
diff --git a/RELEASE.txt b/RELEASE.txt
index cfa8afc..811d957 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -24,6 +24,8 @@ NEW FUNCTIONALITY
performance impact if not used carefully.
- Added MSVC_USE_SETTINGS variable to pass a dictionary to configure the msvc compiler
system environment as an alternative to bypassing Visual Studio autodetection entirely.
+- Ninja: Added new alias "shutdown-ninja-scons-daemon" to allow ninja to shutdown the daemon.
+ Also added cleanup to test framework to kill ninja scons daemons and clean ip daemon logs.
DEPRECATED FUNCTIONALITY
diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py
index c168c7f..553d8cc 100644
--- a/SCons/Tool/ninja/NinjaState.py
+++ b/SCons/Tool/ninja/NinjaState.py
@@ -214,6 +214,12 @@ class NinjaState:
"pool": "local_pool",
"restat": 1
},
+ "EXIT_SCONS_DAEMON": {
+ "command": "$PYTHON_BIN $NINJA_TOOL_DIR/ninja_daemon_build.py $PORT $NINJA_DIR_PATH --exit",
+ "description": "Shutting down ninja scons daemon server",
+ "pool": "local_pool",
+ "restat": 1
+ },
"SCONS": {
"command": "$SCONS_INVOCATION $out",
"description": "$SCONS_INVOCATION $out",
@@ -610,6 +616,11 @@ class NinjaState:
rule="SCONS_DAEMON",
)
+ ninja.build(
+ "shutdown_ninja_scons_daemon_phony",
+ rule="EXIT_SCONS_DAEMON",
+ )
+
if all_targets is None:
# Look in SCons's list of DEFAULT_TARGETS, find the ones that
diff --git a/SCons/Tool/ninja/__init__.py b/SCons/Tool/ninja/__init__.py
index 04a7abb..6f474ca 100644
--- a/SCons/Tool/ninja/__init__.py
+++ b/SCons/Tool/ninja/__init__.py
@@ -495,3 +495,4 @@ def generate(env):
env.Alias('run-ninja-scons-daemon', 'run_ninja_scons_daemon_phony')
+ env.Alias('shutdown-ninja-scons-daemon', 'shutdown_ninja_scons_daemon_phony')
\ No newline at end of file
diff --git a/SCons/Tool/ninja/ninja_daemon_build.py b/SCons/Tool/ninja/ninja_daemon_build.py
index 48156f5..63eb136 100644
--- a/SCons/Tool/ninja/ninja_daemon_build.py
+++ b/SCons/Tool/ninja/ninja_daemon_build.py
@@ -60,11 +60,22 @@ def log_error(msg):
while True:
try:
+ if not os.path.exists(daemon_dir / "pidfile"):
+ if sys.argv[3] != '--exit':
+ logging.debug(f"ERROR: Server pid not found {daemon_dir / 'pidfile'} for request {sys.argv[3]}")
+ exit(1)
+ else:
+ logging.debug(f"WARNING: Unecessary request to shutfown server, its already shutdown.")
+ exit(0)
+
logging.debug(f"Sending request: {sys.argv[3]}")
conn = http.client.HTTPConnection(
"127.0.0.1", port=int(sys.argv[1]), timeout=60
)
- conn.request("GET", "/?build=" + sys.argv[3])
+ if sys.argv[3] == '--exit':
+ conn.request("GET", "/?exit=1")
+ else:
+ conn.request("GET", "/?build=" + sys.argv[3])
response = None
while not response:
@@ -81,6 +92,7 @@ while True:
if status != 200:
log_error(msg.decode("utf-8"))
exit(1)
+
logging.debug(f"Request Done: {sys.argv[3]}")
exit(0)
diff --git a/SCons/Tool/ninja/ninja_scons_daemon.py b/SCons/Tool/ninja/ninja_scons_daemon.py
index c4a1d11..35a7789 100644
--- a/SCons/Tool/ninja/ninja_scons_daemon.py
+++ b/SCons/Tool/ninja/ninja_scons_daemon.py
@@ -168,6 +168,7 @@ def daemon_thread_func():
te.start()
daemon_ready = False
+
building_node = None
startup_complete = False
@@ -218,12 +219,14 @@ def daemon_thread_func():
except queue.Empty:
break
if "exit" in building_node:
+ daemon_log("input: " + "exit")
p.stdin.write("exit\n".encode("utf-8"))
p.stdin.flush()
with building_cv:
shared_state.finished_building += [building_node]
daemon_ready = False
- raise
+ daemon_needs_to_shutdown = True
+ break
else:
input_command = "build " + building_node + "\n"
diff --git a/test/ninja/shutdown_scons_daemon.py b/test/ninja/shutdown_scons_daemon.py
new file mode 100644
index 0000000..fffbadb
--- /dev/null
+++ b/test/ninja/shutdown_scons_daemon.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+import os
+import psutil
+from timeit import default_timer as timer
+
+import TestSCons
+from TestCmd import IS_WINDOWS
+
+test = TestSCons.TestSCons()
+
+try:
+ import ninja
+except ImportError:
+ test.skip_test("Could not find module in python")
+
+_python_ = TestSCons._python_
+_exe = TestSCons._exe
+
+ninja_bin = os.path.abspath(
+ os.path.join(ninja.__file__, os.pardir, "data", "bin", "ninja" + _exe)
+)
+
+test.dir_fixture("ninja-fixture")
+
+test.file_fixture(
+ "ninja_test_sconscripts/sconstruct_force_scons_callback", "SConstruct"
+)
+
+test.run(stdout=None)
+pid = None
+test.must_exist(test.workpath('.ninja/scons_daemon_dirty'))
+with open(test.workpath('.ninja/scons_daemon_dirty')) as f:
+ pid = int(f.read())
+ if pid not in [proc.pid for proc in psutil.process_iter()]:
+ test.fail_test(message="daemon not running!")
+
+program = test.workpath("run_ninja_env.bat") if IS_WINDOWS else ninja_bin
+test.run(program=program, arguments='shutdown-ninja-scons-daemon', stdout=None)
+
+wait_time = 10
+start_time = timer()
+while True:
+ if wait_time > (timer() - start_time):
+ if pid not in [proc.pid for proc in psutil.process_iter()]:
+ break
+ else:
+ test.fail_test(message=f"daemon still not shutdown after {wait_time} seconds.")
+
+test.must_not_exist(test.workpath('.ninja/scons_daemon_dirty'))
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py
index 5759121..34acb4d 100644
--- a/testing/framework/TestCmd.py
+++ b/testing/framework/TestCmd.py
@@ -299,9 +299,12 @@ __version__ = "1.3"
import atexit
import difflib
import errno
+import hashlib
import os
import re
+import psutil
import shutil
+import signal
import stat
import subprocess
import sys
@@ -310,6 +313,7 @@ import threading
import time
import traceback
from collections import UserList, UserString
+from pathlib import Path
from subprocess import PIPE, STDOUT
from typing import Optional
@@ -382,6 +386,31 @@ def _caller(tblist, skip):
atfrom = "\tfrom"
return string
+def clean_up_ninja_daemon(self, result_type):
+ if self:
+ for path in Path(self.workdir).rglob('.ninja'):
+ daemon_dir = Path(tempfile.gettempdir()) / (
+ "scons_daemon_" + str(hashlib.md5(str(path.resolve()).encode()).hexdigest())
+ )
+ pidfiles = [daemon_dir / 'pidfile', path / 'scons_daemon_dirty']
+ for pidfile in pidfiles:
+ if pidfile.exists():
+ with open(daemon_dir / 'pidfile') as f:
+ try:
+ pid = int(f.read())
+ os.kill(pid, signal.SIGINT)
+ except OSError:
+ pass
+
+ while True:
+ if pid not in [proc.pid for proc in psutil.process_iter()]:
+ break
+ else:
+ time.sleep(0.1)
+
+ if not self._preserve[result_type]:
+ if daemon_dir.exists():
+ shutil.rmtree(daemon_dir)
def fail_test(self=None, condition=True, function=None, skip=0, message=None):
"""Causes a test to exit with a fail.
@@ -402,6 +431,7 @@ def fail_test(self=None, condition=True, function=None, skip=0, message=None):
return
if function is not None:
function()
+ clean_up_ninja_daemon(self, 'fail_test')
of = ""
desc = ""
sep = " "
@@ -447,6 +477,7 @@ def no_result(self=None, condition=True, function=None, skip=0):
return
if function is not None:
function()
+ clean_up_ninja_daemon(self, 'no_result')
of = ""
desc = ""
sep = " "
@@ -483,6 +514,7 @@ def pass_test(self=None, condition=True, function=None):
return
if function is not None:
function()
+ clean_up_ninja_daemon(self, 'pass_test')
sys.stderr.write("PASSED\n")
sys.exit(0)
--
cgit v0.12
From b1ede4ce01b6163086c748f509c51616b9dec051 Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Tue, 7 Jun 2022 14:28:15 -0500
Subject: fixed sider complaint
---
SCons/Tool/ninja/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SCons/Tool/ninja/__init__.py b/SCons/Tool/ninja/__init__.py
index ad75978..7c32676 100644
--- a/SCons/Tool/ninja/__init__.py
+++ b/SCons/Tool/ninja/__init__.py
@@ -68,7 +68,7 @@ def ninja_builder(env, target, source):
generated_build_ninja = target[0].get_abspath()
try:
NINJA_STATE.generate()
- except Exception as e:
+ except Exception:
raise SCons.Errors.BuildError(
errstr=f"ERROR: an excetion occurred while generating the ninja file:\n{traceback.format_exc()}",
node=target)
--
cgit v0.12
From e3b2dd31e326a0d5b948fa35eae0cb1eefbbebf4 Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Tue, 7 Jun 2022 14:34:28 -0500
Subject: install psutil for testing and fix sider complaints
---
.github/workflows/experimental_tests.yml | 2 +-
.github/workflows/runtest.yml | 2 +-
SCons/Tool/ninja/ninja_daemon_build.py | 2 +-
SCons/Tool/ninja/ninja_scons_daemon.py | 2 +-
testing/framework/TestCmd.py | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/experimental_tests.yml b/.github/workflows/experimental_tests.yml
index 3672144..92fc134 100644
--- a/.github/workflows/experimental_tests.yml
+++ b/.github/workflows/experimental_tests.yml
@@ -44,7 +44,7 @@ jobs:
run: |
python -m pip install --upgrade pip setuptools wheel
python -m pip install ninja
- # if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+ if [ -f requirements.txt ]; then python -m pip install -r requirements.txt; fi
# sudo apt-get update
- name: Test experimental packages ${{ matrix.os }}
run: |
diff --git a/.github/workflows/runtest.yml b/.github/workflows/runtest.yml
index cfc585e..f37111b 100644
--- a/.github/workflows/runtest.yml
+++ b/.github/workflows/runtest.yml
@@ -38,7 +38,7 @@ jobs:
run: |
python -m pip install --upgrade pip setuptools wheel
python -m pip install ninja
- # if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+ if [ -f requirements.txt ]; then python -m pip install -r requirements.txt; fi
# sudo apt-get update
- name: runtest ${{ matrix.os }}
run: |
diff --git a/SCons/Tool/ninja/ninja_daemon_build.py b/SCons/Tool/ninja/ninja_daemon_build.py
index 63eb136..c21ea0b 100644
--- a/SCons/Tool/ninja/ninja_daemon_build.py
+++ b/SCons/Tool/ninja/ninja_daemon_build.py
@@ -65,7 +65,7 @@ while True:
logging.debug(f"ERROR: Server pid not found {daemon_dir / 'pidfile'} for request {sys.argv[3]}")
exit(1)
else:
- logging.debug(f"WARNING: Unecessary request to shutfown server, its already shutdown.")
+ logging.debug("WARNING: Unnecessary request to shutfown server, its already shutdown.")
exit(0)
logging.debug(f"Sending request: {sys.argv[3]}")
diff --git a/SCons/Tool/ninja/ninja_scons_daemon.py b/SCons/Tool/ninja/ninja_scons_daemon.py
index 35a7789..6802af2 100644
--- a/SCons/Tool/ninja/ninja_scons_daemon.py
+++ b/SCons/Tool/ninja/ninja_scons_daemon.py
@@ -225,7 +225,7 @@ def daemon_thread_func():
with building_cv:
shared_state.finished_building += [building_node]
daemon_ready = False
- daemon_needs_to_shutdown = True
+ shared_state.daemon_needs_to_shutdown = True
break
else:
diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py
index 34acb4d..2d3428c 100644
--- a/testing/framework/TestCmd.py
+++ b/testing/framework/TestCmd.py
@@ -399,7 +399,7 @@ def clean_up_ninja_daemon(self, result_type):
try:
pid = int(f.read())
os.kill(pid, signal.SIGINT)
- except OSError:
+ except OSError:
pass
while True:
--
cgit v0.12
From a20c08c1309486f0f51ecd8cf34ba521da0718f0 Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Tue, 7 Jun 2022 14:36:39 -0500
Subject: fix formatting in changes.txt
---
CHANGES.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 9db19a8..a4ef7b2 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -116,7 +116,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
Note that these are called for every build command run by SCons. It could have considerable
performance impact if not used carefully.
to connect to the server during start up.
- -Ninja: Added new alias "shutdown-ninja-scons-daemon" to allow ninja to shutdown the daemon.
+ - Ninja: Added new alias "shutdown-ninja-scons-daemon" to allow ninja to shutdown the daemon.
Also added cleanup to test framework to kill ninja scons daemons and clean ip daemon logs.
From Mats Wichmann:
--
cgit v0.12
From 719ad105165e942ee39f42749ee48d06e7567c90 Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Tue, 7 Jun 2022 15:05:42 -0500
Subject: Added command line variable to pass ninja args through scons.
---
CHANGES.txt | 2 ++
RELEASE.txt | 1 +
SCons/Tool/ninja/__init__.py | 12 +++++++++++-
SCons/Tool/ninja/ninja.xml | 11 ++++++++++-
test/ninja/generate_and_build.py | 3 ++-
5 files changed, 26 insertions(+), 3 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 03692d6..90e2d4e 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -116,6 +116,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
Note that these are called for every build command run by SCons. It could have considerable
performance impact if not used carefully.
to connect to the server during start up.
+ - Ninja: Added command line variable NINJA_CMD_ARGS that allows to pass through ninja command line args.
+
From Mats Wichmann:
- Tweak the way default site_scons paths on Windows are expressed to
diff --git a/RELEASE.txt b/RELEASE.txt
index cfa8afc..699e51e 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -24,6 +24,7 @@ NEW FUNCTIONALITY
performance impact if not used carefully.
- Added MSVC_USE_SETTINGS variable to pass a dictionary to configure the msvc compiler
system environment as an alternative to bypassing Visual Studio autodetection entirely.
+- Ninja: Added command line variable NINJA_CMD_ARGS that allows to pass through ninja command line args.
DEPRECATED FUNCTIONALITY
diff --git a/SCons/Tool/ninja/__init__.py b/SCons/Tool/ninja/__init__.py
index 04a7abb..27a2957 100644
--- a/SCons/Tool/ninja/__init__.py
+++ b/SCons/Tool/ninja/__init__.py
@@ -33,6 +33,7 @@ import sys
import SCons
import SCons.Script
import SCons.Tool.ninja.Globals
+from SCons.Script import Variables
from SCons.Script import GetOption
from .Globals import NINJA_RULES, NINJA_POOLS, NINJA_CUSTOM_HANDLERS, NINJA_DEFAULT_TARGETS, NINJA_CMDLINE_TARGETS
@@ -87,7 +88,7 @@ def ninja_builder(env, target, source):
if str(env.get("NINJA_DISABLE_AUTO_RUN")).lower() not in ['1', 'true']:
num_jobs = env.get('NINJA_MAX_JOBS', env.GetOption("num_jobs"))
- cmd += ['-j' + str(num_jobs)] + NINJA_CMDLINE_TARGETS
+ cmd += ['-j' + str(num_jobs)] + env.get('NINJA_CMD_ARGS', '').split() + NINJA_CMDLINE_TARGETS
print(f"ninja will be run with command line targets: {' '.join(NINJA_CMDLINE_TARGETS)}")
print("Executing:", str(' '.join(cmd)))
@@ -185,6 +186,15 @@ def generate(env):
env["NINJA_DISABLE_AUTO_RUN"] = env.get("NINJA_DISABLE_AUTO_RUN", GetOption('disable_execute_ninja'))
env["NINJA_FILE_NAME"] = env.get("NINJA_FILE_NAME", "build.ninja")
+ if env.get("NINJA_CMD_ARGS") is not None:
+ env["NINJA_CMD_ARGS"] = env.get("NINJA_CMD_ARGS")
+ else:
+ vars = Variables()
+ vars.Add("NINJA_CMD_ARGS")
+ var_env = env.Clone()
+ vars.Update(var_env)
+ env["NINJA_CMD_ARGS"] = var_env.get("NINJA_CMD_ARGS", '')
+
# Add the Ninja builder.
always_exec_ninja_action = AlwaysExecAction(ninja_builder, {})
ninja_builder_obj = SCons.Builder.Builder(action=always_exec_ninja_action,
diff --git a/SCons/Tool/ninja/ninja.xml b/SCons/Tool/ninja/ninja.xml
index 6b247d0..0929684 100644
--- a/SCons/Tool/ninja/ninja.xml
+++ b/SCons/Tool/ninja/ninja.xml
@@ -77,7 +77,7 @@ See its __doc__ string for a discussion of the format.
IMPLICIT_COMMAND_DEPENDENCIESNINJA_SCONS_DAEMON_KEEP_ALIVENINJA_SCONS_DAEMON_PORT
-
+ NINJA_CMD_ARGS
@@ -395,5 +395,14 @@ python -m pip install ninja
+
+
+
+ A string which will pass arguments through SCons to the ninja command when scons executes ninja.
+ Has no effect if &cv-NINJA_DISABLE_AUTO_RUN; is set.
+
+
+
+
diff --git a/test/ninja/generate_and_build.py b/test/ninja/generate_and_build.py
index 91be108..83b7387 100644
--- a/test/ninja/generate_and_build.py
+++ b/test/ninja/generate_and_build.py
@@ -49,10 +49,11 @@ test.dir_fixture('ninja-fixture')
test.file_fixture('ninja_test_sconscripts/sconstruct_generate_and_build', 'SConstruct')
# generate simple build
-test.run(stdout=None)
+test.run(stdout=None, arguments='NINJA_CMD_ARGS=-v')
test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
test.must_contain_all(test.stdout(), 'Executing:')
test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals())
+test.must_contain_all(test.stdout(), ' -j1 -v')
test.run(program=test.workpath('foo' + _exe), stdout="foo.c")
# clean build and ninja files
--
cgit v0.12
From fc6a0e35e7bc1c25aa0359a2391e31ce4ab2dd1f Mon Sep 17 00:00:00 2001
From: Daniel Moody
Date: Tue, 7 Jun 2022 15:10:39 -0500
Subject: fix psutil install
---
.github/workflows/experimental_tests.yml | 3 +--
.github/workflows/runtest.yml | 3 +--
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/experimental_tests.yml b/.github/workflows/experimental_tests.yml
index 92fc134..52078d5 100644
--- a/.github/workflows/experimental_tests.yml
+++ b/.github/workflows/experimental_tests.yml
@@ -43,8 +43,7 @@ jobs:
- name: Install dependencies including ninja ${{ matrix.os }}
run: |
python -m pip install --upgrade pip setuptools wheel
- python -m pip install ninja
- if [ -f requirements.txt ]; then python -m pip install -r requirements.txt; fi
+ python -m pip install ninja psutil
# sudo apt-get update
- name: Test experimental packages ${{ matrix.os }}
run: |
diff --git a/.github/workflows/runtest.yml b/.github/workflows/runtest.yml
index f37111b..dd18013 100644
--- a/.github/workflows/runtest.yml
+++ b/.github/workflows/runtest.yml
@@ -37,8 +37,7 @@ jobs:
- name: Install dependencies including ninja ${{ matrix.os }}
run: |
python -m pip install --upgrade pip setuptools wheel
- python -m pip install ninja
- if [ -f requirements.txt ]; then python -m pip install -r requirements.txt; fi
+ python -m pip install ninja psutil
# sudo apt-get update
- name: runtest ${{ matrix.os }}
run: |
--
cgit v0.12
From e30a6efe2cbf887a501b2a18be854ea0346e299d Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Wed, 8 Jun 2022 12:29:04 -0400
Subject: Update MSVC_NOTFOUND_POLICY documentation
---
SCons/Tool/msvc.xml | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml
index 59b8944..d99bcef 100644
--- a/SCons/Tool/msvc.xml
+++ b/SCons/Tool/msvc.xml
@@ -632,10 +632,28 @@ Note: in addition to the camel case values shown above, lower case and upper cas
-The default scons behavior when no msvc versions are detected or when the requested msvc version is not detected
-is to issue a warning and continue. This may change in the future.
+The MSVC_NOTFOUND_POLICY is enforced if any of the following conditions are satisfied:
+
+&cv-MSVC_VERSION; is specified, the default tools list is implicitly defined (i.e., the tools list is not specified), and the default tools list contains one or more of the msvc tools.
+&cv-MSVC_VERSION; is specified, the default tools list is explicitly specified (e.g., tools=['default']), and the default tools list contains one or more of the msvc tools.
+A non-default tools list is specified that contains one or more of the msvc tools (e.g., tools=['msvc', 'mslink']).
+
+
+The MSVC_NOTFOUND_POLICY is ignored if any of the following conditions are satisfied:
+
+&cv-MSVC_VERSION; is not specified and the default tools list is implicitly defined (i.e., the tools list is not specified).
+&cv-MSVC_VERSION; is not specified and the default tools list is explicitly specified (e.g., tools=['default']).
+A non-default tool list is specified that does not contain any of the msvc tools (e.g., tools=['mingw']).
+
+
+
+
+When MSVC_NOTFOUND_POLICY is not specified, the default &scons; behavior is to issue a warning and continue subject to the enforcement conditions listed above. The default &scons; behavior may change in the future.
+
+
+
--
cgit v0.12
From be45d8d5f77d15bbca6cee1108f2d7dbfe5ef79b Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Wed, 8 Jun 2022 12:44:27 -0400
Subject: Add preliminary docstrings for set_msvc_notfound_policy and
get_msvc_notfound_policy
---
SCons/Tool/MSCommon/vc.py | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index 32d0e61..f0c286a 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -1070,6 +1070,19 @@ def _msvc_notfound_policy_lookup(symbol):
return notfound_policy_def
def set_msvc_notfound_policy(MSVC_NOTFOUND_POLICY=None):
+ """ Set the default policy when MSVC is not found.
+
+ Args:
+ MSVC_NOTFOUND_POLICY:
+ string representing the policy behavior
+ when MSVC is not found or None
+
+ Returns:
+ The previous policy is returned when the MSVC_NOTFOUND_POLICY argument
+ is not None. The active policy is returned when the MSVC_NOTFOUND_POLICY
+ argument is None.
+
+ """
global _MSVC_NOTFOUND_POLICY_DEF
prev_policy = _MSVC_NOTFOUND_POLICY_DEF.symbol
@@ -1087,6 +1100,7 @@ def set_msvc_notfound_policy(MSVC_NOTFOUND_POLICY=None):
return prev_policy
def get_msvc_notfound_policy():
+ """Return the active policy when MSVC is not found."""
debug(
'policy.symbol=%s, policy.value=%s',
repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value)
--
cgit v0.12
From 609b79f538dc025a2b2d4dfd4d8814f96481949e Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Wed, 8 Jun 2022 14:12:59 -0400
Subject: Update MSVC_NOTFOUND_POLICY documentation
---
SCons/Tool/msvc.xml | 33 ++++++++++++++++++++++++---------
1 file changed, 24 insertions(+), 9 deletions(-)
diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml
index d99bcef..e8df128 100644
--- a/SCons/Tool/msvc.xml
+++ b/SCons/Tool/msvc.xml
@@ -632,25 +632,40 @@ Note: in addition to the camel case values shown above, lower case and upper cas
-The MSVC_NOTFOUND_POLICY is enforced if any of the following conditions are satisfied:
+The MSVC_NOTFOUND_POLICY is applied when any of the following conditions are satisfied:
-&cv-MSVC_VERSION; is specified, the default tools list is implicitly defined (i.e., the tools list is not specified), and the default tools list contains one or more of the msvc tools.
-&cv-MSVC_VERSION; is specified, the default tools list is explicitly specified (e.g., tools=['default']), and the default tools list contains one or more of the msvc tools.
-A non-default tools list is specified that contains one or more of the msvc tools (e.g., tools=['msvc', 'mslink']).
+
+&cv-MSVC_VERSION; is specified, the default tools list is implicitly defined (i.e., the tools list is not specified),
+and the default tools list contains one or more of the msvc tools.
+
+
+&cv-MSVC_VERSION; is specified, the default tools list is explicitly specified (e.g., tools=['default']),
+and the default tools list contains one or more of the msvc tools.
+
+
+A non-default tools list is specified that contains one or more of the msvc tools (e.g., tools=['msvc', 'mslink']).
+
-The MSVC_NOTFOUND_POLICY is ignored if any of the following conditions are satisfied:
+The MSVC_NOTFOUND_POLICY is ignored when any of the following conditions are satisfied:
-&cv-MSVC_VERSION; is not specified and the default tools list is implicitly defined (i.e., the tools list is not specified).
-&cv-MSVC_VERSION; is not specified and the default tools list is explicitly specified (e.g., tools=['default']).
-A non-default tool list is specified that does not contain any of the msvc tools (e.g., tools=['mingw']).
+
+&cv-MSVC_VERSION; is not specified and the default tools list is implicitly defined (i.e., the tools list is not specified).
+
+
+&cv-MSVC_VERSION; is not specified and the default tools list is explicitly specified (e.g., tools=['default']).
+
+
+A non-default tool list is specified that does not contain any of the msvc tools (e.g., tools=['mingw']).
+
-When MSVC_NOTFOUND_POLICY is not specified, the default &scons; behavior is to issue a warning and continue subject to the enforcement conditions listed above. The default &scons; behavior may change in the future.
+When MSVC_NOTFOUND_POLICY is not specified, the default &scons; behavior is to issue a warning and continue
+subject to the conditions listed above. The default &scons; behavior may change in the future.
--
cgit v0.12
From 3d9345e2d086f3b39a419452c9b74f763698dd04 Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Thu, 9 Jun 2022 09:03:58 -0600
Subject: doc: FORTRANCOM doesn't include cpp vars [skip appveyor]
FORTRANCOM and SHFORTRANCOM don't add the C preprocessor variables
by default, the docs suggest they do. The PP variants, which run
through the preprocessor, do add these. Adjust docs.
Fixes #2128
Signed-off-by: Mats Wichmann
---
SCons/Tool/fortran.xml | 43 ++++++++++++++++++++++++++-----------------
1 file changed, 26 insertions(+), 17 deletions(-)
diff --git a/SCons/Tool/fortran.xml b/SCons/Tool/fortran.xml
index a4f0cec..4f0517b 100644
--- a/SCons/Tool/fortran.xml
+++ b/SCons/Tool/fortran.xml
@@ -1,6 +1,6 @@
+
- # On the command line
- --experimental=ninja
+# On the command line
+--experimental=ninja
- # Or in your SConstruct
- SetOption('experimental', 'ninja')
+# Or in your SConstruct
+SetOption('experimental', 'ninja')
This functionality is subject to change and/or removal without deprecation cycle.
-
- To use this tool you must install pypi's ninja
- package.
- This can be done via
- pip install ninja
+ To use this tool you need to install the &Python; &ninja; package,
+ as the tool by default depends on being able to do an
+ import of the package
+
+ This can be done via:
+
+python -m pip install ninja
+
diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen
index c982b96..a2d9acd 100644
--- a/doc/generated/functions.gen
+++ b/doc/generated/functions.gen
@@ -63,7 +63,7 @@ Added methods propagate through &f-env-Clone; calls.
-Examples:
+More examples:
@@ -472,7 +472,7 @@ and/or suffix,
so the contents are treated as a list of strings, that is,
adding a string will result in a separate string entry,
not a combined string. For &cv-CPPDEFINES; as well as
-for &cv-link-LIBS;, and the various *PATH
+for &cv-link-LIBS;, and the various *PATH;
variables, &SCons; will supply the compiler-specific
syntax (e.g. adding a -D or /D
prefix for &cv-CPPDEFINES;), so this syntax should be omitted when
@@ -550,7 +550,7 @@ do not make sense and a &Python; exception will be raised.
When using &f-env-Append; to modify &consvars;
which are path specifications (conventionally,
-the names of such end in PATH),
+the names of such end in PATH),
it is recommended to add the values as a list of strings,
even if there is only a single string to add.
The same goes for adding library names to &cv-LIBS;.
@@ -570,26 +570,26 @@ See also &f-link-env-AppendUnique;,
env.AppendENVPath(name, newpath, [envname, sep, delete_existing=False])
-Append new path elements to the given path in the
-specified external environment (&cv-link-ENV; by default).
-This will only add
-any particular path once (leaving the last one it encounters and
-ignoring the rest, to preserve path order),
-and to help assure this,
-will normalize all paths (using
-os.path.normpath
+Append path elements specified by newpath
+to the given search path string or list name
+in mapping envname in the &consenv;.
+Supplying envname is optional:
+the default is the execution environment &cv-link-ENV;.
+Optional sep is used as the search path separator,
+the default is the platform's separator (os.pathsep).
+A path element will only appear once.
+Any duplicates in newpath are dropped,
+keeping the last appearing (to preserve path order).
+If delete_existing
+is False (the default)
+any addition duplicating an existing path element is ignored;
+if delete_existing
+is True the existing value will
+be dropped and the path element will be added at the end.
+To help maintain uniqueness all paths are normalized (using
+os.path.normpath
and
-os.path.normcase).
-This can also handle the
-case where the given old path variable is a list instead of a
-string, in which case a list will be returned instead of a string.
-
-
-
-If
-delete_existing
-is False, then adding a path that already exists
-will not move it to the end; it will stay where it is in the list.
+os.path.normcase).
@@ -608,6 +608,11 @@ print('after:', env['ENV']['INCLUDE'])
before: /foo:/biz
after: /biz:/foo/bar:/foo
+
+
+See also &f-link-env-PrependENVPath;.
+
+
@@ -718,7 +723,7 @@ is being used and
&scons;
finds a derived file that needs to be rebuilt,
it will first look in the cache to see if a
-file with matching build signature exists
+file with matching &buildsig; exists
(indicating the input file(s) and build action(s)
were identical to those for the current target),
and if so, will retrieve the file from the cache.
@@ -730,7 +735,7 @@ If the derived file is not present in the cache,
&scons;
will build it and
then place a copy of the built file in the cache,
-identified by its build signature, for future use.
+identified by its &buildsig;, for future use.
@@ -787,6 +792,13 @@ method can be used to disable caching of specific files. This can be
useful if inputs and/or outputs of some tool are impossible to
predict or prohibitively large.
+
+
+Note that (at this time) &SCons; provides no facilities
+for managing the derived-file cache. It is up to the developer
+to arrange for cache pruning, expiry, etc. if needed.
+
+
@@ -1194,7 +1206,7 @@ was built.
This can be consulted to match various
file characteristics
such as the timestamp,
-size, or content signature.
+size, or &contentsig;.
@@ -1390,10 +1402,11 @@ Find an executable from one or more choices:
progs may be a string or a list of strings.
Returns the first value from progs
that was found, or None.
-Executable is searched by checking the paths specified
-by env['ENV']['PATH'].
+Executable is searched by checking the paths in the execution environment
+(env['ENV']['PATH']).
On Windows systems, additionally applies the filename suffixes found in
-env['ENV']['PATHEXT']
+the execution environment
+(env['ENV']['PATHEXT'])
but will not include any such extension in the return value.
&f-env-Detect; is a wrapper around &f-link-env-WhereIs;.
@@ -2640,12 +2653,8 @@ not as separate arguments to
-By default,
-duplicate values are eliminated;
-you can, however, specify
-unique=False
-to allow duplicate
-values to be added.
+If unique is true (the default),
+duplicate values are not stored.
When eliminating duplicate values,
any &consvars; that end with
the string
@@ -2653,6 +2662,8 @@ the string
keep the left-most unique value.
All other &consvars; keep
the right-most unique value.
+If unique is false,
+values are added even if they are duplicates.
@@ -2669,9 +2680,13 @@ env.MergeFlags(['!pkg-config gtk+-2.0 --cflags', '-O3'])
# Combine an optimization flag with the flags returned from running pkg-config
# twice and merge the result into the construction variables.
-env.MergeFlags(['-O3',
- '!pkg-config gtk+-2.0 --cflags --libs',
- '!pkg-config libpng12 --cflags --libs'])
+env.MergeFlags(
+ [
+ '-O3',
+ '!pkg-config gtk+-2.0 --cflags --libs',
+ '!pkg-config libpng12 --cflags --libs',
+ ]
+)
@@ -2773,15 +2788,13 @@ NoClean(env.Program('hello', 'hello.c'))
env.ParseConfig(command, [function, unique])
Updates the current &consenv; with the values extracted
-from the output from running external command,
-by calling a helper function function
-which understands
-the output of command.
+from the output of running external command,
+by passing it to a helper function.
command may be a string
or a list of strings representing the command and
its arguments.
If function
-is not given,
+is omitted or None,
&f-link-env-MergeFlags; is used.
By default,
duplicate values are not
@@ -2792,33 +2805,32 @@ to allow duplicate values to be added.
-If &f-env-MergeFlags; is used,
-it expects a response in the style of a
-*-config
-command typical of the POSIX programming environment
-(for example,
-gtk-config)
-and adds the options
-to the appropriate construction variables.
-Interpreted options
-and the construction variables they affect
-are as specified for the
-&f-link-env-ParseFlags;
-method (which
-&f-env-MergeFlags; calls).
-See that method's description
-for a table of options and corresponding construction variables.
+command is executed using the
+SCons execution environment (that is, the &consvar;
+&cv-link-ENV; in the current &consenv;).
+If command needs additional information
+to operate properly, that needs to be set in the execution environment.
+For example, pkg-config
+may need a custom value set in the PKG_CONFIG_PATH
+environment variable.
-If &f-env-MergeFlags; cannot interpret the results of
+&f-env-MergeFlags; needs to understand
+the output produced by command
+in order to distribute it to appropriate &consvars;.
+&f-env-MergeFlags; uses a separate function to
+do that processing -
+see &f-link-env-ParseFlags; for the details, including a
+a table of options and corresponding construction variables.
+To provide alternative processing of the output of
command,
you can suppply a custom
-function to do so.
-function
-must accept three arguments:
-the &consenv; to modify, the string returned
-by running command,
+function,
+which must accept three arguments:
+the &consenv; to modify,
+a string argument containing the output from running
+command,
and the optional
unique flag.
@@ -2828,8 +2840,7 @@ and the optional
ParseDepends(filename, [must_exist, only_one])env.ParseDepends(filename, [must_exist, only_one])
-Parses the contents of the specified
-filename
+Parses the contents of filename
as a list of dependencies in the style of
&Make;
or
@@ -2840,27 +2851,21 @@ and explicitly establishes all of the listed dependencies.
By default,
it is not an error
-if the specified
-filename
+if filename
does not exist.
The optional
must_exist
-argument may be set to a non-zero
-value to have
-scons
-throw an exception and
-generate an error if the file does not exist,
+argument may be set to True
+to have &SCons;
+raise an exception if the file does not exist,
or is otherwise inaccessible.
The optional
only_one
-argument may be set to a non-zero
-value to have
-scons
-thrown an exception and
-generate an error
+argument may be set to True
+to have &SCons; raise an exception
if the file contains dependency
information for more than one target.
This can provide a small sanity check
@@ -2876,7 +2881,6 @@ file.
-The
filename
and all of the files listed therein
will be interpreted relative to
@@ -2892,10 +2896,10 @@ function.
env.ParseFlags(flags, ...)
Parses one or more strings containing
-typical command-line flags for GCC tool chains
+typical command-line flags for GCC-style tool chains
and returns a dictionary with the flag values
separated into the appropriate SCons construction variables.
-This is intended as a companion to the
+Intended as a companion to the
&f-link-env-MergeFlags;
method, but allows for the values in the returned dictionary
to be modified, if necessary,
@@ -2910,11 +2914,20 @@ directly unless you want to manipulate the values.)
If the first character in any string is
-an exclamation mark (!),
+an exclamation mark (!),
the rest of the string is executed as a command,
and the output from the command is
parsed as GCC tool chain command-line flags
and added to the resulting dictionary.
+This can be used to call a *-config
+command typical of the POSIX programming environment
+(for example,
+pkg-config).
+Note that such a comamnd is executed using the
+SCons execution environment;
+if the command needs additional information,
+that information needs to be explcitly provided.
+See &f-link-ParseConfig; for more details.
@@ -3051,30 +3064,28 @@ and &f-link-env-PrependUnique;.
- env.PrependENVPath(name, newpath, [envname, sep, delete_existing])
+ env.PrependENVPath(name, newpath, [envname, sep, delete_existing=True])
-Prepend new path elements to the given path in the
-specified external environment (&cv-link-ENV; by default).
-This will only add
-any particular path once (leaving the first one it encounters and
-ignoring the rest, to preserve path order),
-and to help assure this,
-will normalize all paths (using
+Prepend path elements specified by newpath
+to the given search path string or list name
+in mapping envname in the &consenv;.
+Supplying envname is optional:
+the default is the execution environment &cv-link-ENV;.
+Optional sep is used as the search path separator,
+the default is the platform's separator (os.pathsep).
+A path element will only appear once.
+Any duplicates in newpath are dropped,
+keeping the first appearing (to preserve path order).
+If delete_existing
+is False
+any addition duplicating an existing path element is ignored;
+if delete_existing
+is True (the default) the existing value will
+be dropped and the path element will be inserted at the beginning.
+To help maintain uniqueness all paths are normalized (using
os.path.normpath
and
os.path.normcase).
-This can also handle the
-case where the given old path variable is a list instead of a
-string, in which case a list will be returned instead of a string.
-
-
-
-If
-delete_existing
-is False,
-then adding a path that already exists
-will not move it to the beginning;
-it will stay where it is in the list.
@@ -3094,6 +3105,11 @@ print('after:', env['ENV']['INCLUDE'])
before: /biz:/foo
after: /foo/bar:/foo:/biz
+
+
+See also &f-link-env-AppendENVPath;.
+
+
@@ -3103,7 +3119,7 @@ Prepend values to &consvars; in the current &consenv;,
maintaining uniqueness.
Works like &f-link-env-Append; (see for details),
except that values are added to the front,
-rather than the end, of any existing value of the the &consvar;,
+rather than the end, of any existing value of the &consvar;,
and values already present in the &consvar;
will not be added again.
If delete_existing
@@ -3461,40 +3477,36 @@ for a complete explanation of the arguments and behavior.
SConscript(scripts, [exports, variant_dir, duplicate, must_exist])env.SConscript(scripts, [exports, variant_dir, duplicate, must_exist])
- SConscript(dirs=subdirs, [name=script, exports, variant_dir, duplicate, must_exist])
- env.SConscript(dirs=subdirs, [name=script, exports, variant_dir, duplicate, must_exist])
+ SConscript(dirs=subdirs, [name=scriptname, exports, variant_dir, duplicate, must_exist])
+ env.SConscript(dirs=subdirs, [name=scriptname, exports, variant_dir, duplicate, must_exist])
-Execute one or more subsidiary SConscript (configuration) files.
+Executes one or more subsidiary SConscript (configuration) files.
There are two ways to call the
&f-SConscript; function.
-The first calling style
-is to explicitly specify one or more
-scripts
-as the first argument.
+The first calling style is to supply
+one or more SConscript file names
+as the first (positional) argument.
A single script may be specified as a string;
-multiple scripts must be specified as a list
+multiple scripts must be specified as a list of strings
(either explicitly or as created by
a function like
&f-link-Split;).
Examples:
-SConscript('SConscript') # run SConscript in the current directory
+SConscript('SConscript') # run SConscript in the current directory
SConscript('src/SConscript') # run SConscript in the src directory
SConscript(['src/SConscript', 'doc/SConscript'])
config = SConscript('MyConfig.py')
-The second way to call
-&f-SConscript;
-is to specify a list of (sub)directory names
-as a
-dirs=subdirs
-keyword argument.
+The other calling style is to omit the positional argument naming
+scripts and instead specify a list of directory names using the
+dirs keyword argument.
In this case,
&scons;
will
@@ -3504,14 +3516,14 @@ in each of the specified directories.
You may specify a name other than
&SConscript;
by supplying an optional
-name=script
+name=scriptname
keyword argument.
The first three examples below have the same effect
as the first three examples above:
-SConscript(dirs='.') # run SConscript in the current directory
-SConscript(dirs='src') # run SConscript in the src directory
+SConscript(dirs='.') # run SConscript in the current directory
+SConscript(dirs='src') # run SConscript in the src directory
SConscript(dirs=['src', 'doc'])
SConscript(dirs=['sub1', 'sub2'], name='MySConscript')
@@ -3519,8 +3531,12 @@ SConscript(dirs=['sub1', 'sub2'], name='MySConscript')
The optional
exports
-argument provides a string or list of strings representing
+keyword argument provides a string or list of strings representing
variable names, or a dictionary of named values, to export.
+For the first calling style only, a second positional argument
+will be interpreted as exports; the
+second calling style must use the keyword argument form
+for exports.
These variables are locally exported only to the called
SConscript file(s)
and do not affect the global pool of variables managed by the
@@ -3544,38 +3560,24 @@ SConscript(dirs=['one', 'two', 'three'], exports='shared_info')
If the optional
variant_dir
argument is present, it causes an effect equivalent to the
-&f-link-VariantDir; function.
+&f-link-VariantDir; function,
+but in effect only within the scope of the &f-SConscript; call.
The variant_dir
-argument is interpreted relative to the directory of the calling
-SConscript file.
-The optional
-duplicate argument is
-interpreted as for &f-link-VariantDir;.
-If variant_dir
-is omitted, the duplicate argument is ignored.
-See the description of
-&f-link-VariantDir;
-below for additional details and restrictions.
-
-
-
-If
-variant_dir
-is present,
-the source directory is the directory in which the
-SConscript
-file resides and the
-SConscript
+argument is interpreted relative to the directory of the
+calling SConscript file.
+The source directory is the directory in which the
+called SConscript
+file resides and the SConscript
file is evaluated as if it were in the
variant_dir
-directory:
+directory. Thus:
SConscript('src/SConscript', variant_dir='build')
-is equivalent to
+is equivalent to:
@@ -3584,9 +3586,8 @@ SConscript('build/SConscript')
-This later paradigm is often used when the sources are
-in the same directory as the
-&SConstruct;:
+If the sources are in the same directory as the
+&SConstruct;,
@@ -3594,7 +3595,7 @@ SConscript('SConscript', variant_dir='build')
-is equivalent to
+is equivalent to:
@@ -3603,6 +3604,17 @@ SConscript('build/SConscript')
+The optional
+duplicate argument is
+interpreted as for &f-link-VariantDir;.
+If the variant_dir argument
+is omitted, the duplicate argument is ignored.
+See the description of
+&f-link-VariantDir;
+for additional details and restrictions.
+
+
+
$__LDMODULEVERSIONFLAGS">
-$__NINJA_NO">
$__SHLIBVERSIONFLAGS">
$APPLELINK_COMPATIBILITY_VERSION">
$_APPLELINK_COMPATIBILITY_VERSION">
@@ -38,6 +37,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
$CC">
$CCCOM">
$CCCOMSTR">
+$CCDEPFLAGS">
$CCFLAGS">
$CCPCHFLAGS">
$CCPDBFLAGS">
@@ -320,7 +320,10 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
$MSSDK_DIR">
$MSSDK_VERSION">
$MSVC_BATCH">
+$MSVC_NOTFOUND_POLICY">
$MSVC_USE_SCRIPT">
+$MSVC_USE_SCRIPT_ARGS">
+$MSVC_USE_SETTINGS">
$MSVC_UWP_APP">
$MSVC_VERSION">
$MSVS">
@@ -351,17 +354,22 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
$MWCW_VERSIONS">
$NAME">
$NINJA_ALIAS_NAME">
+$NINJA_CMD_ARGS">
$NINJA_COMPDB_EXPAND">
+$NINJA_DEPFILE_PARSE_FORMAT">
$NINJA_DIR">
$NINJA_DISABLE_AUTO_RUN">
$NINJA_ENV_VAR_CACHE">
$NINJA_FILE_NAME">
$NINJA_FORCE_SCONS_BUILD">
+$NINJA_GENERATED_SOURCE_ALIAS_NAME">
$NINJA_GENERATED_SOURCE_SUFFIXES">
$NINJA_MSVC_DEPS_PREFIX">
$NINJA_POOL">
$NINJA_REGENERATE_DEPS">
$_NINJA_REGENERATE_DEPS_FUNC">
+$NINJA_SCONS_DAEMON_KEEP_ALIVE">
+$NINJA_SCONS_DAEMON_PORT">
$NINJA_SYNTAX">
$no_import_lib">
$OBJPREFIX">
@@ -480,6 +488,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
$SHDLINKCOM">
$SHDLINKFLAGS">
$SHELL">
+$SHELL_ENV_GENERATORS">
$SHF03">
$SHF03COM">
$SHF03COMSTR">
@@ -664,7 +673,6 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
-->
$__LDMODULEVERSIONFLAGS">
-$__NINJA_NO">
$__SHLIBVERSIONFLAGS">
$APPLELINK_COMPATIBILITY_VERSION">
$_APPLELINK_COMPATIBILITY_VERSION">
@@ -693,6 +701,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
$CC">
$CCCOM">
$CCCOMSTR">
+$CCDEPFLAGS">
$CCFLAGS">
$CCPCHFLAGS">
$CCPDBFLAGS">
@@ -975,7 +984,10 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
$MSSDK_DIR">
$MSSDK_VERSION">
$MSVC_BATCH">
+$MSVC_NOTFOUND_POLICY">
$MSVC_USE_SCRIPT">
+$MSVC_USE_SCRIPT_ARGS">
+$MSVC_USE_SETTINGS">
$MSVC_UWP_APP">
$MSVC_VERSION">
$MSVS">
@@ -1006,17 +1018,22 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
$MWCW_VERSIONS">
$NAME">
$NINJA_ALIAS_NAME">
+$NINJA_CMD_ARGS">
$NINJA_COMPDB_EXPAND">
+$NINJA_DEPFILE_PARSE_FORMAT">
$NINJA_DIR">
$NINJA_DISABLE_AUTO_RUN">
$NINJA_ENV_VAR_CACHE">
$NINJA_FILE_NAME">
$NINJA_FORCE_SCONS_BUILD">
+$NINJA_GENERATED_SOURCE_ALIAS_NAME">
$NINJA_GENERATED_SOURCE_SUFFIXES">
$NINJA_MSVC_DEPS_PREFIX">
$NINJA_POOL">
$NINJA_REGENERATE_DEPS">
$_NINJA_REGENERATE_DEPS_FUNC">
+$NINJA_SCONS_DAEMON_KEEP_ALIVE">
+$NINJA_SCONS_DAEMON_PORT">
$NINJA_SYNTAX">
$no_import_lib">
$OBJPREFIX">
@@ -1135,6 +1152,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
$SHDLINKCOM">
$SHDLINKFLAGS">
$SHELL">
+$SHELL_ENV_GENERATORS">
$SHF03">
$SHF03COM">
$SHF03COMSTR">
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index a98f4e8..4578d1c 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -5207,7 +5207,7 @@ the dependency graph related to their sources.
An alias is checked for up to date by checking if
its sources are up to date.
An alias is built by making sure its sources have been built,
-and if any building took place,
+and if any building took place,
applying any Actions that are defined as part of the alias.
@@ -7470,7 +7470,7 @@ There are, however, a few portability
issues waiting to trap the unwary.
-.C file suffix
+.C File Suffix&scons; handles the upper-case
.C
@@ -7490,50 +7490,125 @@ suffix as a C source file.
-Fortran file suffixes
+Fortran File Suffixes
-&scons; handles upper-case
-Fortran file suffixes differently
-depending on the capabilities of
-the underlying system.
-On a case-sensitive system
-such as Linux or UNIX,
-&scons; treats a file with a
-.F
-as a Fortran source file
-that is to be first run through
-the standard C preprocessor,
-while the lower-case version is not.
-This matches the convention of gfortran,
-which may also be followed by other Fortran compilers.
-This also applies to other naming variants,
+
+There are several ways source file suffixes impact the
+behavior of &SCons; when working with Fortran language code
+(not all are system-specific, but they are included here
+for completeness).
+
+
+
+As the Fortran language has evolved through multiple
+standards editions, projects might have a need to handle
+files from different language generations differently.
+To this end, &SCons; dispatches to a different compiler
+dialect setup (expressed as a set of &consvars;)
+depending on the file suffix.
+By default, all of these setups start out the same,
+but individual &consvars; can be modified as needed to tune a given dialect.
+Each of these dialacts has a tool specification module
+whose documentation describes the &consvars; associated
+with that dialect: .f
+(as well as .for and .ftn)
+in &t-link-fortran;; (&consvars; start with FORTRAN)
+.f77 in &t-link-f77;;
+(&consvars; start with F77)
+.f90 in &t-link-f90;;
+(&consvars; start with F90)
+.f95 in &t-link-f95;;
+(&consvars; start with F95)
+.f03 in &t-link-f03;;
+(&consvars; start with F03)
+.f08 in &t-link-f08;
+(&consvars; start with F08).
+
+
+
+While &SCons; recognizes multiple internal dialects
+based on filename suffixes,
+the convention of various available Fortran compilers is
+to assign an actual meaning to only two of these suffixes:
+.f
+(as well as .for and .ftn)
+refers to the fixed-format source
+code that was the only available option in FORTRAN 77 and earlier,
+and .f90 refers to free-format source code
+which became available as of the Fortran 90 standard.
+Some compilers recognize suffixes which correspond to Fortran
+specifications later then F90 as equivalent to
+.f90 for this purpose,
+while some do not - check the documentation for your compiler.
+An occasionally suggested policy suggestion is to use only
+.f and .f90
+as Fortran filename suffixes.
+The fixed/free form determination can usually be controlled
+explicitly with compiler flags
+(e.g. for gfortran),
+overriding any assumption that may be made based on the source file suffix.
+
+
+
+The source file suffix does not imply conformance
+with the similarly-named Fortran standard - a suffix of
+.f08 does not mean you are compiling
+specifically for Fortran 2008. Normally, compilers
+provide command-line options for making this selection
+(e.g. for gfortran).
+
+
+
+For dialects from F90 on (including the generic FORTRAN dialect),
+a suffix of .mod is recognized for Fortran modules.
+These files are a side effect of compiling a Fortran
+source file containing module declarations,
+and must be available when other code which declares
+that it uses the module is processed.
+&SCons; does not currently have integrated support for submodules,
+introduced in the Fortran 2008 standard -
+the invoked compiler will produce results,
+but &SCons; will not recognize
+.smod files as tracked objects.
+
+
+
+On a case-sensitive system such as Linux or UNIX,
+a file with a an upper-cased suffix from the set
+.F,
.FOR,
.FTN,
.F90,
.F95,
.F03 and
-.F08;
-files suffixed with
+.F08
+is treated as a Fortran source file
+which shall first be run through
+the standard C preprocessor.
+The lower-cased versions of these suffixes do not
+trigger this behavior.
+On systems which do not distinguish between uppper
+and lower case in filenames,
+this behavior is not available,
+but files suffixed with either
.FPP
-and .fpp
-are both run through the preprocessor,
-as indicated by the pp
-part of the name.
-On a case-insensitive system
-such as Windows,
-&scons; treats a file with a
-.F
-suffix as a Fortran source file that should
-not
-be run through the C preprocessor.
+or .fpp
+are always passed to the preprocessor first.
+This matches the convention of gfortran
+from the GNU Compiler Collection,
+and also followed by certain other Fortran compilers.
+For these two suffixes,
+the generic FORTRAN dialect will be selected.
+
+
-Run through the C preprocessor
-here means that a different set of &consvars; will
-be applied in constructed commands, for example
+&SCons; itself does not invoke the preprocessor,
+that is handled by the compiler,
+but it adds &consvars; which are applicable to the preprocessor run.
+You can see this difference by examining
&cv-link-FORTRANPPCOM; and &cv-link-FORTRANPPCOMSTR;
-instead of
-&cv-link-FORTRANCOM; and &cv-link-FORTRANCOMSTR;.
-See the Fortran-related &consvars; for more details.
+which are used instead of
+&cv-link-FORTRANCOM; and &cv-link-FORTRANCOMSTR; for that dialect.
@@ -7542,10 +7617,10 @@ See the Fortran-related &consvars; for more details.
Cygwin supplies a set of tools and utilities
that let users work on a
-Windows system using a more POSIX-like environment.
-The Cygwin tools, including Cygwin Python,
+Windows system using a POSIX-like environment.
+The Cygwin tools, including Cygwin &Python;,
do this, in part,
-by sharing an ability to interpret UNIX-like path names.
+by sharing an ability to interpret POSIX-style path names.
For example, the Cygwin tools
will internally translate a Cygwin path name
like /cygdrive/c/mydir
@@ -7553,11 +7628,11 @@ to an equivalent Windows pathname
of C:/mydir (equivalent to C:\mydir).
-Versions of Python
+Versions of &Python;
that are built for native Windows execution,
such as the python.org and ActiveState versions,
-do not have the Cygwin path name semantics.
-This means that using a native Windows version of Python
+do not understand the Cygwin path name semantics.
+This means that using a native Windows version of &Python;
to build compiled programs using Cygwin tools
(such as &gcc;, &bison; and &flex;)
may yield unpredictable results.
@@ -7567,14 +7642,22 @@ but it requires careful attention to the use of path names
in your SConscript files.In practice, users can sidestep
-the issue by adopting the following rules:
+the issue by adopting the following guidelines:
When using Cygwin's &gcc; for compiling,
-use the Cygwin-supplied Python interpreter
+use the Cygwin-supplied &Python; interpreter
to run &scons;;
when using Microsoft Visual C/C++
-(or some other Windows compiler)
-use the python.org or Microsoft Store or ActiveState version of Python
-to run &scons;.
+(or some other "native" Windows compiler)
+use the python.org, Microsoft Store, ActiveState or other
+native version of &Python; to run &scons;.
+
+
+
+This discussion largely applies to the msys2 environment
+as well (with the use of the mingw compiler toolchain),
+in particular the recommendation to use the msys2 version of
+&Python; if running &scons; from inside an msys2 shell.
+
@@ -7584,7 +7667,7 @@ to run &scons;.scons.bat batch file,
there are (at least) two ramifications.
Note this is no longer the default - &scons; installed
-via Python's pip installer
+via &Python;''s pip installer
will have an scons.exe which does
not have these limitations:
@@ -7624,7 +7707,7 @@ directory must be in your PATH
environment variable or the
['ENV']['PATH'] &consvar; for &scons;
to detect and use the MinGW tools. When running under the native Windows
-Python interpreter, &scons; will prefer the MinGW tools over the Cygwin
+Python; interpreter, &scons; will prefer the MinGW tools over the Cygwin
tools, if they are both installed, regardless of the order of the bin
directories in the PATH variable.
If you have both MSVC and MinGW
--
cgit v0.12
From 5187917d8a99b86e965ddeb676b0f8fe6c670318 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Thu, 16 Jun 2022 11:41:17 -0400
Subject: Add SDK version support and validate all arguments.
---
SCons/Tool/MSCommon/vc.py | 1637 ++++++++++++++++++++++++++++-----------------
1 file changed, 1010 insertions(+), 627 deletions(-)
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index 0c9fcee..c44c698 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -101,6 +101,17 @@ class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault):
class _Const:
+ BOOLEAN_KEYS = {}
+ BOOLEAN_SYMBOLS = {}
+
+ for bool, symbol_list in [
+ (False, (0, '0', False, 'False', 'FALSE', 'false', 'No', 'NO', 'no', None, '')),
+ (True, (1, '1', True, 'True', 'TRUE', 'true', 'Yes', 'YES', 'yes', )),
+ ]:
+ BOOLEAN_KEYS[bool] = symbol_list
+ for symbol in symbol_list:
+ BOOLEAN_SYMBOLS[symbol] = bool
+
MSVC_RUNTIME_DEFINITION = namedtuple('MSVCRuntime', [
'vc_runtime',
'vc_runtime_numeric',
@@ -1197,7 +1208,8 @@ def reset_installed_vcs():
global __INSTALLED_VCS_RUN
__INSTALLED_VCS_RUN = None
_MSVCSetupEnvDefault.reset()
- _MSVCScriptArguments.reset()
+ _WindowsSDK.reset()
+ _ScriptArguments.reset()
# Running these batch files isn't cheap: most of the time spent in
# msvs.generate() is due to vcvars*.bat. In a build that uses "tools='msvs'"
@@ -1622,287 +1634,543 @@ def msvc_setup_env_once(env, tool=None):
" Requested tool(s) are: {}".format(req_tools)
_msvc_notfound_policy_handler(env, msg)
-class _MSVCScriptArguments:
-
- # Force -vcvars_ver argument for default toolset
- MSVC_TOOLSET_DEFAULT_VCVARSVER = False
+def msvc_find_valid_batch_script(env, version):
+ """Find and execute appropriate batch script to set up build env.
- # MSVC batch file arguments:
- #
- # VS2022: UWP, SDK, TOOLSET, SPECTRE
- # VS2019: UWP, SDK, TOOLSET, SPECTRE
- # VS2017: UWP, SDK, TOOLSET, SPECTRE
- # VS2015: UWP, SDK
- #
- # MSVC_SCRIPT_ARGS: VS2015+
- #
- # MSVC_UWP_APP: VS2015+
- # MSVC_SDK_VERSION: VS2015+
- # MSVC_TOOLSET_VERSION: VS2017+
- # MSVC_SPECTRE_LIBS: VS2017+
+ The MSVC build environment depends heavily on having the shell
+ environment set. SCons does not inherit that, and does not count
+ on that being set up correctly anyway, so it tries to find the right
+ MSVC batch script, or the right arguments to the generic batch script
+ vcvarsall.bat, and run that, so we have a valid environment to build in.
+ There are dragons here: the batch scripts don't fail (see comments
+ elsewhere), they just leave you with a bad setup, so try hard to
+ get it right.
+ """
- @enum.unique
- class SortOrder(enum.IntEnum):
- ARCH = 0 # arch
- UWP = 1 # MSVC_UWP_APP
- SDK = 2 # MSVC_SDK_VERSION
- TOOLSET = 3 # MSVC_TOOLSET_VERSION
- SPECTRE = 4 # MSVC_SPECTRE_LIBS
- USER = 5 # MSVC_SCRIPT_ARGS
+ # Find the host, target, and all candidate (host, target) platform combinations:
+ platforms = get_host_target(env, version)
+ debug("host_platform %s, target_platform %s host_target_list %s", *platforms)
+ host_platform, target_platform, host_target_list = platforms
- VS2019 = _Const.MSVS_VERSION_INTERNAL['2019']
- VS2017 = _Const.MSVS_VERSION_INTERNAL['2017']
- VS2015 = _Const.MSVS_VERSION_INTERNAL['2015']
+ d = None
+ version_installed = False
+ for host_arch, target_arch, in host_target_list:
+ # Set to current arch.
+ env['TARGET_ARCH'] = target_arch
+ arg = ''
- MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [
- 'version', # fully qualified msvc version (e.g., '14.1Exp')
- 'vs_def',
- ])
+ # Try to locate a batch file for this host/target platform combo
+ try:
+ (vc_script, arg, vc_dir, sdk_script) = find_batch_file(env, version, host_arch, target_arch)
+ debug('vc_script:%s vc_script_arg:%s sdk_script:%s', vc_script, arg, sdk_script)
+ version_installed = True
+ except VisualCException as e:
+ msg = str(e)
+ debug('Caught exception while looking for batch file (%s)', msg)
+ version_installed = False
+ continue
- @classmethod
- def _msvc_version(cls, version):
+ # Try to use the located batch file for this host/target platform combo
+ debug('use_script 2 %s, args:%s', repr(vc_script), arg)
+ found = None
+ if vc_script:
+ arg = _ScriptArguments.msvc_script_arguments(env, version, vc_dir, arg)
+ try:
+ d = script_env(vc_script, args=arg)
+ found = vc_script
+ except BatchFileExecutionError as e:
+ debug('use_script 3: failed running VC script %s: %s: Error:%s', repr(vc_script), arg, e)
+ vc_script=None
+ continue
+ if not vc_script and sdk_script:
+ debug('use_script 4: trying sdk script: %s', sdk_script)
+ try:
+ d = script_env(sdk_script)
+ found = sdk_script
+ except BatchFileExecutionError as e:
+ debug('use_script 5: failed running SDK script %s: Error:%s', repr(sdk_script), e)
+ continue
+ elif not vc_script and not sdk_script:
+ debug('use_script 6: Neither VC script nor SDK script found')
+ continue
- verstr = get_msvc_version_numeric(version)
- vs_def = _Const.MSVC_VERSION_INTERNAL[verstr]
+ debug("Found a working script/target: %s/%s", repr(found), arg)
+ break # We've found a working target_platform, so stop looking
- version_args = cls.MSVC_VERSION_ARGS_DEFINITION(
- version = version,
- vs_def = vs_def,
- )
+ # If we cannot find a viable installed compiler, reset the TARGET_ARCH
+ # To it's initial value
+ if not d:
+ env['TARGET_ARCH'] = target_platform
- return version_args
+ if version_installed:
+ msg = "MSVC version '{}' working host/target script was not found.\n" \
+ " Host = '{}', Target = '{}'\n" \
+ " Visual Studio C/C++ compilers may not be set correctly".format(
+ version, host_platform, target_platform
+ )
+ else:
+ installed_vcs = get_installed_vcs(env)
+ if installed_vcs:
+ msg = "MSVC version '{}' was not found.\n" \
+ " Visual Studio C/C++ compilers may not be set correctly.\n" \
+ " Installed versions are: {}".format(version, installed_vcs)
+ else:
+ msg = "MSVC version '{}' was not found.\n" \
+ " No versions of the MSVC compiler were found.\n" \
+ " Visual Studio C/C++ compilers may not be set correctly".format(version)
- @classmethod
- def _msvc_script_argument_uwp(cls, env, msvc, arglist):
+ _msvc_notfound_policy_handler(env, msg)
- uwp_app = env['MSVC_UWP_APP']
- debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app))
+ return d
- if not uwp_app:
- return None
+_undefined = None
- if uwp_app not in (True, '1'):
- return None
+def get_use_script_use_settings(env):
+ global _undefined
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric:
- debug(
- 'invalid: msvc version constraint: %s < %s VS2015',
- repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
- repr(cls.VS2015.vc_buildtools_def.vc_version_numeric)
- )
- err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
- repr(uwp_app), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version)
- )
- raise MSVCArgumentError(err_msg)
+ if _undefined is None:
+ _undefined = object()
- # VS2017+ rewrites uwp => store for 14.0 toolset
- uwp_arg = msvc.vs_def.vc_uwp
+ # use_script use_settings return values action
+ # value ignored (value, None) use script or bypass detection
+ # undefined value not None (False, value) use dictionary
+ # undefined undefined/None (True, None) msvc detection
- # uwp may not be installed
- argpair = (cls.SortOrder.UWP, uwp_arg)
- arglist.append(argpair)
+ # None (documentation) or evaluates False (code): bypass detection
+ # need to distinguish between undefined and None
+ use_script = env.get('MSVC_USE_SCRIPT', _undefined)
- return uwp_arg
+ if use_script != _undefined:
+ # use_script defined, use_settings ignored (not type checked)
+ return (use_script, None)
- # TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last?
- re_sdk_version_10 = re.compile(r'^10[.][0-9][.][0-9]{5}[.][0-9]{1}$')
+ # undefined or None: use_settings ignored
+ use_settings = env.get('MSVC_USE_SETTINGS', None)
- @classmethod
- def _msvc_script_argument_sdk_constraints(cls, msvc, sdk_version):
+ if use_settings is not None:
+ # use script undefined, use_settings defined and not None (type checked)
+ return (False, use_settings)
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric:
- debug(
- 'invalid: msvc_version constraint: %s < %s VS2015',
- repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
- repr(cls.VS2015.vc_buildtools_def.vc_version_numeric)
- )
- err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
- repr(sdk_version), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version)
- )
- return err_msg
+ # use script undefined, use_settings undefined or None
+ return (True, None)
- # TODO: check sdk against vs_def/vc_buildtools_def
+def msvc_setup_env(env):
+ debug('called')
+ version = get_default_version(env)
+ if version is None:
+ if not msvc_setup_env_user(env):
+ _MSVCSetupEnvDefault.set_nodefault()
+ return None
- if sdk_version == '8.1':
- debug('valid: sdk_version=%s', repr(sdk_version))
- return None
+ # XXX: we set-up both MSVS version for backward
+ # compatibility with the msvs tool
+ env['MSVC_VERSION'] = version
+ env['MSVS_VERSION'] = version
+ env['MSVS'] = {}
- if cls.re_sdk_version_10.match(sdk_version):
- debug('valid: sdk_version=%s', repr(sdk_version))
- return None
+ use_script, use_settings = get_use_script_use_settings(env)
+ if SCons.Util.is_String(use_script):
+ use_script = use_script.strip()
+ if not os.path.exists(use_script):
+ raise MSVCScriptNotFound('Script specified by MSVC_USE_SCRIPT not found: "{}"'.format(use_script))
+ args = env.subst('$MSVC_USE_SCRIPT_ARGS')
+ debug('use_script 1 %s %s', repr(use_script), repr(args))
+ d = script_env(use_script, args)
+ elif use_script:
+ d = msvc_find_valid_batch_script(env,version)
+ debug('use_script 2 %s', d)
+ if not d:
+ return d
+ elif use_settings is not None:
+ if not SCons.Util.is_Dict(use_settings):
+ error_msg = 'MSVC_USE_SETTINGS type error: expected a dictionary, found {}'.format(type(use_settings).__name__)
+ raise MSVCUseSettingsError(error_msg)
+ d = use_settings
+ debug('use_settings %s', d)
+ else:
+ debug('MSVC_USE_SCRIPT set to False')
+ warn_msg = "MSVC_USE_SCRIPT set to False, assuming environment " \
+ "set correctly."
+ SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
+ return None
- debug('invalid: method exit: sdk_version=%s', repr(sdk_version))
- err_msg = "MSVC_SDK_VERSION ({}) is not supported".format(repr(sdk_version))
- return err_msg
+ for k, v in d.items():
+ env.PrependENVPath(k, v, delete_existing=True)
+ debug("env['ENV']['%s'] = %s", k, env['ENV'][k])
- @classmethod
- def _msvc_script_argument_sdk(cls, env, msvc, is_uwp, arglist):
+ # final check to issue a warning if the compiler is not present
+ if not find_program_path(env, 'cl'):
+ debug("did not find %s", _CL_EXE_NAME)
+ if CONFIG_CACHE:
+ propose = "SCONS_CACHE_MSVC_CONFIG caching enabled, remove cache file {} if out of date.".format(CONFIG_CACHE)
+ else:
+ propose = "It may need to be installed separately with Visual Studio."
+ warn_msg = "Could not find MSVC compiler 'cl'. {}".format(propose)
+ SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
- sdk_version = env['MSVC_SDK_VERSION']
- debug(
- 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, uwp=%s',
- repr(msvc.version), repr(sdk_version), repr(is_uwp)
- )
+def msvc_exists(env=None, version=None):
+ debug('version=%s', repr(version))
+ vcs = get_installed_vcs(env)
+ if version is None:
+ rval = len(vcs) > 0
+ else:
+ rval = version in vcs
+ debug('version=%s, return=%s', repr(version), rval)
+ return rval
- if not sdk_version:
- return None
+def msvc_setup_env_user(env=None):
+ rval = False
+ if env:
- err_msg = cls._msvc_script_argument_sdk_constraints(msvc, sdk_version)
- if err_msg:
- raise MSVCArgumentError(err_msg)
+ # Intent is to use msvc tools:
+ # MSVC_VERSION or MSVS_VERSION: defined and is True
+ # MSVC_USE_SCRIPT: defined and (is string or is False)
+ # MSVC_USE_SETTINGS: defined and is not None
- # sdk folder may not exist
- argpair = (cls.SortOrder.SDK, sdk_version)
- arglist.append(argpair)
+ # defined and is True
+ for key in ['MSVC_VERSION', 'MSVS_VERSION']:
+ if key in env and env[key]:
+ rval = True
+ debug('key=%s, return=%s', repr(key), rval)
+ return rval
- return sdk_version
+ # defined and (is string or is False)
+ for key in ['MSVC_USE_SCRIPT']:
+ if key in env and (SCons.Util.is_String(env[key]) or not env[key]):
+ rval = True
+ debug('key=%s, return=%s', repr(key), rval)
+ return rval
- @classmethod
- def _msvc_read_toolset_file(cls, msvc, filename):
- toolset_version = None
+ # defined and is not None
+ for key in ['MSVC_USE_SETTINGS']:
+ if key in env and env[key] is not None:
+ rval = True
+ debug('key=%s, return=%s', repr(key), rval)
+ return rval
+
+ debug('return=%s', rval)
+ return rval
+
+def msvc_setup_env_tool(env=None, version=None, tool=None):
+ debug('tool=%s, version=%s', repr(tool), repr(version))
+ _MSVCSetupEnvDefault.register_tool(env, tool)
+ rval = False
+ if not rval and msvc_exists(env, version):
+ rval = True
+ if not rval and msvc_setup_env_user(env):
+ rval = True
+ debug('tool=%s, version=%s, return=%s', repr(tool), repr(version), rval)
+ return rval
+
+class _Util:
+
+ @staticmethod
+ def listdir_dirs(p):
+ dirs = []
+ for dir_name in os.listdir(p):
+ dir_path = os.path.join(p, dir_name)
+ if os.path.isdir(dir_path):
+ dirs.append((dir_name, dir_path))
+ return dirs
+
+ @staticmethod
+ def process_path(p):
+ if p:
+ p = os.path.normpath(p)
+ p = os.path.realpath(p)
+ p = os.path.normcase(p)
+ return p
+
+class _Registry:
+
+ def read_value(hkey, subkey_valname):
try:
- with open(filename) as f:
- toolset_version = f.readlines()[0].strip()
- debug(
- 'msvc_version=%s, filename=%s, toolset_version=%s',
- repr(msvc.version), repr(filename), repr(toolset_version)
- )
+ rval = common.read_reg(subkey_valname, hkroot=hkey)
except OSError:
- debug('OSError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename))
+ debug('OSError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname))
+ return None
except IndexError:
- debug('IndexError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename))
- return toolset_version
+ debug('IndexError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname))
+ return None
+ debug('hkey=%s, subkey=%s, rval=%s', repr(hkey), repr(subkey_valname), repr(rval))
+ return rval
@classmethod
- def _msvc_read_toolset_folders(cls, msvc, vc_dir):
+ def registry_query_path(cls, key, val, suffix):
+ extval = val + '\\' + suffix if suffix else val
+ qpath = cls.read_value(key, extval)
+ if qpath and os.path.exists(qpath):
+ qpath = _Util.process_path(qpath)
+ else:
+ qpath = None
+ return (qpath, key, val, extval)
+
+ REG_SOFTWARE_MICROSOFT = [
+ (SCons.Util.HKEY_LOCAL_MACHINE, r'Software\Wow6432Node\Microsoft'),
+ (SCons.Util.HKEY_CURRENT_USER, r'Software\Wow6432Node\Microsoft'), # SDK queries
+ (SCons.Util.HKEY_LOCAL_MACHINE, r'Software\Microsoft'),
+ (SCons.Util.HKEY_CURRENT_USER, r'Software\Microsoft'),
+ ]
- toolsets_sxs = {}
- toolsets_full = []
+ @classmethod
+ def microsoft_query_paths(cls, suffix, usrval=None):
+ paths = []
+ records = []
+ for key, val in cls.REG_SOFTWARE_MICROSOFT:
+ extval = val + '\\' + suffix if suffix else val
+ qpath = cls.read_value(key, extval)
+ if qpath and os.path.exists(qpath):
+ qpath = _Util.process_path(qpath)
+ if qpath not in paths:
+ paths.append(qpath)
+ records.append((qpath, key, val, extval, usrval))
+ return records
- build_dir = os.path.join(vc_dir, "Auxiliary", "Build")
- sxs_toolsets = [f.name for f in os.scandir(build_dir) if f.is_dir()]
- for sxs_toolset in sxs_toolsets:
- filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset)
- filepath = os.path.join(build_dir, sxs_toolset, filename)
- debug('sxs toolset: check file=%s', repr(filepath))
- if os.path.exists(filepath):
- toolset_version = cls._msvc_read_toolset_file(msvc, filepath)
- if not toolset_version:
- continue
- toolsets_sxs[sxs_toolset] = toolset_version
- debug(
- 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s',
- repr(msvc.version), repr(sxs_toolset), toolset_version
- )
+ @classmethod
+ def microsoft_query_keys(cls, suffix, usrval=None):
+ records = []
+ for key, val in cls.REG_SOFTWARE_MICROSOFT:
+ extval = val + '\\' + suffix if suffix else val
+ rval = cls.read_value(key, extval)
+ if rval:
+ records.append((key, val, extval, usrval))
+ return records
- toolset_dir = os.path.join(vc_dir, "Tools", "MSVC")
- toolsets = [f.name for f in os.scandir(toolset_dir) if f.is_dir()]
- for toolset_version in toolsets:
- binpath = os.path.join(toolset_dir, toolset_version, "bin")
- debug('toolset: check binpath=%s', repr(binpath))
- if os.path.exists(binpath):
- toolsets_full.append(toolset_version)
- debug(
- 'toolset: msvc_version=%s, toolset_version=%s',
- repr(msvc.version), repr(toolset_version)
- )
+ @classmethod
+ def microsoft_sdks(cls, version):
+ return '\\'.join([r'Microsoft SDKs\Windows', 'v' + version, r'InstallationFolder'])
- toolsets_full.sort(reverse=True)
- debug('msvc_version=%s, toolsets=%s', repr(msvc.version), repr(toolsets_full))
+ @classmethod
+ def sdk_query_paths(cls, version):
+ q = cls.microsoft_sdks(version)
+ return cls.microsoft_query_paths(q)
- return toolsets_sxs, toolsets_full
+ @classmethod
+ def windows_kits(cls, version):
+ return r'Windows Kits\Installed Roots\KitsRoot' + version
@classmethod
- def _msvc_read_toolset_default(cls, msvc, vc_dir):
+ def windows_kit_query_paths(cls, version):
+ q = cls.windows_kits(version)
+ return cls.microsoft_query_paths(q)
- build_dir = os.path.join(vc_dir, "Auxiliary", "Build")
+class _WindowsSDK:
- # VS2019+
- filename = "Microsoft.VCToolsVersion.{}.default.txt".format(msvc.vs_def.vc_buildtools_def.vc_buildtools)
- filepath = os.path.join(build_dir, filename)
+ sdk_map_cache = {}
+ sdk_cache = {}
- debug('default toolset: check file=%s', repr(filepath))
- toolset_buildtools = None
- if os.path.exists(filepath):
- toolset_buildtools = cls._msvc_read_toolset_file(msvc, filepath)
- if toolset_buildtools:
- return toolset_buildtools
+ @classmethod
+ def reset(cls):
+ cls.sdk_map_cache = {}
+ cls.sdk_cache = {}
- # VS2017+
- filename = "Microsoft.VCToolsVersion.default.txt"
- filepath = os.path.join(build_dir, filename)
+ @classmethod
+ def _new_sdk_map(cls):
+ sdk_map = {
+ 'desktop': [],
+ 'uwp': [],
+ }
+ return sdk_map
- debug('default toolset: check file=%s', repr(filepath))
- toolset_default = None
- if os.path.exists(filepath):
- toolset_default = cls._msvc_read_toolset_file(msvc, filepath)
- if toolset_default:
- return toolset_default
+ @classmethod
+ def _sdk_10_layout(cls, version):
- return None
+ folder_prefix = version + '.'
+
+ sdk_map = cls._new_sdk_map()
+
+ sdk_roots = _Registry.sdk_query_paths(version)
+
+ sdk_version_platform_seen = set()
+ sdk_roots_seen = set()
+
+ for sdk_t in sdk_roots:
+
+ sdk_root = sdk_t[0]
+ if sdk_root in sdk_roots_seen:
+ continue
+ sdk_roots_seen.add(sdk_root)
+
+ if not os.path.exists(sdk_root):
+ continue
+
+ sdk_include_path = os.path.join(sdk_root, 'include')
+ if not os.path.exists(sdk_include_path):
+ continue
+
+ for version_nbr, version_nbr_path in _Util.listdir_dirs(sdk_include_path):
+
+ if not version_nbr.startswith(folder_prefix):
+ continue
+
+ sdk_inc_path = _Util.process_path(os.path.join(version_nbr_path, 'um'))
+ if not os.path.exists(sdk_inc_path):
+ continue
+
+ for platform, sdk_inc_file in [
+ ('desktop', 'winsdkver.h'),
+ ('uwp', 'windows.h'),
+ ]:
+
+ if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)):
+ continue
+
+ key = (version_nbr, platform)
+ if key in sdk_version_platform_seen:
+ continue
+ sdk_version_platform_seen.add(key)
+
+ sdk_map[platform].append(version_nbr)
+
+ for key, val in sdk_map.items():
+ val.sort(reverse=True)
+
+ return sdk_map
@classmethod
- def _reset_toolsets(cls):
- debug('reset: toolset cache')
- cls._toolset_version_cache = {}
- cls._toolset_default_cache = {}
+ def _sdk_81_layout(cls, version):
- _toolset_version_cache = {}
- _toolset_default_cache = {}
+ version_nbr = version
+
+ sdk_map = cls._new_sdk_map()
+
+ sdk_roots = _Registry.sdk_query_paths(version)
+
+ sdk_version_platform_seen = set()
+ sdk_roots_seen = set()
+
+ sdk_targets = []
+
+ for sdk_t in sdk_roots:
+
+ sdk_root = sdk_t[0]
+ if sdk_root in sdk_roots_seen:
+ continue
+ sdk_roots_seen.add(sdk_root)
+
+ # msvc does not check for existence of root or other files
+
+ sdk_inc_path = _Util.process_path(os.path.join(sdk_root, r'include\um'))
+ if not os.path.exists(sdk_inc_path):
+ continue
+
+ for platform, sdk_inc_file in [
+ ('desktop', 'winsdkver.h'),
+ ('uwp', 'windows.h'),
+ ]:
+
+ if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)):
+ continue
+
+ key = (version_nbr, platform)
+ if key in sdk_version_platform_seen:
+ continue
+ sdk_version_platform_seen.add(key)
+
+ sdk_map[platform].append(version_nbr)
+
+ for key, val in sdk_map.items():
+ val.sort(reverse=True)
+
+ return sdk_map
@classmethod
- def _msvc_version_toolsets(cls, msvc, vc_dir):
+ def _sdk_10(cls, key, reg_version):
+ if key in cls.sdk_map_cache:
+ sdk_map = cls.sdk_map_cache[key]
+ else:
+ sdk_map = cls._sdk_10_layout(reg_version)
+ cls.sdk_map_cache[key] = sdk_map
+ return sdk_map
- if msvc.version in cls._toolset_version_cache:
- toolsets_sxs, toolsets_full = cls._toolset_version_cache[msvc.version]
+ @classmethod
+ def _sdk_81(cls, key, reg_version):
+ if key in cls.sdk_map_cache:
+ sdk_map = cls.sdk_map_cache[key]
else:
- toolsets_sxs, toolsets_full = cls._msvc_read_toolset_folders(msvc, vc_dir)
- cls._toolset_version_cache[msvc.version] = toolsets_sxs, toolsets_full
+ sdk_map = cls._sdk_81_layout(reg_version)
+ cls.sdk_map_cache[key] = sdk_map
+ return sdk_map
- return toolsets_sxs, toolsets_full
+ @classmethod
+ def _combine_sdk_map_list(cls, sdk_map_list):
+ combined_sdk_map = cls._new_sdk_map()
+ for sdk_map in sdk_map_list:
+ for key, val in sdk_map.items():
+ combined_sdk_map[key].extend(val)
+ return combined_sdk_map
+
+ sdk_dispatch_map = None
@classmethod
- def _msvc_default_toolset(cls, msvc, vc_dir):
+ def _version_list_sdk_map(cls, version_list):
- if msvc.version in cls._toolset_default_cache:
- toolset_default = cls._toolset_default_cache[msvc.version]
- else:
- toolset_default = cls._msvc_read_toolset_default(msvc, vc_dir)
- cls._toolset_default_cache[msvc.version] = toolset_default
+ if not cls.sdk_dispatch_map:
+ cls.sdk_dispatch_map = {
+ '10.0': (cls._sdk_10, '10.0'),
+ '8.1': (cls._sdk_81, '8.1'),
+ }
- return toolset_default
+ sdk_map_list = []
+ for version in version_list:
+ func, reg_version = cls.sdk_dispatch_map[version]
+ sdk_map = func(version, reg_version)
+ sdk_map_list.append(sdk_map)
+
+ combined_sdk_map = cls._combine_sdk_map_list(sdk_map_list)
+ return combined_sdk_map
@classmethod
- def _msvc_version_toolset_vcvars(cls, msvc, vc_dir, toolset_version):
+ def _sdk_map(cls, version_list):
+ key = tuple(version_list)
+ if key in cls.sdk_cache:
+ sdk_map = cls.sdk_cache[key]
+ else:
+ version_numlist = [float(v) for v in version_list]
+ version_numlist.sort(reverse=True)
+ key = tuple([str(v) for v in version_numlist])
+ sdk_map = cls._version_list_sdk_map(key)
+ cls.sdk_cache[key] = sdk_map
+ return sdk_map
- if toolset_version == '14.0':
- return toolset_version
+ @classmethod
+ def _get_sdk_version_list(cls, version_list, platform):
+ sdk_map = cls._sdk_map(version_list)
+ sdk_list = sdk_map.get(platform, [])
+ return sdk_list
- toolsets_sxs, toolsets_full = cls._msvc_version_toolsets(msvc, vc_dir)
+def get_sdk_versions(MSVC_VERSION=None, MSVC_UWP_APP=False):
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric == cls.VS2019.vc_buildtools_def.vc_version_numeric:
- # necessary to detect toolset not found
- if toolset_version == '14.28.16.8':
- new_toolset_version = '14.28'
- # VS2019\Common7\Tools\vsdevcmd\ext\vcvars.bat AzDO Bug#1293526
- # special handling of the 16.8 SxS toolset, use VC\Auxiliary\Build\14.28 directory and SxS files
- # if SxS version 14.28 not present/installed, fallback selection of toolset VC\Tools\MSVC\14.28.nnnnn.
- debug(
- 'rewrite toolset_version=%s => toolset_version=%s',
- repr(toolset_version), repr(new_toolset_version)
- )
- toolset_version = new_toolset_version
+ sdk_versions = []
- if toolset_version in toolsets_sxs:
- toolset_vcvars = toolsets_sxs[toolset_version]
- return toolset_vcvars
+ if not MSVC_VERSION:
+ vcs = get_installed_vcs()
+ if not vcs:
+ return sdk_versions
+ MSVC_VERSION = vcs[0]
- for toolset_full in toolsets_full:
- if toolset_full.startswith(toolset_version):
- toolset_vcvars = toolset_full
- return toolset_vcvars
+ verstr = get_msvc_version_numeric(MSVC_VERSION)
+ vs_def = _Const.MSVC_VERSION_EXTERNAL.get(verstr, None)
+ if not vs_def:
+ return sdk_versions
- return None
+ is_uwp = True if MSVC_UWP_APP in _Const.BOOLEAN_KEYS[True] else False
+ platform = 'uwp' if is_uwp else 'desktop'
+ sdk_list = _WindowsSDK._get_sdk_version_list(vs_def.vc_sdk_versions, platform)
+
+ sdk_versions.extend(sdk_list)
+ return sdk_versions
+
+class _ScriptArguments:
+
+ # TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last?
+ re_sdk_version_100 = re.compile(r'^10[.][0-9][.][0-9]{5}[.][0-9]{1}$')
+ re_sdk_version_81 = re.compile(r'^8[.]1$')
+
+ re_sdk_dispatch_map = {
+ '10.0': re_sdk_version_100,
+ '8.1': re_sdk_version_81,
+ }
# capture msvc version
re_toolset_version = re.compile(r'^(?P[1-9][0-9]?[.][0-9])[0-9.]*$', re.IGNORECASE)
@@ -1920,223 +2188,210 @@ class _MSVCScriptArguments:
# valid SxS formats will be matched with re_toolset_full: match 3 '.' format
re_toolset_sxs = re.compile(r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$')
- @classmethod
- def _msvc_script_argument_toolset_constraints(cls, msvc, toolset_version):
+ # MSVC_SCRIPT_ARGS
+ re_vcvars_uwp = re.compile(r'(?:(?(?:uwp|store))(?:(?!\S)|$)',re.IGNORECASE)
+ re_vcvars_sdk = re.compile(r'(?:(?(?:[1-9][0-9]*[.]\S*))(?:(?!\S)|$)',re.IGNORECASE)
+ re_vcvars_toolset = re.compile(r'(?:(?(?:[-]{1,2}|[/])vcvars_ver[=](?P\S*))(?:(?!\S)|$)', re.IGNORECASE)
+ re_vcvars_spectre = re.compile(r'(?:(?(?:[-]{1,2}|[/])vcvars_spectre_libs[=](?P\S*))(?:(?!\S)|$)',re.IGNORECASE)
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric:
- debug(
- 'invalid: msvc version constraint: %s < %s VS2017',
- repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
- repr(cls.VS2017.vc_buildtools_def.vc_version_numeric)
- )
- err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
- repr(toolset_version), repr(msvc.version), repr(cls.VS2017.vc_buildtools_def.vc_version)
- )
- return err_msg
+ # Force default sdk argument
+ MSVC_FORCE_DEFAULT_SDK = False
- m = cls.re_toolset_version.match(toolset_version)
- if not m:
- debug('invalid: re_toolset_version: toolset_version=%s', repr(toolset_version))
- err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format(
- repr(toolset_version)
- )
- return err_msg
+ # Force default toolset argument
+ MSVC_FORCE_DEFAULT_TOOLSET = False
- toolset_ver = m.group('version')
- toolset_vernum = float(toolset_ver)
+ # MSVC batch file arguments:
+ #
+ # VS2022: UWP, SDK, TOOLSET, SPECTRE
+ # VS2019: UWP, SDK, TOOLSET, SPECTRE
+ # VS2017: UWP, SDK, TOOLSET, SPECTRE
+ # VS2015: UWP, SDK
+ #
+ # MSVC_SCRIPT_ARGS: VS2015+
+ #
+ # MSVC_UWP_APP: VS2015+
+ # MSVC_SDK_VERSION: VS2015+
+ # MSVC_TOOLSET_VERSION: VS2017+
+ # MSVC_SPECTRE_LIBS: VS2017+
- if toolset_vernum < cls.VS2015.vc_buildtools_def.vc_version_numeric:
- debug(
- 'invalid: toolset version constraint: %s < %s VS2015',
- repr(toolset_vernum), repr(cls.VS2015.vc_buildtools_def.vc_version_numeric)
- )
- err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < {} VS2015".format(
- repr(toolset_version), repr(toolset_ver), repr(cls.VS2015.vc_buildtools_def.vc_version)
- )
- return err_msg
+ @enum.unique
+ class SortOrder(enum.IntEnum):
+ ARCH = 0 # arch
+ UWP = 1 # MSVC_UWP_APP
+ SDK = 2 # MSVC_SDK_VERSION
+ TOOLSET = 3 # MSVC_TOOLSET_VERSION
+ SPECTRE = 4 # MSVC_SPECTRE_LIBS
+ USER = 5 # MSVC_SCRIPT_ARGS
- if toolset_vernum > msvc.vs_def.vc_buildtools_def.vc_version_numeric:
- debug(
- 'invalid: toolset version constraint: toolset %s > %s msvc',
- repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric)
- )
- err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format(
- repr(toolset_version), repr(toolset_ver), repr(msvc.version)
- )
- return err_msg
+ VS2019 = _Const.MSVS_VERSION_INTERNAL['2019']
+ VS2017 = _Const.MSVS_VERSION_INTERNAL['2017']
+ VS2015 = _Const.MSVS_VERSION_INTERNAL['2015']
- if toolset_vernum == cls.VS2015.vc_buildtools_def.vc_version_numeric:
- # tooset = 14.0
- if cls.re_toolset_full.match(toolset_version):
- if not cls.re_toolset_140.match(toolset_version):
- debug(
- 'invalid: toolset version 14.0 constraint: %s != 14.0',
- repr(toolset_version)
- )
- err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0'".format(
- repr(toolset_version), repr(toolset_version)
- )
- return err_msg
- return None
+ MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [
+ 'version', # fully qualified msvc version (e.g., '14.1Exp')
+ 'vs_def',
+ ])
- if cls.re_toolset_full.match(toolset_version):
- debug('valid: re_toolset_full: toolset_version=%s', repr(toolset_version))
- return None
+ @classmethod
+ def _msvc_version(cls, version):
- if cls.re_toolset_sxs.match(toolset_version):
- debug('valid: re_toolset_sxs: toolset_version=%s', repr(toolset_version))
- return None
+ verstr = get_msvc_version_numeric(version)
+ vs_def = _Const.MSVC_VERSION_INTERNAL[verstr]
- debug('invalid: method exit: toolset_version=%s', repr(toolset_version))
- err_msg = "MSVC_TOOLSET_VERSION ({}) format is not supported".format(repr(toolset_version))
- return err_msg
+ version_args = cls.MSVC_VERSION_ARGS_DEFINITION(
+ version = version,
+ vs_def = vs_def,
+ )
+
+ return version_args
@classmethod
- def _msvc_script_argument_toolset(cls, env, msvc, vc_dir, arglist):
+ def _msvc_script_argument_uwp(cls, env, msvc, arglist):
- toolset_version = env['MSVC_TOOLSET_VERSION']
- debug('MSVC_VERSION=%s, MSVC_TOOLSET_VERSION=%s', repr(msvc.version), repr(toolset_version))
+ uwp_app = env['MSVC_UWP_APP']
+ debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app))
- if not toolset_version:
+ if not uwp_app:
return None
- err_msg = cls._msvc_script_argument_toolset_constraints(msvc, toolset_version)
- if err_msg:
- raise MSVCArgumentError(err_msg)
+ if uwp_app not in _Const.BOOLEAN_KEYS[True]:
+ return None
- if toolset_version.startswith('14.0') and len(toolset_version) > len('14.0'):
- new_toolset_version = '14.0'
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric:
debug(
- 'rewrite toolset_version=%s => toolset_version=%s',
- repr(toolset_version), repr(new_toolset_version)
+ 'invalid: msvc version constraint: %s < %s VS2015',
+ repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(cls.VS2015.vc_buildtools_def.vc_version_numeric)
)
- toolset_version = new_toolset_version
-
- toolset_vcvars = cls._msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version)
- debug(
- 'toolset: toolset_version=%s, toolset_vcvars=%s',
- repr(toolset_version), repr(toolset_vcvars)
- )
-
- if not toolset_vcvars:
- err_msg = "MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}".format(
- repr(toolset_version), repr(msvc.version)
+ err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
+ repr(uwp_app), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version)
)
raise MSVCArgumentError(err_msg)
- argpair = (cls.SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_vcvars))
+ # VS2017+ rewrites uwp => store for 14.0 toolset
+ uwp_arg = msvc.vs_def.vc_uwp
+
+ # uwp may not be installed
+ argpair = (cls.SortOrder.UWP, uwp_arg)
arglist.append(argpair)
- return toolset_vcvars
+ return uwp_arg
@classmethod
- def _msvc_script_default_toolset(cls, env, msvc, vc_dir, arglist):
+ def _user_script_argument_uwp(cls, env, uwp, user_argstr):
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric:
+ matches = [m for m in cls.re_vcvars_uwp.finditer(user_argstr)]
+ if not matches:
return None
- toolset_default = cls._msvc_default_toolset(msvc, vc_dir)
- if not toolset_default:
+ if len(matches) > 1:
+ debug('multiple uwp declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
+ err_msg = "multiple uwp declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
+ raise MSVCArgumentError(err_msg)
+
+ if not uwp:
return None
- debug('MSVC_VERSION=%s, toolset_default=%s', repr(msvc.version), repr(toolset_default))
+ env_argstr = env.get('MSVC_UWP_APP','')
+ debug('multiple uwp declarations: MSVC_UWP_APP=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
- argpair = (cls.SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_default))
- arglist.append(argpair)
+ err_msg = "multiple uwp declarations: MSVC_UWP_APP={} and MSVC_SCRIPT_ARGS={}".format(
+ repr(env_argstr), repr(user_argstr)
+ )
- return toolset_default
+ raise MSVCArgumentError(err_msg)
@classmethod
- def _msvc_script_argument_spectre(cls, env, msvc, arglist):
-
- spectre_libs = env['MSVC_SPECTRE_LIBS']
- debug('MSVC_VERSION=%s, MSVC_SPECTRE_LIBS=%s', repr(msvc.version), repr(spectre_libs))
-
- if not spectre_libs:
- return None
-
- if spectre_libs not in (True, '1'):
- return None
+ def _msvc_script_argument_sdk_constraints(cls, msvc, sdk_version):
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric:
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric:
debug(
- 'invalid: msvc version constraint: %s < %s VS2017',
+ 'invalid: msvc_version constraint: %s < %s VS2015',
repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
- repr(cls.VS2017.vc_buildtools_def.vc_version_numeric)
+ repr(cls.VS2015.vc_buildtools_def.vc_version_numeric)
)
- err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
- repr(spectre_libs), repr(msvc.version), repr(cls.VS2017.vc_buildtools_def.vc_version)
+ err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
+ repr(sdk_version), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version)
)
- raise MSVCArgumentError(err_msg)
-
- spectre_arg = 'spectre'
+ return err_msg
- # spectre libs may not be installed
- argpair = (cls.SortOrder.SPECTRE, '-vcvars_spectre_libs={}'.format(spectre_arg))
- arglist.append(argpair)
+ for msvc_sdk_version in msvc.vs_def.vc_sdk_versions:
+ re_sdk_version = cls.re_sdk_dispatch_map[msvc_sdk_version]
+ if re_sdk_version.match(sdk_version):
+ debug('valid: sdk_version=%s', repr(sdk_version))
+ return None
- return spectre_arg
+ debug('invalid: method exit: sdk_version=%s', repr(sdk_version))
+ err_msg = "MSVC_SDK_VERSION ({}) is not supported".format(repr(sdk_version))
+ return err_msg
@classmethod
- def _msvc_script_argument_user(cls, env, msvc, arglist):
+ def _msvc_script_argument_sdk(cls, env, msvc, platform, arglist):
- # subst None -> empty string
- script_args = env.subst('$MSVC_SCRIPT_ARGS')
- debug('MSVC_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(msvc.version), repr(script_args))
+ sdk_version = env['MSVC_SDK_VERSION']
+ debug(
+ 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, platform=%s',
+ repr(msvc.version), repr(sdk_version), repr(platform)
+ )
- if not script_args:
+ if not sdk_version:
return None
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric:
- debug(
- 'invalid: msvc version constraint: %s < %s VS2015',
- repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
- repr(cls.VS2015.vc_buildtools_def.vc_version_numeric)
- )
- err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
- repr(script_args), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version)
+ err_msg = cls._msvc_script_argument_sdk_constraints(msvc, sdk_version)
+ if err_msg:
+ raise MSVCArgumentError(err_msg)
+
+ sdk_list = _WindowsSDK._get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform)
+
+ if sdk_version not in sdk_list:
+ err_msg = "MSVC_SDK_VERSION {} not found for platform {}".format(
+ repr(sdk_version), repr(platform)
)
raise MSVCArgumentError(err_msg)
- # user arguments are not validated
- argpair = (cls.SortOrder.USER, script_args)
+ # sdk folder may not exist
+ argpair = (cls.SortOrder.SDK, sdk_version)
arglist.append(argpair)
- return script_args
-
- re_vcvars_uwp = re.compile(r'(?:\s|^)(?P(?:uwp|store))(?:\s|$)',re.IGNORECASE)
- re_vcvars_sdk = re.compile(r'(?:\s|^)(?P(?:[1-9][0-9]*[.]\S*))(?:\s|$)',re.IGNORECASE)
- re_vcvars_spectre = re.compile(r'(?:\s|^)(?P(?:[-]{1,2}|[/])vcvars_spectre_libs[=](?P\S*))(?:\s|$)',re.IGNORECASE)
- re_vcvars_toolset = re.compile(r'(?:\s|^)(?P(?:[-]{1,2}|[/])vcvars_ver[=](?P\S*))(?:\s|$)', re.IGNORECASE)
+ return sdk_version
@classmethod
- def _user_script_argument_uwp(cls, env, uwp, user_argstr):
+ def _msvc_script_default_sdk(cls, env, msvc, platform, arglist):
- if not uwp:
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric:
return None
- m = cls.re_vcvars_uwp.search(user_argstr)
- if not m:
+ sdk_list = _WindowsSDK._get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform)
+ if not len(sdk_list):
return None
- env_argstr = env.get('MSVC_UWP_APP','')
- debug('multiple uwp declarations: MSVC_UWP_APP=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
+ sdk_default = sdk_list[0]
- err_msg = "multiple uwp declarations: MSVC_UWP_APP={} and MSVC_SCRIPT_ARGS={}".format(
- repr(env_argstr), repr(user_argstr)
+ debug(
+ 'MSVC_VERSION=%s, sdk_default=%s, platform=%s',
+ repr(msvc.version), repr(sdk_default), repr(platform)
)
- raise MSVCArgumentError(err_msg)
+ argpair = (cls.SortOrder.SDK, sdk_default)
+ arglist.append(argpair)
+
+ return sdk_default
@classmethod
def _user_script_argument_sdk(cls, env, sdk_version, user_argstr):
- if not sdk_version:
+ matches = [m for m in cls.re_vcvars_sdk.finditer(user_argstr)]
+ if not matches:
return None
- m = cls.re_vcvars_sdk.search(user_argstr)
- if not m:
- return None
+ if len(matches) > 1:
+ debug('multiple sdk version declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
+ err_msg = "multiple sdk version declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
+ raise MSVCArgumentError(err_msg)
+
+ if not sdk_version:
+ user_sdk = matches[0].group('sdk')
+ return user_sdk
env_argstr = env.get('MSVC_SDK_VERSION','')
debug('multiple sdk version declarations: MSVC_SDK_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
@@ -2148,33 +2403,374 @@ class _MSVCScriptArguments:
raise MSVCArgumentError(err_msg)
@classmethod
- def _user_script_argument_toolset(cls, env, toolset_version, user_argstr):
+ def _msvc_read_toolset_file(cls, msvc, filename):
+ toolset_version = None
+ try:
+ with open(filename) as f:
+ toolset_version = f.readlines()[0].strip()
+ debug(
+ 'msvc_version=%s, filename=%s, toolset_version=%s',
+ repr(msvc.version), repr(filename), repr(toolset_version)
+ )
+ except OSError:
+ debug('OSError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename))
+ except IndexError:
+ debug('IndexError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename))
+ return toolset_version
- m = cls.re_vcvars_toolset.search(user_argstr)
- if not m:
- return None
+ @classmethod
+ def _msvc_read_toolset_folders(cls, msvc, vc_dir):
- if not toolset_version:
- user_toolset = m.group('toolset')
- return user_toolset
+ toolsets_sxs = {}
+ toolsets_full = []
- env_argstr = env.get('MSVC_TOOLSET_VERSION','')
- debug('multiple toolset version declarations: MSVC_TOOLSET_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
+ build_dir = os.path.join(vc_dir, "Auxiliary", "Build")
+ sxs_toolsets = [f.name for f in os.scandir(build_dir) if f.is_dir()]
+ for sxs_toolset in sxs_toolsets:
+ filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset)
+ filepath = os.path.join(build_dir, sxs_toolset, filename)
+ debug('sxs toolset: check file=%s', repr(filepath))
+ if os.path.exists(filepath):
+ toolset_version = cls._msvc_read_toolset_file(msvc, filepath)
+ if not toolset_version:
+ continue
+ toolsets_sxs[sxs_toolset] = toolset_version
+ debug(
+ 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s',
+ repr(msvc.version), repr(sxs_toolset), toolset_version
+ )
- err_msg = "multiple toolset version declarations: MSVC_TOOLSET_VERSION={} and MSVC_SCRIPT_ARGS={}".format(
- repr(env_argstr), repr(user_argstr)
- )
+ toolset_dir = os.path.join(vc_dir, "Tools", "MSVC")
+ toolsets = [f.name for f in os.scandir(toolset_dir) if f.is_dir()]
+ for toolset_version in toolsets:
+ binpath = os.path.join(toolset_dir, toolset_version, "bin")
+ debug('toolset: check binpath=%s', repr(binpath))
+ if os.path.exists(binpath):
+ toolsets_full.append(toolset_version)
+ debug(
+ 'toolset: msvc_version=%s, toolset_version=%s',
+ repr(msvc.version), repr(toolset_version)
+ )
- raise MSVCArgumentError(err_msg)
+ toolsets_full.sort(reverse=True)
+ debug('msvc_version=%s, toolsets=%s', repr(msvc.version), repr(toolsets_full))
+
+ return toolsets_sxs, toolsets_full
+
+ @classmethod
+ def _msvc_read_toolset_default(cls, msvc, vc_dir):
+
+ build_dir = os.path.join(vc_dir, "Auxiliary", "Build")
+
+ # VS2019+
+ filename = "Microsoft.VCToolsVersion.{}.default.txt".format(msvc.vs_def.vc_buildtools_def.vc_buildtools)
+ filepath = os.path.join(build_dir, filename)
+
+ debug('default toolset: check file=%s', repr(filepath))
+ toolset_buildtools = None
+ if os.path.exists(filepath):
+ toolset_buildtools = cls._msvc_read_toolset_file(msvc, filepath)
+ if toolset_buildtools:
+ return toolset_buildtools
+
+ # VS2017+
+ filename = "Microsoft.VCToolsVersion.default.txt"
+ filepath = os.path.join(build_dir, filename)
+
+ debug('default toolset: check file=%s', repr(filepath))
+ toolset_default = None
+ if os.path.exists(filepath):
+ toolset_default = cls._msvc_read_toolset_file(msvc, filepath)
+ if toolset_default:
+ return toolset_default
+
+ return None
+
+ @classmethod
+ def _reset_toolsets(cls):
+ debug('reset: toolset cache')
+ cls._toolset_version_cache = {}
+ cls._toolset_default_cache = {}
+
+ _toolset_version_cache = {}
+ _toolset_default_cache = {}
+
+ @classmethod
+ def _msvc_version_toolsets(cls, msvc, vc_dir):
+
+ if msvc.version in cls._toolset_version_cache:
+ toolsets_sxs, toolsets_full = cls._toolset_version_cache[msvc.version]
+ else:
+ toolsets_sxs, toolsets_full = cls._msvc_read_toolset_folders(msvc, vc_dir)
+ cls._toolset_version_cache[msvc.version] = toolsets_sxs, toolsets_full
+
+ return toolsets_sxs, toolsets_full
+
+ @classmethod
+ def _msvc_default_toolset(cls, msvc, vc_dir):
+
+ if msvc.version in cls._toolset_default_cache:
+ toolset_default = cls._toolset_default_cache[msvc.version]
+ else:
+ toolset_default = cls._msvc_read_toolset_default(msvc, vc_dir)
+ cls._toolset_default_cache[msvc.version] = toolset_default
+
+ return toolset_default
+
+ @classmethod
+ def _msvc_version_toolset_vcvars(cls, msvc, vc_dir, toolset_version):
+
+ if toolset_version == '14.0':
+ return toolset_version
+
+ toolsets_sxs, toolsets_full = cls._msvc_version_toolsets(msvc, vc_dir)
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric == cls.VS2019.vc_buildtools_def.vc_version_numeric:
+ # necessary to detect toolset not found
+ if toolset_version == '14.28.16.8':
+ new_toolset_version = '14.28'
+ # VS2019\Common7\Tools\vsdevcmd\ext\vcvars.bat AzDO Bug#1293526
+ # special handling of the 16.8 SxS toolset, use VC\Auxiliary\Build\14.28 directory and SxS files
+ # if SxS version 14.28 not present/installed, fallback selection of toolset VC\Tools\MSVC\14.28.nnnnn.
+ debug(
+ 'rewrite toolset_version=%s => toolset_version=%s',
+ repr(toolset_version), repr(new_toolset_version)
+ )
+ toolset_version = new_toolset_version
+
+ if toolset_version in toolsets_sxs:
+ toolset_vcvars = toolsets_sxs[toolset_version]
+ return toolset_vcvars
+
+ for toolset_full in toolsets_full:
+ if toolset_full.startswith(toolset_version):
+ toolset_vcvars = toolset_full
+ return toolset_vcvars
+
+ return None
+
+ @classmethod
+ def _msvc_script_argument_toolset_constraints(cls, msvc, toolset_version):
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: msvc version constraint: %s < %s VS2017',
+ repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(cls.VS2017.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
+ repr(toolset_version), repr(msvc.version), repr(cls.VS2017.vc_buildtools_def.vc_version)
+ )
+ return err_msg
+
+ m = cls.re_toolset_version.match(toolset_version)
+ if not m:
+ debug('invalid: re_toolset_version: toolset_version=%s', repr(toolset_version))
+ err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format(
+ repr(toolset_version)
+ )
+ return err_msg
+
+ toolset_ver = m.group('version')
+ toolset_vernum = float(toolset_ver)
+
+ if toolset_vernum < cls.VS2015.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: toolset version constraint: %s < %s VS2015',
+ repr(toolset_vernum), repr(cls.VS2015.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < {} VS2015".format(
+ repr(toolset_version), repr(toolset_ver), repr(cls.VS2015.vc_buildtools_def.vc_version)
+ )
+ return err_msg
+
+ if toolset_vernum > msvc.vs_def.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: toolset version constraint: toolset %s > %s msvc',
+ repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format(
+ repr(toolset_version), repr(toolset_ver), repr(msvc.version)
+ )
+ return err_msg
+
+ if toolset_vernum == cls.VS2015.vc_buildtools_def.vc_version_numeric:
+ # tooset = 14.0
+ if cls.re_toolset_full.match(toolset_version):
+ if not cls.re_toolset_140.match(toolset_version):
+ debug(
+ 'invalid: toolset version 14.0 constraint: %s != 14.0',
+ repr(toolset_version)
+ )
+ err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0'".format(
+ repr(toolset_version), repr(toolset_version)
+ )
+ return err_msg
+ return None
+
+ if cls.re_toolset_full.match(toolset_version):
+ debug('valid: re_toolset_full: toolset_version=%s', repr(toolset_version))
+ return None
+
+ if cls.re_toolset_sxs.match(toolset_version):
+ debug('valid: re_toolset_sxs: toolset_version=%s', repr(toolset_version))
+ return None
+
+ debug('invalid: method exit: toolset_version=%s', repr(toolset_version))
+ err_msg = "MSVC_TOOLSET_VERSION ({}) format is not supported".format(repr(toolset_version))
+ return err_msg
+
+ @classmethod
+ def _msvc_script_argument_toolset(cls, env, msvc, vc_dir, arglist):
+
+ toolset_version = env['MSVC_TOOLSET_VERSION']
+ debug('MSVC_VERSION=%s, MSVC_TOOLSET_VERSION=%s', repr(msvc.version), repr(toolset_version))
+
+ if not toolset_version:
+ return None
+
+ err_msg = cls._msvc_script_argument_toolset_constraints(msvc, toolset_version)
+ if err_msg:
+ raise MSVCArgumentError(err_msg)
+
+ if toolset_version.startswith('14.0') and len(toolset_version) > len('14.0'):
+ new_toolset_version = '14.0'
+ debug(
+ 'rewrite toolset_version=%s => toolset_version=%s',
+ repr(toolset_version), repr(new_toolset_version)
+ )
+ toolset_version = new_toolset_version
+
+ toolset_vcvars = cls._msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version)
+ debug(
+ 'toolset: toolset_version=%s, toolset_vcvars=%s',
+ repr(toolset_version), repr(toolset_vcvars)
+ )
+
+ if not toolset_vcvars:
+ err_msg = "MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}".format(
+ repr(toolset_version), repr(msvc.version)
+ )
+ raise MSVCArgumentError(err_msg)
+
+ argpair = (cls.SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_vcvars))
+ arglist.append(argpair)
+
+ return toolset_vcvars
+
+ @classmethod
+ def _msvc_script_default_toolset(cls, env, msvc, vc_dir, arglist):
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric:
+ return None
+
+ toolset_default = cls._msvc_default_toolset(msvc, vc_dir)
+ if not toolset_default:
+ return None
+
+ debug('MSVC_VERSION=%s, toolset_default=%s', repr(msvc.version), repr(toolset_default))
+
+ argpair = (cls.SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_default))
+ arglist.append(argpair)
+
+ return toolset_default
+
+ @classmethod
+ def _user_script_argument_toolset(cls, env, toolset_version, user_argstr):
+
+ matches = [m for m in cls.re_vcvars_toolset.finditer(user_argstr)]
+ if not matches:
+ return None
+
+ if len(matches) > 1:
+ debug('multiple toolset version declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
+ err_msg = "multiple toolset version declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
+ raise MSVCArgumentError(err_msg)
+
+ if not toolset_version:
+ user_toolset = matches[0].group('toolset')
+ return user_toolset
+
+ env_argstr = env.get('MSVC_TOOLSET_VERSION','')
+ debug('multiple toolset version declarations: MSVC_TOOLSET_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
+
+ err_msg = "multiple toolset version declarations: MSVC_TOOLSET_VERSION={} and MSVC_SCRIPT_ARGS={}".format(
+ repr(env_argstr), repr(user_argstr)
+ )
+
+ raise MSVCArgumentError(err_msg)
+
+ @classmethod
+ def _msvc_script_argument_spectre(cls, env, msvc, arglist):
+
+ spectre_libs = env['MSVC_SPECTRE_LIBS']
+ debug('MSVC_VERSION=%s, MSVC_SPECTRE_LIBS=%s', repr(msvc.version), repr(spectre_libs))
+
+ if not spectre_libs:
+ return None
+
+ if spectre_libs not in (True, '1'):
+ return None
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: msvc version constraint: %s < %s VS2017',
+ repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(cls.VS2017.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
+ repr(spectre_libs), repr(msvc.version), repr(cls.VS2017.vc_buildtools_def.vc_version)
+ )
+ raise MSVCArgumentError(err_msg)
+
+ spectre_arg = 'spectre'
+
+ # spectre libs may not be installed
+ argpair = (cls.SortOrder.SPECTRE, '-vcvars_spectre_libs={}'.format(spectre_arg))
+ arglist.append(argpair)
+
+ return spectre_arg
+
+ @classmethod
+ def _msvc_script_argument_user(cls, env, msvc, arglist):
+
+ # subst None -> empty string
+ script_args = env.subst('$MSVC_SCRIPT_ARGS')
+ debug('MSVC_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(msvc.version), repr(script_args))
+
+ if not script_args:
+ return None
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: msvc version constraint: %s < %s VS2015',
+ repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(cls.VS2015.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
+ repr(script_args), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version)
+ )
+ raise MSVCArgumentError(err_msg)
+
+ # user arguments are not validated
+ argpair = (cls.SortOrder.USER, script_args)
+ arglist.append(argpair)
+
+ return script_args
@classmethod
def _user_script_argument_spectre(cls, env, spectre, user_argstr):
- if not spectre:
+ matches = [m for m in cls.re_vcvars_spectre.finditer(user_argstr)]
+ if not matches:
return None
- m = cls.re_vcvars_spectre.search(user_argstr)
- if not m:
+ if len(matches) > 1:
+ debug('multiple spectre declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
+ err_msg = "multiple spectre declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
+ raise MSVCArgumentError(err_msg)
+
+ if not spectre:
return None
env_argstr = env.get('MSVC_SPECTRE_LIBS','')
@@ -2189,55 +2785,70 @@ class _MSVCScriptArguments:
@classmethod
def msvc_script_arguments(cls, env, version, vc_dir, arg):
- msvc = cls._msvc_version(version)
-
- argstr = ''
arglist = []
+ msvc = cls._msvc_version(version)
+
if arg:
argpair = (cls.SortOrder.ARCH, arg)
arglist.append(argpair)
- user_argstr = None
- user_toolset = None
-
- uwp = None
- sdk_version = None
- toolset_version = None
- spectre = None
-
if 'MSVC_SCRIPT_ARGS' in env:
user_argstr = cls._msvc_script_argument_user(env, msvc, arglist)
+ else:
+ user_argstr = None
if 'MSVC_UWP_APP' in env:
uwp = cls._msvc_script_argument_uwp(env, msvc, arglist)
- if uwp and user_argstr:
- cls._user_script_argument_uwp(env, uwp, user_argstr)
+ else:
+ uwp = None
+
+ if user_argstr:
+ cls._user_script_argument_uwp(env, uwp, user_argstr)
+
+ platform = 'uwp' if uwp else 'desktop'
if 'MSVC_SDK_VERSION' in env:
- is_uwp = True if uwp else False
- sdk_version = cls._msvc_script_argument_sdk(env, msvc, is_uwp, arglist)
- if sdk_version and user_argstr:
- cls._user_script_argument_sdk(env, sdk_version, user_argstr)
+ sdk_version = cls._msvc_script_argument_sdk(env, msvc, platform, arglist)
+ else:
+ sdk_version = None
+
+ if user_argstr:
+ user_sdk = cls._user_script_argument_sdk(env, sdk_version, user_argstr)
+ else:
+ user_sdk = None
+
+ if cls.MSVC_FORCE_DEFAULT_SDK:
+ if not sdk_version and not user_sdk:
+ sdk_version = cls._msvc_script_default_sdk(env, msvc, platform, arglist)
if 'MSVC_TOOLSET_VERSION' in env:
toolset_version = cls._msvc_script_argument_toolset(env, msvc, vc_dir, arglist)
+ else:
+ toolset_version = None
if user_argstr:
user_toolset = cls._user_script_argument_toolset(env, toolset_version, user_argstr)
+ else:
+ user_toolset = None
- if cls.MSVC_TOOLSET_DEFAULT_VCVARSVER:
+ if cls.MSVC_FORCE_DEFAULT_TOOLSET:
if not toolset_version and not user_toolset:
toolset_version = cls._msvc_script_default_toolset(env, msvc, vc_dir, arglist)
if 'MSVC_SPECTRE_LIBS' in env:
spectre = cls._msvc_script_argument_spectre(env, msvc, arglist)
- if spectre and user_argstr:
- cls._user_script_argument_spectre(env, spectre, user_argstr)
+ else:
+ spectre = None
+
+ if user_argstr:
+ cls._user_script_argument_spectre(env, spectre, user_argstr)
if arglist:
arglist.sort()
argstr = ' '.join([argpair[-1] for argpair in arglist]).strip()
+ else:
+ argstr = ''
debug('arguments: %s', repr(argstr))
return argstr
@@ -2247,231 +2858,3 @@ class _MSVCScriptArguments:
debug('reset')
cls._reset_toolsets()
-def msvc_find_valid_batch_script(env, version):
- """Find and execute appropriate batch script to set up build env.
-
- The MSVC build environment depends heavily on having the shell
- environment set. SCons does not inherit that, and does not count
- on that being set up correctly anyway, so it tries to find the right
- MSVC batch script, or the right arguments to the generic batch script
- vcvarsall.bat, and run that, so we have a valid environment to build in.
- There are dragons here: the batch scripts don't fail (see comments
- elsewhere), they just leave you with a bad setup, so try hard to
- get it right.
- """
-
- # Find the host, target, and all candidate (host, target) platform combinations:
- platforms = get_host_target(env, version)
- debug("host_platform %s, target_platform %s host_target_list %s", *platforms)
- host_platform, target_platform, host_target_list = platforms
-
- d = None
- version_installed = False
- for host_arch, target_arch, in host_target_list:
- # Set to current arch.
- env['TARGET_ARCH'] = target_arch
- arg = ''
-
- # Try to locate a batch file for this host/target platform combo
- try:
- (vc_script, arg, vc_dir, sdk_script) = find_batch_file(env, version, host_arch, target_arch)
- debug('vc_script:%s vc_script_arg:%s sdk_script:%s', vc_script, arg, sdk_script)
- version_installed = True
- except VisualCException as e:
- msg = str(e)
- debug('Caught exception while looking for batch file (%s)', msg)
- version_installed = False
- continue
-
- # Try to use the located batch file for this host/target platform combo
- debug('use_script 2 %s, args:%s', repr(vc_script), arg)
- found = None
- if vc_script:
- arg = _MSVCScriptArguments.msvc_script_arguments(env, version, vc_dir, arg)
- try:
- d = script_env(vc_script, args=arg)
- found = vc_script
- except BatchFileExecutionError as e:
- debug('use_script 3: failed running VC script %s: %s: Error:%s', repr(vc_script), arg, e)
- vc_script=None
- continue
- if not vc_script and sdk_script:
- debug('use_script 4: trying sdk script: %s', sdk_script)
- try:
- d = script_env(sdk_script)
- found = sdk_script
- except BatchFileExecutionError as e:
- debug('use_script 5: failed running SDK script %s: Error:%s', repr(sdk_script), e)
- continue
- elif not vc_script and not sdk_script:
- debug('use_script 6: Neither VC script nor SDK script found')
- continue
-
- debug("Found a working script/target: %s/%s", repr(found), arg)
- break # We've found a working target_platform, so stop looking
-
- # If we cannot find a viable installed compiler, reset the TARGET_ARCH
- # To it's initial value
- if not d:
- env['TARGET_ARCH'] = target_platform
-
- if version_installed:
- msg = "MSVC version '{}' working host/target script was not found.\n" \
- " Host = '{}', Target = '{}'\n" \
- " Visual Studio C/C++ compilers may not be set correctly".format(
- version, host_platform, target_platform
- )
- else:
- installed_vcs = get_installed_vcs(env)
- if installed_vcs:
- msg = "MSVC version '{}' was not found.\n" \
- " Visual Studio C/C++ compilers may not be set correctly.\n" \
- " Installed versions are: {}".format(version, installed_vcs)
- else:
- msg = "MSVC version '{}' was not found.\n" \
- " No versions of the MSVC compiler were found.\n" \
- " Visual Studio C/C++ compilers may not be set correctly".format(version)
-
- _msvc_notfound_policy_handler(env, msg)
-
- return d
-
-_undefined = None
-
-def get_use_script_use_settings(env):
- global _undefined
-
- if _undefined is None:
- _undefined = object()
-
- # use_script use_settings return values action
- # value ignored (value, None) use script or bypass detection
- # undefined value not None (False, value) use dictionary
- # undefined undefined/None (True, None) msvc detection
-
- # None (documentation) or evaluates False (code): bypass detection
- # need to distinguish between undefined and None
- use_script = env.get('MSVC_USE_SCRIPT', _undefined)
-
- if use_script != _undefined:
- # use_script defined, use_settings ignored (not type checked)
- return (use_script, None)
-
- # undefined or None: use_settings ignored
- use_settings = env.get('MSVC_USE_SETTINGS', None)
-
- if use_settings is not None:
- # use script undefined, use_settings defined and not None (type checked)
- return (False, use_settings)
-
- # use script undefined, use_settings undefined or None
- return (True, None)
-
-def msvc_setup_env(env):
- debug('called')
- version = get_default_version(env)
- if version is None:
- if not msvc_setup_env_user(env):
- _MSVCSetupEnvDefault.set_nodefault()
- return None
-
- # XXX: we set-up both MSVS version for backward
- # compatibility with the msvs tool
- env['MSVC_VERSION'] = version
- env['MSVS_VERSION'] = version
- env['MSVS'] = {}
-
- use_script, use_settings = get_use_script_use_settings(env)
- if SCons.Util.is_String(use_script):
- use_script = use_script.strip()
- if not os.path.exists(use_script):
- raise MSVCScriptNotFound('Script specified by MSVC_USE_SCRIPT not found: "{}"'.format(use_script))
- args = env.subst('$MSVC_USE_SCRIPT_ARGS')
- debug('use_script 1 %s %s', repr(use_script), repr(args))
- d = script_env(use_script, args)
- elif use_script:
- d = msvc_find_valid_batch_script(env,version)
- debug('use_script 2 %s', d)
- if not d:
- return d
- elif use_settings is not None:
- if not SCons.Util.is_Dict(use_settings):
- error_msg = 'MSVC_USE_SETTINGS type error: expected a dictionary, found {}'.format(type(use_settings).__name__)
- raise MSVCUseSettingsError(error_msg)
- d = use_settings
- debug('use_settings %s', d)
- else:
- debug('MSVC_USE_SCRIPT set to False')
- warn_msg = "MSVC_USE_SCRIPT set to False, assuming environment " \
- "set correctly."
- SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
- return None
-
- for k, v in d.items():
- env.PrependENVPath(k, v, delete_existing=True)
- debug("env['ENV']['%s'] = %s", k, env['ENV'][k])
-
- # final check to issue a warning if the compiler is not present
- if not find_program_path(env, 'cl'):
- debug("did not find %s", _CL_EXE_NAME)
- if CONFIG_CACHE:
- propose = "SCONS_CACHE_MSVC_CONFIG caching enabled, remove cache file {} if out of date.".format(CONFIG_CACHE)
- else:
- propose = "It may need to be installed separately with Visual Studio."
- warn_msg = "Could not find MSVC compiler 'cl'. {}".format(propose)
- SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
-
-def msvc_exists(env=None, version=None):
- debug('version=%s', repr(version))
- vcs = get_installed_vcs(env)
- if version is None:
- rval = len(vcs) > 0
- else:
- rval = version in vcs
- debug('version=%s, return=%s', repr(version), rval)
- return rval
-
-def msvc_setup_env_user(env=None):
- rval = False
- if env:
-
- # Intent is to use msvc tools:
- # MSVC_VERSION or MSVS_VERSION: defined and is True
- # MSVC_USE_SCRIPT: defined and (is string or is False)
- # MSVC_USE_SETTINGS: defined and is not None
-
- # defined and is True
- for key in ['MSVC_VERSION', 'MSVS_VERSION']:
- if key in env and env[key]:
- rval = True
- debug('key=%s, return=%s', repr(key), rval)
- return rval
-
- # defined and (is string or is False)
- for key in ['MSVC_USE_SCRIPT']:
- if key in env and (SCons.Util.is_String(env[key]) or not env[key]):
- rval = True
- debug('key=%s, return=%s', repr(key), rval)
- return rval
-
- # defined and is not None
- for key in ['MSVC_USE_SETTINGS']:
- if key in env and env[key] is not None:
- rval = True
- debug('key=%s, return=%s', repr(key), rval)
- return rval
-
- debug('return=%s', rval)
- return rval
-
-def msvc_setup_env_tool(env=None, version=None, tool=None):
- debug('tool=%s, version=%s', repr(tool), repr(version))
- _MSVCSetupEnvDefault.register_tool(env, tool)
- rval = False
- if not rval and msvc_exists(env, version):
- rval = True
- if not rval and msvc_setup_env_user(env):
- rval = True
- debug('tool=%s, version=%s, return=%s', repr(tool), repr(version), rval)
- return rval
-
--
cgit v0.12
From 33bef70b9ccaac0106d5e7f0c37211d224b6a585 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Thu, 16 Jun 2022 12:43:22 -0400
Subject: Rename msvc batch platform (uwp/store/desktop) to platform_type.
---
SCons/Tool/MSCommon/vc.py | 50 +++++++++++++++++++++++------------------------
1 file changed, 24 insertions(+), 26 deletions(-)
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index c44c698..acffc7c 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -106,7 +106,7 @@ class _Const:
for bool, symbol_list in [
(False, (0, '0', False, 'False', 'FALSE', 'false', 'No', 'NO', 'no', None, '')),
- (True, (1, '1', True, 'True', 'TRUE', 'true', 'Yes', 'YES', 'yes', )),
+ (True, (1, '1', True, 'True', 'TRUE', 'true', 'Yes', 'YES', 'yes')),
]:
BOOLEAN_KEYS[bool] = symbol_list
for symbol in symbol_list:
@@ -2007,7 +2007,7 @@ class _WindowsSDK:
if not os.path.exists(sdk_inc_path):
continue
- for platform, sdk_inc_file in [
+ for platform_type, sdk_inc_file in [
('desktop', 'winsdkver.h'),
('uwp', 'windows.h'),
]:
@@ -2015,12 +2015,12 @@ class _WindowsSDK:
if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)):
continue
- key = (version_nbr, platform)
+ key = (version_nbr, platform_type)
if key in sdk_version_platform_seen:
continue
sdk_version_platform_seen.add(key)
- sdk_map[platform].append(version_nbr)
+ sdk_map[platform_type].append(version_nbr)
for key, val in sdk_map.items():
val.sort(reverse=True)
@@ -2039,8 +2039,6 @@ class _WindowsSDK:
sdk_version_platform_seen = set()
sdk_roots_seen = set()
- sdk_targets = []
-
for sdk_t in sdk_roots:
sdk_root = sdk_t[0]
@@ -2054,7 +2052,7 @@ class _WindowsSDK:
if not os.path.exists(sdk_inc_path):
continue
- for platform, sdk_inc_file in [
+ for platform_type, sdk_inc_file in [
('desktop', 'winsdkver.h'),
('uwp', 'windows.h'),
]:
@@ -2062,12 +2060,12 @@ class _WindowsSDK:
if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)):
continue
- key = (version_nbr, platform)
+ key = (version_nbr, platform_type)
if key in sdk_version_platform_seen:
continue
sdk_version_platform_seen.add(key)
- sdk_map[platform].append(version_nbr)
+ sdk_map[platform_type].append(version_nbr)
for key, val in sdk_map.items():
val.sort(reverse=True)
@@ -2134,9 +2132,9 @@ class _WindowsSDK:
return sdk_map
@classmethod
- def _get_sdk_version_list(cls, version_list, platform):
+ def _get_sdk_version_list(cls, version_list, platform_type):
sdk_map = cls._sdk_map(version_list)
- sdk_list = sdk_map.get(platform, [])
+ sdk_list = sdk_map.get(platform_type, [])
return sdk_list
def get_sdk_versions(MSVC_VERSION=None, MSVC_UWP_APP=False):
@@ -2155,8 +2153,8 @@ def get_sdk_versions(MSVC_VERSION=None, MSVC_UWP_APP=False):
return sdk_versions
is_uwp = True if MSVC_UWP_APP in _Const.BOOLEAN_KEYS[True] else False
- platform = 'uwp' if is_uwp else 'desktop'
- sdk_list = _WindowsSDK._get_sdk_version_list(vs_def.vc_sdk_versions, platform)
+ platform_type = 'uwp' if is_uwp else 'desktop'
+ sdk_list = _WindowsSDK._get_sdk_version_list(vs_def.vc_sdk_versions, platform_type)
sdk_versions.extend(sdk_list)
return sdk_versions
@@ -2326,12 +2324,12 @@ class _ScriptArguments:
return err_msg
@classmethod
- def _msvc_script_argument_sdk(cls, env, msvc, platform, arglist):
+ def _msvc_script_argument_sdk(cls, env, msvc, platform_type, arglist):
sdk_version = env['MSVC_SDK_VERSION']
debug(
- 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, platform=%s',
- repr(msvc.version), repr(sdk_version), repr(platform)
+ 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, platform_type=%s',
+ repr(msvc.version), repr(sdk_version), repr(platform_type)
)
if not sdk_version:
@@ -2341,11 +2339,11 @@ class _ScriptArguments:
if err_msg:
raise MSVCArgumentError(err_msg)
- sdk_list = _WindowsSDK._get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform)
+ sdk_list = _WindowsSDK._get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type)
if sdk_version not in sdk_list:
- err_msg = "MSVC_SDK_VERSION {} not found for platform {}".format(
- repr(sdk_version), repr(platform)
+ err_msg = "MSVC_SDK_VERSION {} not found for platform type {}".format(
+ repr(sdk_version), repr(platform_type)
)
raise MSVCArgumentError(err_msg)
@@ -2356,20 +2354,20 @@ class _ScriptArguments:
return sdk_version
@classmethod
- def _msvc_script_default_sdk(cls, env, msvc, platform, arglist):
+ def _msvc_script_default_sdk(cls, env, msvc, platform_type, arglist):
if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric:
return None
- sdk_list = _WindowsSDK._get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform)
+ sdk_list = _WindowsSDK._get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type)
if not len(sdk_list):
return None
sdk_default = sdk_list[0]
debug(
- 'MSVC_VERSION=%s, sdk_default=%s, platform=%s',
- repr(msvc.version), repr(sdk_default), repr(platform)
+ 'MSVC_VERSION=%s, sdk_default=%s, platform_type=%s',
+ repr(msvc.version), repr(sdk_default), repr(platform_type)
)
argpair = (cls.SortOrder.SDK, sdk_default)
@@ -2806,10 +2804,10 @@ class _ScriptArguments:
if user_argstr:
cls._user_script_argument_uwp(env, uwp, user_argstr)
- platform = 'uwp' if uwp else 'desktop'
+ platform_type = 'uwp' if uwp else 'desktop'
if 'MSVC_SDK_VERSION' in env:
- sdk_version = cls._msvc_script_argument_sdk(env, msvc, platform, arglist)
+ sdk_version = cls._msvc_script_argument_sdk(env, msvc, platform_type, arglist)
else:
sdk_version = None
@@ -2820,7 +2818,7 @@ class _ScriptArguments:
if cls.MSVC_FORCE_DEFAULT_SDK:
if not sdk_version and not user_sdk:
- sdk_version = cls._msvc_script_default_sdk(env, msvc, platform, arglist)
+ sdk_version = cls._msvc_script_default_sdk(env, msvc, platform_type, arglist)
if 'MSVC_TOOLSET_VERSION' in env:
toolset_version = cls._msvc_script_argument_toolset(env, msvc, vc_dir, arglist)
--
cgit v0.12
From 716f93a5207d8e7c941f3ff38ef2b3b0c923c479 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Thu, 16 Jun 2022 13:04:52 -0400
Subject: Update comments
---
SCons/Tool/MSCommon/vc.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index acffc7c..e29fba4 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -2269,7 +2269,7 @@ class _ScriptArguments:
# VS2017+ rewrites uwp => store for 14.0 toolset
uwp_arg = msvc.vs_def.vc_uwp
- # uwp may not be installed
+ # store/uwp may not be fully installed
argpair = (cls.SortOrder.UWP, uwp_arg)
arglist.append(argpair)
@@ -2347,7 +2347,6 @@ class _ScriptArguments:
)
raise MSVCArgumentError(err_msg)
- # sdk folder may not exist
argpair = (cls.SortOrder.SDK, sdk_version)
arglist.append(argpair)
--
cgit v0.12
From e2033e5f52002d404383c97e3a6a010efef174c8 Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Thu, 9 Jun 2022 11:30:47 -0600
Subject: Fortran vairants now include FORTRANCOMMONFLAGS
The documentation suggested $FORTRANFLAGS was included in the build
lines for all variants, but it was not. Turns out the test suite
quite explicitly checks that FORTRANFLAGS doesn't leak through to other
variants, so instead define a new FORTRANCOMMONFLAGS to serve the purpose
of a flag that applies to all variants.
Assorted cleanup. And f-strings.
Fixes #2257
Signed-off-by: Mats Wichmann
---
CHANGES.txt | 6 ++
RELEASE.txt | 2 +
SCons/Tool/FortranCommon.py | 178 ++++++++++++++++----------------
SCons/Tool/f03.xml | 5 +-
SCons/Tool/f08.xml | 5 +-
SCons/Tool/f77.xml | 7 +-
SCons/Tool/f90.xml | 5 +-
SCons/Tool/f95.xml | 5 +-
SCons/Tool/fortran.xml | 13 ++-
SCons/Tool/g77.py | 2 -
SCons/Tool/g77.xml | 5 +-
SCons/Tool/gfortran.py | 18 ++--
SCons/Tool/gfortran.xml | 19 +---
SCons/Tool/ifl.py | 21 ++--
SCons/Tool/ifort.py | 13 +--
test/Fortran/F77PATH.py | 78 ++++++--------
test/Fortran/F90PATH.py | 66 +++++-------
test/Fortran/FORTRANCOMMONFLAGS.py | 132 +++++++++++++++++++++++
test/Fortran/FORTRANFILESUFFIXES2.py | 43 ++++----
test/Fortran/FORTRANPATH.py | 54 +++++-----
test/Fortran/fixture/myfortran.py | 17 +--
test/Fortran/fixture/myfortran_flags.py | 17 +--
22 files changed, 406 insertions(+), 305 deletions(-)
create mode 100644 test/Fortran/FORTRANCOMMONFLAGS.py
diff --git a/CHANGES.txt b/CHANGES.txt
index 28785e1..d221603 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -194,6 +194,12 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
to add a path to the execution environment if it was discovered in
default_paths. Previously, the routine, called by many tool modules,
never altered the execution environment, leaving it to the tools.
+ - A new construction variable FORTRANCOMMONFLAGS is added which is
+ applied to all Fortran dialects, in case someone needs to set some
+ flags globally. FORTRANFLAGS looked like it was intended for that,
+ but was not applied to other dialects, and e2e tests explicitly checked
+ that FORTRANFLAGS did not propagate outside the FORTRAN dialect,
+ so the conclusion is that behavior is intentional (issue #2257)
From Zhichang Yu:
- Added MSVC_USE_SCRIPT_ARGS variable to pass arguments to MSVC_USE_SCRIPT.
diff --git a/RELEASE.txt b/RELEASE.txt
index abecf39..4f42e8c 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -43,6 +43,8 @@ NEW FUNCTIONALITY
is taken and the constructed environment is likely incomplete. As implemented, the default
global policy is "warning". The ability to set the global policy via an SCons command-line
option may be added in a future enhancement.
+- Fortran: a new construction variable FORTRANCOMMONFLAGS is added which is
+ applied to all Fortran dialects, to enable global (all-dialect) settings.
DEPRECATED FUNCTIONALITY
diff --git a/SCons/Tool/FortranCommon.py b/SCons/Tool/FortranCommon.py
index cad16b6..f889383 100644
--- a/SCons/Tool/FortranCommon.py
+++ b/SCons/Tool/FortranCommon.py
@@ -21,43 +21,48 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-"""
-
-Stuff for processing Fortran, common to all fortran dialects.
-
-"""
+"""Routines for setting up Fortran, common to all dialects."""
import re
import os.path
+from typing import Tuple
-import SCons.Action
import SCons.Scanner.Fortran
import SCons.Tool
import SCons.Util
+from SCons.Action import Action
+
+def isfortran(env, source) -> bool:
+ """Returns True if source has any fortran files in it.
-def isfortran(env, source):
- """Return 1 if any of code in source has fortran files in it, 0
- otherwise."""
+ Only checks based on filename suffixes, does not examine code.
+ """
try:
fsuffixes = env['FORTRANSUFFIXES']
except KeyError:
# If no FORTRANSUFFIXES, no fortran tool, so there is no need to look
# for fortran sources.
- return 0
+ return False
if not source:
# Source might be None for unusual cases like SConf.
- return 0
+ return False
for s in source:
if s.sources:
ext = os.path.splitext(str(s.sources[0]))[1]
if ext in fsuffixes:
- return 1
- return 0
+ return True
+ return False
-def _fortranEmitter(target, source, env):
+def _fortranEmitter(target, source, env) -> Tuple:
+ """Common code for Fortran emitter.
+
+ Called by both the static and shared object emitters,
+ mainly to account for generated module files.
+ """
+
node = source[0].rfile()
if not node.exists() and not node.is_derived():
print("Could not locate " + str(node.name))
@@ -78,21 +83,29 @@ def _fortranEmitter(target, source, env):
return (target, source)
-def FortranEmitter(target, source, env):
+def FortranEmitter(target, source, env) -> Tuple:
import SCons.Defaults
target, source = _fortranEmitter(target, source, env)
return SCons.Defaults.StaticObjectEmitter(target, source, env)
-def ShFortranEmitter(target, source, env):
+def ShFortranEmitter(target, source, env) -> Tuple:
import SCons.Defaults
target, source = _fortranEmitter(target, source, env)
return SCons.Defaults.SharedObjectEmitter(target, source, env)
-def ComputeFortranSuffixes(suffixes, ppsuffixes):
- """suffixes are fortran source files, and ppsuffixes the ones to be
- pre-processed. Both should be sequences, not strings."""
+def ComputeFortranSuffixes(suffixes, ppsuffixes) -> None:
+ """Update the suffix lists to reflect the platform requirements.
+
+ If upper-cased suffixes can be distinguished from lower, those are
+ added to *ppsuffixes*. If not, they are added to *suffixes*.
+
+ Args:
+ suffixes (list): indicate regular Fortran source files
+ ppsuffixes (list): indicate Fortran source files that should be
+ be run through the pre-processor
+ """
assert len(suffixes) > 0
s = suffixes[0]
sup = s.upper()
@@ -102,31 +115,34 @@ def ComputeFortranSuffixes(suffixes, ppsuffixes):
else:
suffixes.extend(upper_suffixes)
-
-def CreateDialectActions(dialect):
+def CreateDialectActions(dialect) -> Tuple[Action, Action, Action, Action]:
"""Create dialect specific actions."""
- CompAction = SCons.Action.Action('$%sCOM ' % dialect, '$%sCOMSTR' % dialect)
- CompPPAction = SCons.Action.Action('$%sPPCOM ' % dialect, '$%sPPCOMSTR' % dialect)
- ShCompAction = SCons.Action.Action('$SH%sCOM ' % dialect, '$SH%sCOMSTR' % dialect)
- ShCompPPAction = SCons.Action.Action('$SH%sPPCOM ' % dialect, '$SH%sPPCOMSTR' % dialect)
-
+ CompAction = Action(f'${dialect}COM ', cmdstr=f'${dialect}COMSTR')
+ CompPPAction = Action(f'${dialect}PPCOM ', cmdstr=f'${dialect}PPCOMSTR')
+ ShCompAction = Action(f'$SH{dialect}COM ', cmdstr=f'$SH{dialect}COMSTR')
+ ShCompPPAction = Action(f'$SH{dialect}PPCOM ', cmdstr=f'$SH{dialect}PPCOMSTR')
return CompAction, CompPPAction, ShCompAction, ShCompPPAction
-def DialectAddToEnv(env, dialect, suffixes, ppsuffixes, support_module=False):
- """Add dialect specific construction variables."""
- ComputeFortranSuffixes(suffixes, ppsuffixes)
+def DialectAddToEnv(env, dialect, suffixes, ppsuffixes, support_mods=False) -> None:
+ """Add dialect specific construction variables.
- fscan = SCons.Scanner.Fortran.FortranScan("%sPATH" % dialect)
+ Args:
+ dialect (str): dialect name
+ suffixes (list): suffixes associated with this dialect
+ ppsuffixes (list): suffixes using cpp associated with this dialect
+ support_mods (bool): whether this dialect supports modules
+ """
+ ComputeFortranSuffixes(suffixes, ppsuffixes)
+ fscan = SCons.Scanner.Fortran.FortranScan(f"{dialect}PATH")
for suffix in suffixes + ppsuffixes:
SCons.Tool.SourceFileScanner.add_scanner(suffix, fscan)
- env.AppendUnique(FORTRANSUFFIXES = suffixes + ppsuffixes)
+ env.AppendUnique(FORTRANSUFFIXES=suffixes + ppsuffixes)
compaction, compppaction, shcompaction, shcompppaction = \
CreateDialectActions(dialect)
-
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
for suffix in suffixes:
@@ -141,64 +157,60 @@ def DialectAddToEnv(env, dialect, suffixes, ppsuffixes, support_module=False):
static_obj.add_emitter(suffix, FortranEmitter)
shared_obj.add_emitter(suffix, ShFortranEmitter)
- if '%sFLAGS' % dialect not in env:
- env['%sFLAGS' % dialect] = SCons.Util.CLVar('')
-
- if 'SH%sFLAGS' % dialect not in env:
- env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect)
+ if f'{dialect}FLAGS' not in env:
+ env[f'{dialect}FLAGS'] = SCons.Util.CLVar('')
+ if f'SH{dialect}FLAGS' not in env:
+ env[f'SH{dialect}FLAGS'] = SCons.Util.CLVar(f'${dialect}FLAGS')
# If a tool does not define fortran prefix/suffix for include path, use C ones
- if 'INC%sPREFIX' % dialect not in env:
- env['INC%sPREFIX' % dialect] = '$INCPREFIX'
-
- if 'INC%sSUFFIX' % dialect not in env:
- env['INC%sSUFFIX' % dialect] = '$INCSUFFIX'
-
- env['_%sINCFLAGS' % dialect] = '${_concat(INC%sPREFIX, %sPATH, INC%sSUFFIX, __env__, RDirs, TARGET, SOURCE, affect_signature=False)}' % (dialect, dialect, dialect)
-
- if support_module:
- env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
- env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
- env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
- env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
+ if f'INC{dialect}PREFIX' not in env:
+ env[f'INC{dialect}PREFIX'] = '$INCPREFIX'
+ if f'INC{dialect}SUFFIX' not in env:
+ env[f'INC{dialect}SUFFIX'] = '$INCSUFFIX'
+
+ env[f'_{dialect}INCFLAGS'] = f'${{_concat(INC{dialect}PREFIX, {dialect}PATH, INC{dialect}SUFFIX, __env__, RDirs, TARGET, SOURCE, affect_signature=False)}}'
+
+ if support_mods:
+ env[f'{dialect}COM'] = f'${dialect} -o $TARGET -c $FORTRANCOMMONFLAGS ${dialect}FLAGS $_{dialect}INCFLAGS $_FORTRANMODFLAG $SOURCES #XXX{dialect}'
+ env[f'{dialect}PPCOM'] = f'${dialect} -o $TARGET -c $FORTRANCOMMONFLAGS ${dialect}FLAGS $CPPFLAGS $_CPPDEFFLAGS $_{dialect}INCFLAGS $_FORTRANMODFLAG $SOURCES #XXXPP{dialect}'
+ env[f'SH{dialect}COM'] = f'$SH{dialect} -o $TARGET -c $FORTRANCOMMONFLAGS $SH{dialect}FLAGS $_{dialect}INCFLAGS $_FORTRANMODFLAG $SOURCES #XXX{dialect}'
+ env[f'SH{dialect}PPCOM'] = f'$SH{dialect} -o $TARGET -c $FORTRANCOMMONFLAGS $SH{dialect}FLAGS $CPPFLAGS $_CPPDEFFLAGS $_{dialect}INCFLAGS $_FORTRANMODFLAG $SOURCES #XXXPP{dialect}'
else:
- env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
- env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
- env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
- env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
+ env[f'{dialect}COM'] = f'${dialect} -o $TARGET -c $FORTRANCOMMONFLAGS ${dialect}FLAGS $_{dialect}INCFLAGS $SOURCES #XXX{dialect}'
+ env[f'{dialect}PPCOM'] = f'${dialect} -o $TARGET -c $FORTRANCOMMONFLAGS ${dialect}FLAGS $CPPFLAGS $_CPPDEFFLAGS $_{dialect}INCFLAGS $SOURCES #XXXPP{dialect}'
+ env[f'SH{dialect}COM'] = f'$SH{dialect} -o $TARGET -c $FORTRANCOMMONFLAGS $SH{dialect}FLAGS $_{dialect}INCFLAGS $SOURCES #XXX{dialect}'
+ env[f'SH{dialect}PPCOM'] = f'$SH{dialect} -o $TARGET -c $FORTRANCOMMONFLAGS $SH{dialect}FLAGS $CPPFLAGS $_CPPDEFFLAGS $_{dialect}INCFLAGS $SOURCES #XXXPP{dialect}'
+
-def add_fortran_to_env(env):
- """Add Builders and construction variables for Fortran to an Environment."""
+def add_fortran_to_env(env) -> None:
+ """Add Builders and construction variables for Fortran/generic."""
try:
FortranSuffixes = env['FORTRANFILESUFFIXES']
except KeyError:
FortranSuffixes = ['.f', '.for', '.ftn']
- #print("Adding %s to fortran suffixes" % FortranSuffixes)
try:
FortranPPSuffixes = env['FORTRANPPFILESUFFIXES']
except KeyError:
FortranPPSuffixes = ['.fpp', '.FPP']
- DialectAddToEnv(env, "FORTRAN", FortranSuffixes,
- FortranPPSuffixes, support_module=True)
+ DialectAddToEnv(env, "FORTRAN", FortranSuffixes, FortranPPSuffixes, support_mods=True)
+ # Module support
env['FORTRANMODPREFIX'] = '' # like $LIBPREFIX
env['FORTRANMODSUFFIX'] = '.mod' # like $LIBSUFFIX
-
env['FORTRANMODDIR'] = '' # where the compiler should place .mod files
env['FORTRANMODDIRPREFIX'] = '' # some prefix to $FORTRANMODDIR - similar to $INCPREFIX
env['FORTRANMODDIRSUFFIX'] = '' # some suffix to $FORTRANMODDIR - similar to $INCSUFFIX
env['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
-def add_f77_to_env(env):
- """Add Builders and construction variables for f77 to an Environment."""
+def add_f77_to_env(env) -> None:
+ """Add Builders and construction variables for f77 dialect."""
try:
F77Suffixes = env['F77FILESUFFIXES']
except KeyError:
F77Suffixes = ['.f77']
- #print("Adding %s to f77 suffixes" % F77Suffixes)
try:
F77PPSuffixes = env['F77PPFILESUFFIXES']
except KeyError:
@@ -206,59 +218,50 @@ def add_f77_to_env(env):
DialectAddToEnv(env, "F77", F77Suffixes, F77PPSuffixes)
-def add_f90_to_env(env):
- """Add Builders and construction variables for f90 to an Environment."""
+def add_f90_to_env(env) -> None:
+ """Add Builders and construction variables for f90 dialect."""
try:
F90Suffixes = env['F90FILESUFFIXES']
except KeyError:
F90Suffixes = ['.f90']
- #print("Adding %s to f90 suffixes" % F90Suffixes)
try:
F90PPSuffixes = env['F90PPFILESUFFIXES']
except KeyError:
F90PPSuffixes = []
- DialectAddToEnv(env, "F90", F90Suffixes, F90PPSuffixes,
- support_module=True)
-
+ DialectAddToEnv(env, "F90", F90Suffixes, F90PPSuffixes, support_mods=True)
-def add_f95_to_env(env):
- """Add Builders and construction variables for f95 to an Environment."""
+def add_f95_to_env(env) -> None:
+ """Add Builders and construction variables for f95 dialect."""
try:
F95Suffixes = env['F95FILESUFFIXES']
except KeyError:
F95Suffixes = ['.f95']
- #print("Adding %s to f95 suffixes" % F95Suffixes)
try:
F95PPSuffixes = env['F95PPFILESUFFIXES']
except KeyError:
F95PPSuffixes = []
- DialectAddToEnv(env, "F95", F95Suffixes, F95PPSuffixes,
- support_module=True)
-
+ DialectAddToEnv(env, "F95", F95Suffixes, F95PPSuffixes, support_mods=True)
-def add_f03_to_env(env):
- """Add Builders and construction variables for f03 to an Environment."""
+def add_f03_to_env(env) -> None:
+ """Add Builders and construction variables for f03 dialect."""
try:
F03Suffixes = env['F03FILESUFFIXES']
except KeyError:
F03Suffixes = ['.f03']
- #print("Adding %s to f95 suffixes" % F95Suffixes)
try:
F03PPSuffixes = env['F03PPFILESUFFIXES']
except KeyError:
F03PPSuffixes = []
- DialectAddToEnv(env, "F03", F03Suffixes, F03PPSuffixes,
- support_module=True)
+ DialectAddToEnv(env, "F03", F03Suffixes, F03PPSuffixes, support_mods=True)
-
-def add_f08_to_env(env):
- """Add Builders and construction variables for f08 to an Environment."""
+def add_f08_to_env(env) -> None:
+ """Add Builders and construction variables for f08 dialect."""
try:
F08Suffixes = env['F08FILESUFFIXES']
except KeyError:
@@ -269,13 +272,10 @@ def add_f08_to_env(env):
except KeyError:
F08PPSuffixes = []
- DialectAddToEnv(env, "F08", F08Suffixes, F08PPSuffixes,
- support_module=True)
-
+ DialectAddToEnv(env, "F08", F08Suffixes, F08PPSuffixes, support_mods=True)
-def add_all_to_env(env):
- """Add builders and construction variables for all supported fortran
- dialects."""
+def add_all_to_env(env) -> None:
+ """Add builders and construction variables for all supported dialects."""
add_fortran_to_env(env)
add_f77_to_env(env)
add_f90_to_env(env)
diff --git a/SCons/Tool/f03.xml b/SCons/Tool/f03.xml
index fc14ace..c989385 100644
--- a/SCons/Tool/f03.xml
+++ b/SCons/Tool/f03.xml
@@ -1,6 +1,6 @@
@@ -5585,38 +5605,37 @@ and the list of sources for this builder.
def e(target, source, env):
- return (target + ['foo.foo'], source + ['foo.src'])
+ return target + ['foo.foo'], source + ['foo.src']
# Simple association of an emitter function with a Builder.
-b = Builder("my_build < $TARGET > $SOURCE",
- emitter = e)
+b = Builder("my_build < $TARGET > $SOURCE", emitter=e)
def e2(target, source, env):
- return (target + ['bar.foo'], source + ['bar.src'])
+ return target + ['bar.foo'], source + ['bar.src']
# Simple association of a list of emitter functions with a Builder.
-b = Builder("my_build < $TARGET > $SOURCE",
- emitter = [e, e2])
+b = Builder("my_build < $TARGET > $SOURCE", emitter=[e, e2])
-# Calling an emitter function through a &consvar;.
+# Calling an emitter function through a construction variable.
env = Environment(MY_EMITTER=e)
-b = Builder("my_build < $TARGET > $SOURCE",
- emitter='$MY_EMITTER')
+b = Builder("my_build < $TARGET > $SOURCE", emitter='$MY_EMITTER')
-# Calling a list of emitter functions through a &consvar;.
+# Calling a list of emitter functions through a construction variable.
env = Environment(EMITTER_LIST=[e, e2])
-b = Builder("my_build < $TARGET > $SOURCE",
- emitter='$EMITTER_LIST')
+b = Builder("my_build < $TARGET > $SOURCE", emitter='$EMITTER_LIST')
# Associating multiple emitters with different file
# suffixes using a dictionary.
def e_suf1(target, source, env):
- return (target + ['another_target_file'], source)
+ return target + ['another_target_file'], source
+
def e_suf2(target, source, env):
- return (target, source + ['another_source_file'])
-b = Builder("my_build < $TARGET > $SOURCE",
- emitter={'.suf1' : e_suf1,
- '.suf2' : e_suf2})
+ return target, source + ['another_source_file']
+
+b = Builder(
+ action="my_build < $TARGET > $SOURCE",
+ emitter={'.suf1': e_suf1, '.suf2': e_suf2}
+)
--
cgit v0.12
From 869c75679492039a5dcb1f66fd9a78d59a4340da Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Mon, 20 Jun 2022 08:58:35 -0600
Subject: A few more tweaks in Builder Methods intro [skip appveyor]
Signed-off-by: Mats Wichmann
---
doc/man/scons.xml | 50 ++++++++++++++++++++++++--------------------------
1 file changed, 24 insertions(+), 26 deletions(-)
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index 8e64290..2e7784c 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -2675,7 +2675,7 @@ and a same-named environment method
that splits a single string
into a list, using
strings of white-space characters as the delimiter
-(similar to the Python string split
+(similar to the &Python; string split
method, but succeeds even if the input isn't a string).
@@ -2697,7 +2697,7 @@ env.Program('bar', source='bar.c foo.c'.split())
Sources and targets can be specified as a a scalar or as a list,
either of strings or nodes (more on nodes below).
When specifying path strings,
-Python follows the POSIX pathname convention:
+&Python; follows the POSIX pathname convention:
if a string begins with the operating system pathname separator
(on Windows both the slash and backslash separator are accepted,
and any leading drive specifier is ignored for
@@ -2806,7 +2806,7 @@ env.Program('build/prog', ['f1.c', 'f2.c'], srcdir='src')
The optional
parse_flags
-keyword argument causes behavior similarly to the
+keyword argument causes behavior similarl to the
&f-link-env-MergeFlags; method, where the argument value is
broken into individual settings and merged into the appropriate &consvars;.
@@ -2861,15 +2861,13 @@ env.Command('sub/dir/foo.out', 'sub/dir/foo.in', "cp foo.in foo.out", chdir=True
not
automatically modify
its expansion of
-&consvars; like
-$TARGET
-and
-$SOURCE
+&consvars; like &cv-link-TARGET;
+and &cv-link-SOURCE;
when using the chdir
keyword argument--that is,
the expanded file names
will still be relative to
-the top-level directory where &SConstruct; was found,
+the top-level directory where the &SConstruct; was found,
and consequently incorrect
relative to the chdir directory.
If you use the chdir keyword argument,
@@ -2880,7 +2878,7 @@ expansions like
and
${SOURCE.file}
to use just the filename portion of the
-targets and source.
+target and source.
Keyword arguments that are not specifically
recognized are treated as &consvar;
@@ -2911,7 +2909,7 @@ env.SharedLibrary(
and &cv-link-LIBSUFFIXES;
&consvars; must be set if you want &scons; to search automatically
for dependencies on the non-standard library names;
-see the descriptions below of these variables for more information.
+see the descriptions of these variables for more information.
Although the builder methods defined by
&scons;
@@ -2930,11 +2928,11 @@ SharedLibrary('word', 'word.cpp')
has determined are appropriate for the local system.Builder methods that can be called without an explicit
-environment (indicated in the listing of builders without
-a leading env.)
-may be called from custom Python modules that you
+environment (indicated in the listing of builders below
+without a leading env.)
+may be called from custom &Python; modules that you
import into an SConscript file by adding the following
-to the Python module:
+to the &Python; module:
from SCons.Script import *
@@ -2942,17 +2940,17 @@ from SCons.Script import *
A builder may add additional targets
-(and, in fact, sources) beyond those requested,
+beyond those requested
if an attached Emitter chooses to do so
-(see and, as an example,
-&cv-link-PROGEMITTER;, for more information).
+(see for more information.
+&cv-link-PROGEMITTER; is an example).
For example, the GNU linker takes a command-line argument
,
which causes it to produce a linker map file in addition
to the executable file actually being linked.
If the &b-link-Program; builder's emitter is configured
to add this mapfile if the option is set,
-two targets will be returned when you only asked for one.
+then two targets will be returned when you only asked for one.
@@ -3000,7 +2998,7 @@ contain elements which are themselves lists, such as
bar_obj_list
returned by the &b-link-StaticObject; call.
If you need to manipulate a list of lists returned by builders
-directly in Python code,
+directly in &Python; code,
you can either build a new list by hand:
@@ -3025,19 +3023,19 @@ for obj in objects:
Since builder calls return
-a list-like object, not an actual Python list,
-it is not appropriate to use the Python add
+a list-like object, not an actual &Python; list,
+it is not appropriate to use the &Python; add
operator (+ or +=)
-to append builder results to a Python list.
+to append builder results to a &Python; list.
Because the list and the object are different types,
-Python will not update the original list in place,
+&Python; will not update the original list in place,
but will instead create a new NodeList object
containing the concatenation of the list
elements and the builder results.
-This will cause problems for any other Python variables
+This will cause problems for any other &Python; variables
in your SCons configuration
that still hold on to a reference to the original list.
-Instead, use the Python list
+Instead, use the &Python; list
extend
method to make sure the list is updated in-place.
Example:
@@ -3055,7 +3053,7 @@ object_files.extend(Object('bar.c'))
The path name for a Node's file may be used
-by passing the Node to Python's builtin
+by passing the Node to &Python;'s builtin
str
function:
--
cgit v0.12
From 94b23f77472aabaabc372b0facb26c4f384413c5 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Mon, 20 Jun 2022 12:11:24 -0400
Subject: Rename _Const to _Config. Rework msvc sdk version function. Minor
cleanup.
---
SCons/Tool/MSCommon/__init__.py | 1 +
SCons/Tool/MSCommon/vc.py | 68 ++++++++++++++++++++++-------------------
2 files changed, 37 insertions(+), 32 deletions(-)
diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py
index 9d8a8ff..de78f84 100644
--- a/SCons/Tool/MSCommon/__init__.py
+++ b/SCons/Tool/MSCommon/__init__.py
@@ -40,6 +40,7 @@ from SCons.Tool.MSCommon.vc import (
msvc_find_vswhere,
set_msvc_notfound_policy,
get_msvc_notfound_policy,
+ get_msvc_sdk_versions,
)
from SCons.Tool.MSCommon.vs import (
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index e193d91..3abd606 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -132,7 +132,7 @@ class _Dispatcher:
func = getattr(classref, method)
func()
-class _Const:
+class _Config:
BOOLEAN_SYMBOLS = {}
BOOLEAN_EXTERNAL = {}
@@ -1255,6 +1255,12 @@ def reset_installed_vcs():
__INSTALLED_VCS_RUN = None
_Dispatcher.reset()
+def get_default_installed_msvc(env=None):
+ vcs = get_installed_vcs(env)
+ msvc_version = vcs[0] if vcs else None
+ debug('msvc_version=%s', repr(msvc_version))
+ return msvc_version
+
# Running these batch files isn't cheap: most of the time spent in
# msvs.generate() is due to vcvars*.bat. In a build that uses "tools='msvs'"
# in multiple environments, for example:
@@ -1648,13 +1654,11 @@ def get_default_version(env):
return msvs_version
if not msvc_version:
- installed_vcs = get_installed_vcs(env)
- debug('installed_vcs:%s', installed_vcs)
- if not installed_vcs:
+ msvc_version = get_default_installed_msvc(env)
+ if not msvc_version:
#SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
debug('No installed VCs')
return None
- msvc_version = installed_vcs[0]
debug('using default installed MSVC version %s', repr(msvc_version))
else:
debug('using specified MSVC version %s', repr(msvc_version))
@@ -1769,13 +1773,9 @@ def msvc_find_valid_batch_script(env, version):
return d
-_undefined = None
+_UNDEFINED = object()
def get_use_script_use_settings(env):
- global _undefined
-
- if _undefined is None:
- _undefined = object()
# use_script use_settings return values action
# value ignored (value, None) use script or bypass detection
@@ -1784,9 +1784,9 @@ def get_use_script_use_settings(env):
# None (documentation) or evaluates False (code): bypass detection
# need to distinguish between undefined and None
- use_script = env.get('MSVC_USE_SCRIPT', _undefined)
+ use_script = env.get('MSVC_USE_SCRIPT', _UNDEFINED)
- if use_script != _undefined:
+ if use_script != _UNDEFINED:
# use_script defined, use_settings ignored (not type checked)
return (use_script, None)
@@ -1908,6 +1908,21 @@ def msvc_setup_env_tool(env=None, version=None, tool=None):
debug('tool=%s, version=%s, return=%s', repr(tool), repr(version), rval)
return rval
+def get_msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False):
+ debug('msvc_version=%s, msvc_uwp_app=%s', repr(msvc_version), repr(msvc_uwp_app))
+
+ rval = []
+
+ if not msvc_version:
+ msvc_version = get_default_installed_msvc()
+
+ if not msvc_version:
+ debug('no msvc versions detected')
+ return rval
+
+ rval = MSVC.WinSDK.get_msvc_sdk_version_list(msvc_version, msvc_uwp_app)
+ return rval
+
class _Util:
@staticmethod
@@ -2158,7 +2173,7 @@ class _WindowsSDK:
def _verify_sdk_dispatch_map(cls):
debug('%s verify sdk_dispatch_map', cls.__name__)
cls._init_sdk_dispatch_map()
- for sdk_version in _Const.MSVC_SDK_VERSIONS:
+ for sdk_version in _Config.MSVC_SDK_VERSIONS:
if sdk_version in cls.sdk_dispatch_map:
continue
err_msg = 'sdk version {} not in {}.sdk_dispatch_map'.format(sdk_version, cls.__name__)
@@ -2205,20 +2220,13 @@ class _WindowsSDK:
sdk_versions = []
- if not msvc_version:
- vcs = get_installed_vcs()
- if not vcs:
- debug('no msvc versions detected')
- return sdk_versions
- msvc_version = vcs[0]
-
verstr = get_msvc_version_numeric(msvc_version)
- vs_def = _Const.MSVC_VERSION_EXTERNAL.get(verstr, None)
+ vs_def = _Config.MSVC_VERSION_EXTERNAL.get(verstr, None)
if not vs_def:
debug('vs_def is not defined')
return sdk_versions
- is_uwp = True if msvc_uwp_app in _Const.BOOLEAN_SYMBOLS[True] else False
+ is_uwp = True if msvc_uwp_app in _Config.BOOLEAN_SYMBOLS[True] else False
platform_type = 'uwp' if is_uwp else 'desktop'
sdk_list = _WindowsSDK.get_sdk_version_list(vs_def.vc_sdk_versions, platform_type)
@@ -2239,10 +2247,6 @@ class _WindowsSDK:
_Dispatcher.register(_WindowsSDK)
-def get_sdk_versions(MSVC_VERSION=None, MSVC_UWP_APP=False):
- debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(MSVC_VERSION), repr(MSVC_UWP_APP))
- return _WindowsSDK.get_msvc_sdk_version_list(msvc_version=MSVC_VERSION, msvc_uwp_app=MSVC_UWP_APP)
-
class _ScriptArguments:
# TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last?
@@ -2257,7 +2261,7 @@ class _ScriptArguments:
@classmethod
def _verify_re_sdk_dispatch_map(cls):
debug('%s verify re_sdk_dispatch_map', cls.__name__)
- for sdk_version in _Const.MSVC_SDK_VERSIONS:
+ for sdk_version in _Config.MSVC_SDK_VERSIONS:
if sdk_version in cls.re_sdk_dispatch_map:
continue
err_msg = 'sdk version {} not in {}.re_sdk_dispatch_map'.format(sdk_version, cls.__name__)
@@ -2315,9 +2319,9 @@ class _ScriptArguments:
SPECTRE = 4 # MSVC_SPECTRE_LIBS
USER = 5 # MSVC_SCRIPT_ARGS
- VS2019 = _Const.MSVS_VERSION_INTERNAL['2019']
- VS2017 = _Const.MSVS_VERSION_INTERNAL['2017']
- VS2015 = _Const.MSVS_VERSION_INTERNAL['2015']
+ VS2019 = _Config.MSVS_VERSION_INTERNAL['2019']
+ VS2017 = _Config.MSVS_VERSION_INTERNAL['2017']
+ VS2015 = _Config.MSVS_VERSION_INTERNAL['2015']
MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [
'version', # fully qualified msvc version (e.g., '14.1Exp')
@@ -2328,7 +2332,7 @@ class _ScriptArguments:
def _msvc_version(cls, version):
verstr = get_msvc_version_numeric(version)
- vs_def = _Const.MSVC_VERSION_INTERNAL[verstr]
+ vs_def = _Config.MSVC_VERSION_INTERNAL[verstr]
version_args = cls.MSVC_VERSION_ARGS_DEFINITION(
version = version,
@@ -2346,7 +2350,7 @@ class _ScriptArguments:
if not uwp_app:
return None
- if uwp_app not in _Const.BOOLEAN_SYMBOLS[True]:
+ if uwp_app not in _Config.BOOLEAN_SYMBOLS[True]:
return None
if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric:
--
cgit v0.12
From dbd301878f8f7bb004658b25a62d57b48ee4c8a3 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Mon, 20 Jun 2022 12:19:39 -0400
Subject: Fix windows SDK reference
---
SCons/Tool/MSCommon/vc.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index 3abd606..ab05323 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -1920,7 +1920,7 @@ def get_msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False):
debug('no msvc versions detected')
return rval
- rval = MSVC.WinSDK.get_msvc_sdk_version_list(msvc_version, msvc_uwp_app)
+ rval = _WindowsSDK.get_msvc_sdk_version_list(msvc_version, msvc_uwp_app)
return rval
class _Util:
--
cgit v0.12
From 010c2ad46c32827b7ed8082ec11b83404a039faa Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Mon, 20 Jun 2022 16:53:31 -0400
Subject: Refactor recent portions of vc.py into MSVC module
---
SCons/Tool/MSCommon/MSVC/Config.py | 284 +++++
SCons/Tool/MSCommon/MSVC/Dispatcher.py | 62 +
SCons/Tool/MSCommon/MSVC/Exceptions.py | 39 +
SCons/Tool/MSCommon/MSVC/NotFound.py | 132 +++
SCons/Tool/MSCommon/MSVC/Registry.py | 110 ++
SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 733 ++++++++++++
SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py | 235 ++++
SCons/Tool/MSCommon/MSVC/Util.py | 55 +
SCons/Tool/MSCommon/MSVC/WinSDK.py | 250 ++++
SCons/Tool/MSCommon/MSVC/__init__.py | 50 +
SCons/Tool/MSCommon/__init__.py | 7 +-
SCons/Tool/MSCommon/vc.py | 1638 +--------------------------
12 files changed, 1968 insertions(+), 1627 deletions(-)
create mode 100644 SCons/Tool/MSCommon/MSVC/Config.py
create mode 100644 SCons/Tool/MSCommon/MSVC/Dispatcher.py
create mode 100644 SCons/Tool/MSCommon/MSVC/Exceptions.py
create mode 100644 SCons/Tool/MSCommon/MSVC/NotFound.py
create mode 100644 SCons/Tool/MSCommon/MSVC/Registry.py
create mode 100644 SCons/Tool/MSCommon/MSVC/ScriptArguments.py
create mode 100644 SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py
create mode 100644 SCons/Tool/MSCommon/MSVC/Util.py
create mode 100644 SCons/Tool/MSCommon/MSVC/WinSDK.py
create mode 100644 SCons/Tool/MSCommon/MSVC/__init__.py
diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py
new file mode 100644
index 0000000..476dcb3
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/Config.py
@@ -0,0 +1,284 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""
+Constants and initialized data structures for Microsoft Visual C/C++.
+"""
+
+from collections import (
+ namedtuple,
+)
+
+from . import Dispatcher
+
+Dispatcher.register_modulename(__name__)
+
+UNDEFINED = object()
+
+BOOLEAN_SYMBOLS = {}
+BOOLEAN_EXTERNAL = {}
+
+for bool, symbol_list, symbol_case_list in [
+ (False, (False, 0, '0', None, ''), ('False', 'No', 'F', 'N')),
+ (True, (True, 1, '1'), ('True', 'Yes', 'T', 'Y')),
+]:
+ BOOLEAN_SYMBOLS[bool] = list(symbol_list)
+ for symbol in symbol_case_list:
+ BOOLEAN_SYMBOLS[bool].extend([symbol, symbol.lower(), symbol.upper()])
+
+ for symbol in BOOLEAN_SYMBOLS[bool]:
+ BOOLEAN_EXTERNAL[symbol] = bool
+
+MSVC_RUNTIME_DEFINITION = namedtuple('MSVCRuntime', [
+ 'vc_runtime',
+ 'vc_runtime_numeric',
+ 'vc_runtime_alias_list',
+ 'vc_runtime_vsdef_list',
+])
+
+MSVC_RUNTIME_DEFINITION_LIST = []
+
+MSVC_RUNTIME_INTERNAL = {}
+MSVC_RUNTIME_EXTERNAL = {}
+
+for vc_runtime, vc_runtime_numeric, vc_runtime_alias_list in [
+ ('140', 140, ['ucrt']),
+ ('120', 120, ['msvcr120']),
+ ('110', 110, ['msvcr110']),
+ ('100', 100, ['msvcr100']),
+ ('90', 90, ['msvcr90']),
+ ('80', 80, ['msvcr80']),
+ ('71', 71, ['msvcr71']),
+ ('70', 70, ['msvcr70']),
+ ('60', 60, ['msvcrt']),
+]:
+ vc_runtime_def = MSVC_RUNTIME_DEFINITION(
+ vc_runtime = vc_runtime,
+ vc_runtime_numeric = vc_runtime_numeric,
+ vc_runtime_alias_list = vc_runtime_alias_list,
+ vc_runtime_vsdef_list = [],
+ )
+
+ MSVC_RUNTIME_DEFINITION_LIST.append(vc_runtime_def)
+
+ MSVC_RUNTIME_INTERNAL[vc_runtime] = vc_runtime_def
+ MSVC_RUNTIME_EXTERNAL[vc_runtime] = vc_runtime_def
+
+ for vc_runtime_alias in vc_runtime_alias_list:
+ MSVC_RUNTIME_EXTERNAL[vc_runtime_alias] = vc_runtime_def
+
+MSVC_BUILDTOOLS_DEFINITION = namedtuple('MSVCBuildtools', [
+ 'vc_buildtools',
+ 'vc_buildtools_numeric',
+ 'vc_version',
+ 'vc_version_numeric',
+ 'cl_version',
+ 'cl_version_numeric',
+ 'vc_runtime_def',
+])
+
+MSVC_BUILDTOOLS_DEFINITION_LIST = []
+
+MSVC_BUILDTOOLS_INTERNAL = {}
+MSVC_BUILDTOOLS_EXTERNAL = {}
+
+VC_VERSION_MAP = {}
+
+for vc_buildtools, vc_version, cl_version, vc_runtime in [
+ ('v143', '14.3', '19.3', '140'),
+ ('v142', '14.2', '19.2', '140'),
+ ('v141', '14.1', '19.1', '140'),
+ ('v140', '14.0', '19.0', '140'),
+ ('v120', '12.0', '18.0', '120'),
+ ('v110', '11.0', '17.0', '110'),
+ ('v100', '10.0', '16.0', '100'),
+ ('v90', '9.0', '15.0', '90'),
+ ('v80', '8.0', '14.0', '80'),
+ ('v71', '7.1', '13.1', '71'),
+ ('v70', '7.0', '13.0', '70'),
+ ('v60', '6.0', '12.0', '60'),
+]:
+
+ vc_runtime_def = MSVC_RUNTIME_INTERNAL[vc_runtime]
+
+ vc_buildtools_def = MSVC_BUILDTOOLS_DEFINITION(
+ vc_buildtools = vc_buildtools,
+ vc_buildtools_numeric = int(vc_buildtools[1:]),
+ vc_version = vc_version,
+ vc_version_numeric = float(vc_version),
+ cl_version = cl_version,
+ cl_version_numeric = float(cl_version),
+ vc_runtime_def = vc_runtime_def,
+ )
+
+ MSVC_BUILDTOOLS_DEFINITION_LIST.append(vc_buildtools_def)
+
+ MSVC_BUILDTOOLS_INTERNAL[vc_buildtools] = vc_buildtools_def
+ MSVC_BUILDTOOLS_EXTERNAL[vc_buildtools] = vc_buildtools_def
+ MSVC_BUILDTOOLS_EXTERNAL[vc_version] = vc_buildtools_def
+
+ VC_VERSION_MAP[vc_version] = vc_buildtools_def
+
+MSVS_VERSION_INTERNAL = {}
+MSVS_VERSION_EXTERNAL = {}
+
+MSVC_VERSION_INTERNAL = {}
+MSVC_VERSION_EXTERNAL = {}
+
+MSVS_VERSION_MAJOR_MAP = {}
+
+CL_VERSION_MAP = {}
+
+MSVC_SDK_VERSIONS = set()
+
+VISUALSTUDIO_DEFINITION = namedtuple('VisualStudioDefinition', [
+ 'vs_product',
+ 'vs_product_alias_list',
+ 'vs_version',
+ 'vs_version_major',
+ 'vs_envvar',
+ 'vs_express',
+ 'vs_lookup',
+ 'vc_sdk_versions',
+ 'vc_ucrt_versions',
+ 'vc_uwp',
+ 'vc_buildtools_def',
+ 'vc_buildtools_all',
+])
+
+VISUALSTUDIO_DEFINITION_LIST = []
+
+VS_PRODUCT_ALIAS = {
+ '1998': ['6']
+}
+
+# vs_envvar: VisualStudioVersion defined in environment for MSVS 2012 and later
+# MSVS 2010 and earlier cl_version -> vs_def is a 1:1 mapping
+# SDK attached to product or buildtools?
+for vs_product, vs_version, vs_envvar, vs_express, vs_lookup, vc_sdk, vc_ucrt, vc_uwp, vc_buildtools_all in [
+ ('2022', '17.0', True, False, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v143', 'v142', 'v141', 'v140']),
+ ('2019', '16.0', True, False, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v142', 'v141', 'v140']),
+ ('2017', '15.0', True, True, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v141', 'v140']),
+ ('2015', '14.0', True, True, 'registry', ['10.0', '8.1'], ['10'], 'store', ['v140']),
+ ('2013', '12.0', True, True, 'registry', None, None, None, ['v120']),
+ ('2012', '11.0', True, True, 'registry', None, None, None, ['v110']),
+ ('2010', '10.0', False, True, 'registry', None, None, None, ['v100']),
+ ('2008', '9.0', False, True, 'registry', None, None, None, ['v90']),
+ ('2005', '8.0', False, True, 'registry', None, None, None, ['v80']),
+ ('2003', '7.1', False, False, 'registry', None, None, None, ['v71']),
+ ('2002', '7.0', False, False, 'registry', None, None, None, ['v70']),
+ ('1998', '6.0', False, False, 'registry', None, None, None, ['v60']),
+]:
+
+ vs_version_major = vs_version.split('.')[0]
+
+ vc_buildtools_def = MSVC_BUILDTOOLS_INTERNAL[vc_buildtools_all[0]]
+
+ vs_def = VISUALSTUDIO_DEFINITION(
+ vs_product = vs_product,
+ vs_product_alias_list = [],
+ vs_version = vs_version,
+ vs_version_major = vs_version_major,
+ vs_envvar = vs_envvar,
+ vs_express = vs_express,
+ vs_lookup = vs_lookup,
+ vc_sdk_versions = vc_sdk,
+ vc_ucrt_versions = vc_ucrt,
+ vc_uwp = vc_uwp,
+ vc_buildtools_def = vc_buildtools_def,
+ vc_buildtools_all = vc_buildtools_all,
+ )
+
+ VISUALSTUDIO_DEFINITION_LIST.append(vs_def)
+
+ vc_buildtools_def.vc_runtime_def.vc_runtime_vsdef_list.append(vs_def)
+
+ MSVS_VERSION_INTERNAL[vs_product] = vs_def
+ MSVS_VERSION_EXTERNAL[vs_product] = vs_def
+ MSVS_VERSION_EXTERNAL[vs_version] = vs_def
+
+ MSVC_VERSION_INTERNAL[vc_buildtools_def.vc_version] = vs_def
+ MSVC_VERSION_EXTERNAL[vs_product] = vs_def
+ MSVC_VERSION_EXTERNAL[vc_buildtools_def.vc_version] = vs_def
+ MSVC_VERSION_EXTERNAL[vc_buildtools_def.vc_buildtools] = vs_def
+
+ if vs_product in VS_PRODUCT_ALIAS:
+ for vs_product_alias in VS_PRODUCT_ALIAS[vs_product]:
+ vs_def.vs_product_alias_list.append(vs_product_alias)
+ MSVS_VERSION_EXTERNAL[vs_product_alias] = vs_def
+ MSVC_VERSION_EXTERNAL[vs_product_alias] = vs_def
+
+ MSVS_VERSION_MAJOR_MAP[vs_version_major] = vs_def
+
+ CL_VERSION_MAP[vc_buildtools_def.cl_version] = vs_def
+
+ if not vc_sdk:
+ continue
+
+ MSVC_SDK_VERSIONS.update(vc_sdk)
+
+# convert string version set to string version list ranked in descending order
+MSVC_SDK_VERSIONS = [str(f) for f in sorted([float(s) for s in MSVC_SDK_VERSIONS], reverse=True)]
+
+MSVS_VERSION_LEGACY = {}
+MSVC_VERSION_LEGACY = {}
+
+for vdict in (MSVS_VERSION_EXTERNAL, MSVC_VERSION_INTERNAL):
+ for key, vs_def in vdict.items():
+ if key not in MSVS_VERSION_LEGACY:
+ MSVS_VERSION_LEGACY[key] = vs_def
+ MSVC_VERSION_LEGACY[key] = vs_def
+
+# MSVC_NOTFOUND_POLICY definition:
+# error: raise exception
+# warning: issue warning and continue
+# ignore: continue
+
+MSVC_NOTFOUND_POLICY_DEFINITION = namedtuple('MSVCNotFoundPolicyDefinition', [
+ 'value',
+ 'symbol',
+])
+
+MSVC_NOTFOUND_DEFINITION_LIST = []
+
+MSVC_NOTFOUND_POLICY_INTERNAL = {}
+MSVC_NOTFOUND_POLICY_EXTERNAL = {}
+
+for policy_value, policy_symbol_list in [
+ (True, ['Error', 'Exception']),
+ (False, ['Warning', 'Warn']),
+ (None, ['Ignore', 'Suppress']),
+]:
+
+ policy_symbol = policy_symbol_list[0].lower()
+ policy_def = MSVC_NOTFOUND_POLICY_DEFINITION(policy_value, policy_symbol)
+
+ MSVC_NOTFOUND_DEFINITION_LIST.append(vs_def)
+
+ MSVC_NOTFOUND_POLICY_INTERNAL[policy_symbol] = policy_def
+
+ for policy_symbol in policy_symbol_list:
+ MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.lower()] = policy_def
+ MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol] = policy_def
+ MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.upper()] = policy_def
+
diff --git a/SCons/Tool/MSCommon/MSVC/Dispatcher.py b/SCons/Tool/MSCommon/MSVC/Dispatcher.py
new file mode 100644
index 0000000..ebcd704
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/Dispatcher.py
@@ -0,0 +1,62 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""
+Internal method dispatcher for Microsoft Visual C/C++.
+"""
+
+import sys
+
+from ..common import (
+ debug,
+)
+
+_refs = []
+
+def register_class(ref):
+ _refs.append(ref)
+
+def register_modulename(modname):
+ module = sys.modules[modname]
+ _refs.append(module)
+
+def reset():
+ debug('')
+ for ref in _refs:
+ for method in ['reset', '_reset']:
+ if not hasattr(ref, method) or not callable(getattr(ref, method, None)):
+ continue
+ debug('call %s.%s()', ref.__name__, method)
+ func = getattr(ref, method)
+ func()
+
+def verify():
+ debug('')
+ for ref in _refs:
+ for method in ['verify', '_verify']:
+ if not hasattr(ref, method) or not callable(getattr(ref, method, None)):
+ continue
+ debug('call %s.%s()', ref.__name__, method)
+ func = getattr(ref, method)
+ func()
+
diff --git a/SCons/Tool/MSCommon/MSVC/Exceptions.py b/SCons/Tool/MSCommon/MSVC/Exceptions.py
new file mode 100644
index 0000000..7a61ec5
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/Exceptions.py
@@ -0,0 +1,39 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""
+Exceptions for Microsoft Visual C/C++.
+"""
+
+class VisualCException(Exception):
+ pass
+
+class MSVCVersionNotFound(VisualCException):
+ pass
+
+class MSVCInternalError(VisualCException):
+ pass
+
+class MSVCArgumentError(VisualCException):
+ pass
+
diff --git a/SCons/Tool/MSCommon/MSVC/NotFound.py b/SCons/Tool/MSCommon/MSVC/NotFound.py
new file mode 100644
index 0000000..7abe5ad
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/NotFound.py
@@ -0,0 +1,132 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""
+Microsoft Visual C/C++ not found policy.
+
+Notes:
+ * As implemented, the default is that a warning is issued. This can
+ be changed globally via the function set_msvc_notfound_policy and/or
+ through the environment via the MSVC_NOTFOUND_POLICY variable.
+"""
+
+import SCons.Warnings
+
+from ..common import (
+ debug,
+)
+
+from . import Dispatcher
+from . import Config
+
+from .Exceptions import (
+ MSVCVersionNotFound,
+)
+
+Dispatcher.register_modulename(__name__)
+
+_MSVC_NOTFOUND_POLICY_DEF = Config.MSVC_NOTFOUND_POLICY_INTERNAL['warning']
+
+def _msvc_notfound_policy_lookup(symbol):
+
+ try:
+ notfound_policy_def = Config.MSVC_NOTFOUND_POLICY_EXTERNAL[symbol]
+ except KeyError:
+ err_msg = "Value specified for MSVC_NOTFOUND_POLICY is not supported: {}.\n" \
+ " Valid values are: {}".format(
+ repr(symbol),
+ ', '.join([repr(s) for s in Config.MSVC_NOTFOUND_POLICY_EXTERNAL.keys()])
+ )
+ raise ValueError(err_msg)
+
+ return notfound_policy_def
+
+def set_msvc_notfound_policy(MSVC_NOTFOUND_POLICY=None):
+ """ Set the default policy when MSVC is not found.
+
+ Args:
+ MSVC_NOTFOUND_POLICY:
+ string representing the policy behavior
+ when MSVC is not found or None
+
+ Returns:
+ The previous policy is returned when the MSVC_NOTFOUND_POLICY argument
+ is not None. The active policy is returned when the MSVC_NOTFOUND_POLICY
+ argument is None.
+
+ """
+ global _MSVC_NOTFOUND_POLICY_DEF
+
+ prev_policy = _MSVC_NOTFOUND_POLICY_DEF.symbol
+
+ policy = MSVC_NOTFOUND_POLICY
+ if policy is not None:
+ _MSVC_NOTFOUND_POLICY_DEF = _msvc_notfound_policy_lookup(policy)
+
+ debug(
+ 'prev_policy=%s, set_policy=%s, policy.symbol=%s, policy.value=%s',
+ repr(prev_policy), repr(policy),
+ repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value)
+ )
+
+ return prev_policy
+
+def get_msvc_notfound_policy():
+ """Return the active policy when MSVC is not found."""
+ debug(
+ 'policy.symbol=%s, policy.value=%s',
+ repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value)
+ )
+ return _MSVC_NOTFOUND_POLICY_DEF.symbol
+
+def policy_handler(env, msg):
+
+ if env and 'MSVC_NOTFOUND_POLICY' in env:
+ # environment setting
+ notfound_policy_src = 'environment'
+ policy = env['MSVC_NOTFOUND_POLICY']
+ if policy is not None:
+ # user policy request
+ notfound_policy_def = _msvc_notfound_policy_lookup(policy)
+ else:
+ # active global setting
+ notfound_policy_def = _MSVC_NOTFOUND_POLICY_DEF
+ else:
+ # active global setting
+ notfound_policy_src = 'default'
+ policy = None
+ notfound_policy_def = _MSVC_NOTFOUND_POLICY_DEF
+
+ debug(
+ 'source=%s, set_policy=%s, policy.symbol=%s, policy.value=%s',
+ notfound_policy_src, repr(policy), repr(notfound_policy_def.symbol), repr(notfound_policy_def.value)
+ )
+
+ if notfound_policy_def.value is None:
+ # ignore
+ pass
+ elif notfound_policy_def.value:
+ raise MSVCVersionNotFound(msg)
+ else:
+ SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
+
diff --git a/SCons/Tool/MSCommon/MSVC/Registry.py b/SCons/Tool/MSCommon/MSVC/Registry.py
new file mode 100644
index 0000000..848b125
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/Registry.py
@@ -0,0 +1,110 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""
+Windows registry functions for Microsoft Visual C/C++.
+"""
+
+import os
+
+from SCons.Util import (
+ HKEY_LOCAL_MACHINE,
+ HKEY_CURRENT_USER,
+ HKEY_LOCAL_MACHINE,
+ HKEY_CURRENT_USER,
+)
+
+from .. common import (
+ debug,
+ read_reg,
+)
+
+from . import Dispatcher
+from . import Util
+
+Dispatcher.register_modulename(__name__)
+
+def read_value(hkey, subkey_valname):
+ try:
+ rval = read_reg(subkey_valname, hkroot=hkey)
+ except OSError:
+ debug('OSError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname))
+ return None
+ except IndexError:
+ debug('IndexError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname))
+ return None
+ debug('hkey=%s, subkey=%s, rval=%s', repr(hkey), repr(subkey_valname), repr(rval))
+ return rval
+
+def registry_query_path(key, val, suffix):
+ extval = val + '\\' + suffix if suffix else val
+ qpath = read_value(key, extval)
+ if qpath and os.path.exists(qpath):
+ qpath = Util.process_path(qpath)
+ else:
+ qpath = None
+ return (qpath, key, val, extval)
+
+REG_SOFTWARE_MICROSOFT = [
+ (HKEY_LOCAL_MACHINE, r'Software\Wow6432Node\Microsoft'),
+ (HKEY_CURRENT_USER, r'Software\Wow6432Node\Microsoft'), # SDK queries
+ (HKEY_LOCAL_MACHINE, r'Software\Microsoft'),
+ (HKEY_CURRENT_USER, r'Software\Microsoft'),
+]
+
+def microsoft_query_paths(suffix, usrval=None):
+ paths = []
+ records = []
+ for key, val in REG_SOFTWARE_MICROSOFT:
+ extval = val + '\\' + suffix if suffix else val
+ qpath = read_value(key, extval)
+ if qpath and os.path.exists(qpath):
+ qpath = Util.process_path(qpath)
+ if qpath not in paths:
+ paths.append(qpath)
+ records.append((qpath, key, val, extval, usrval))
+ return records
+
+def microsoft_query_keys(suffix, usrval=None):
+ records = []
+ for key, val in REG_SOFTWARE_MICROSOFT:
+ extval = val + '\\' + suffix if suffix else val
+ rval = read_value(key, extval)
+ if rval:
+ records.append((key, val, extval, usrval))
+ return records
+
+def microsoft_sdks(version):
+ return '\\'.join([r'Microsoft SDKs\Windows', 'v' + version, r'InstallationFolder'])
+
+def sdk_query_paths(version):
+ q = microsoft_sdks(version)
+ return microsoft_query_paths(q)
+
+def windows_kits(version):
+ return r'Windows Kits\Installed Roots\KitsRoot' + version
+
+def windows_kit_query_paths(version):
+ q = windows_kits(version)
+ return microsoft_query_paths(q)
+
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
new file mode 100644
index 0000000..324f8be
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -0,0 +1,733 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""
+Batch file argument functions for Microsoft Visual C/C++.
+"""
+
+import os
+import re
+import enum
+
+from collections import (
+ namedtuple,
+)
+
+from ..common import (
+ debug,
+)
+
+from . import Dispatcher
+from . import Util
+from . import Config
+from . import WinSDK
+
+from .Exceptions import (
+ MSVCInternalError,
+ MSVCArgumentError,
+)
+
+Dispatcher.register_modulename(__name__)
+
+# TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last?
+re_sdk_version_100 = re.compile(r'^10[.][0-9][.][0-9]{5}[.][0-9]{1}$')
+re_sdk_version_81 = re.compile(r'^8[.]1$')
+
+re_sdk_dispatch_map = {
+ '10.0': re_sdk_version_100,
+ '8.1': re_sdk_version_81,
+}
+
+def _verify_re_sdk_dispatch_map():
+ debug('')
+ for sdk_version in Config.MSVC_SDK_VERSIONS:
+ if sdk_version in re_sdk_dispatch_map:
+ continue
+ err_msg = 'sdk version {} not in re_sdk_dispatch_map'.format(sdk_version)
+ raise MSVCInternalError(err_msg)
+ return None
+
+# capture msvc version
+re_toolset_version = re.compile(r'^(?P[1-9][0-9]?[.][0-9])[0-9.]*$', re.IGNORECASE)
+
+re_toolset_full = re.compile(r'''^(?:
+ (?:[1-9][0-9][.][0-9]{1,2})| # XX.Y - XX.YY
+ (?:[1-9][0-9][.][0-9]{2}[.][0-9]{1,5}) # XX.YY.Z - XX.YY.ZZZZZ
+)$''', re.VERBOSE)
+
+re_toolset_140 = re.compile(r'''^(?:
+ (?:14[.]0{1,2})| # 14.0 - 14.00
+ (?:14[.]0{2}[.]0{1,5}) # 14.00.0 - 14.00.00000
+)$''', re.VERBOSE)
+
+# valid SxS formats will be matched with re_toolset_full: match 3 '.' format
+re_toolset_sxs = re.compile(r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$')
+
+# MSVC_SCRIPT_ARGS
+re_vcvars_uwp = re.compile(r'(?:(?(?:uwp|store))(?:(?!\S)|$)',re.IGNORECASE)
+re_vcvars_sdk = re.compile(r'(?:(?(?:[1-9][0-9]*[.]\S*))(?:(?!\S)|$)',re.IGNORECASE)
+re_vcvars_toolset = re.compile(r'(?:(?(?:[-]{1,2}|[/])vcvars_ver[=](?P\S*))(?:(?!\S)|$)', re.IGNORECASE)
+re_vcvars_spectre = re.compile(r'(?:(?(?:[-]{1,2}|[/])vcvars_spectre_libs[=](?P\S*))(?:(?!\S)|$)',re.IGNORECASE)
+
+# Force default sdk argument
+MSVC_FORCE_DEFAULT_SDK = False
+
+# Force default toolset argument
+MSVC_FORCE_DEFAULT_TOOLSET = False
+
+# MSVC batch file arguments:
+#
+# VS2022: UWP, SDK, TOOLSET, SPECTRE
+# VS2019: UWP, SDK, TOOLSET, SPECTRE
+# VS2017: UWP, SDK, TOOLSET, SPECTRE
+# VS2015: UWP, SDK
+#
+# MSVC_SCRIPT_ARGS: VS2015+
+#
+# MSVC_UWP_APP: VS2015+
+# MSVC_SDK_VERSION: VS2015+
+# MSVC_TOOLSET_VERSION: VS2017+
+# MSVC_SPECTRE_LIBS: VS2017+
+
+@enum.unique
+class SortOrder(enum.IntEnum):
+ ARCH = 0 # arch
+ UWP = 1 # MSVC_UWP_APP
+ SDK = 2 # MSVC_SDK_VERSION
+ TOOLSET = 3 # MSVC_TOOLSET_VERSION
+ SPECTRE = 4 # MSVC_SPECTRE_LIBS
+ USER = 5 # MSVC_SCRIPT_ARGS
+
+VS2019 = Config.MSVS_VERSION_INTERNAL['2019']
+VS2017 = Config.MSVS_VERSION_INTERNAL['2017']
+VS2015 = Config.MSVS_VERSION_INTERNAL['2015']
+
+MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [
+ 'version', # fully qualified msvc version (e.g., '14.1Exp')
+ 'vs_def',
+])
+
+def _msvc_version(version):
+
+ verstr = Util.get_version_prefix(version)
+ vs_def = Config.MSVC_VERSION_INTERNAL[verstr]
+
+ version_args = MSVC_VERSION_ARGS_DEFINITION(
+ version = version,
+ vs_def = vs_def,
+ )
+
+ return version_args
+
+def _msvc_script_argument_uwp(env, msvc, arglist):
+
+ uwp_app = env['MSVC_UWP_APP']
+ debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app))
+
+ if not uwp_app:
+ return None
+
+ if uwp_app not in Config.BOOLEAN_SYMBOLS[True]:
+ return None
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: msvc version constraint: %s < %s VS2015',
+ repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(VS2015.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
+ repr(uwp_app), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version)
+ )
+ raise MSVCArgumentError(err_msg)
+
+ # VS2017+ rewrites uwp => store for 14.0 toolset
+ uwp_arg = msvc.vs_def.vc_uwp
+
+ # store/uwp may not be fully installed
+ argpair = (SortOrder.UWP, uwp_arg)
+ arglist.append(argpair)
+
+ return uwp_arg
+
+def _user_script_argument_uwp(env, uwp, user_argstr):
+
+ matches = [m for m in re_vcvars_uwp.finditer(user_argstr)]
+ if not matches:
+ return None
+
+ if len(matches) > 1:
+ debug('multiple uwp declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
+ err_msg = "multiple uwp declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
+ raise MSVCArgumentError(err_msg)
+
+ if not uwp:
+ return None
+
+ env_argstr = env.get('MSVC_UWP_APP','')
+ debug('multiple uwp declarations: MSVC_UWP_APP=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
+
+ err_msg = "multiple uwp declarations: MSVC_UWP_APP={} and MSVC_SCRIPT_ARGS={}".format(
+ repr(env_argstr), repr(user_argstr)
+ )
+
+ raise MSVCArgumentError(err_msg)
+
+def _msvc_script_argument_sdk_constraints(msvc, sdk_version):
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: msvc_version constraint: %s < %s VS2015',
+ repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(VS2015.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
+ repr(sdk_version), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version)
+ )
+ return err_msg
+
+ for msvc_sdk_version in msvc.vs_def.vc_sdk_versions:
+ re_sdk_version = re_sdk_dispatch_map[msvc_sdk_version]
+ if re_sdk_version.match(sdk_version):
+ debug('valid: sdk_version=%s', repr(sdk_version))
+ return None
+
+ debug('invalid: method exit: sdk_version=%s', repr(sdk_version))
+ err_msg = "MSVC_SDK_VERSION ({}) is not supported".format(repr(sdk_version))
+ return err_msg
+
+def _msvc_script_argument_sdk(env, msvc, platform_type, arglist):
+
+ sdk_version = env['MSVC_SDK_VERSION']
+ debug(
+ 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, platform_type=%s',
+ repr(msvc.version), repr(sdk_version), repr(platform_type)
+ )
+
+ if not sdk_version:
+ return None
+
+ err_msg = _msvc_script_argument_sdk_constraints(msvc, sdk_version)
+ if err_msg:
+ raise MSVCArgumentError(err_msg)
+
+ sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type)
+
+ if sdk_version not in sdk_list:
+ err_msg = "MSVC_SDK_VERSION {} not found for platform type {}".format(
+ repr(sdk_version), repr(platform_type)
+ )
+ raise MSVCArgumentError(err_msg)
+
+ argpair = (SortOrder.SDK, sdk_version)
+ arglist.append(argpair)
+
+ return sdk_version
+
+def _msvc_script_default_sdk(env, msvc, platform_type, arglist):
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric:
+ return None
+
+ sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type)
+ if not len(sdk_list):
+ return None
+
+ sdk_default = sdk_list[0]
+
+ debug(
+ 'MSVC_VERSION=%s, sdk_default=%s, platform_type=%s',
+ repr(msvc.version), repr(sdk_default), repr(platform_type)
+ )
+
+ argpair = (SortOrder.SDK, sdk_default)
+ arglist.append(argpair)
+
+ return sdk_default
+
+def _user_script_argument_sdk(env, sdk_version, user_argstr):
+
+ matches = [m for m in re_vcvars_sdk.finditer(user_argstr)]
+ if not matches:
+ return None
+
+ if len(matches) > 1:
+ debug('multiple sdk version declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
+ err_msg = "multiple sdk version declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
+ raise MSVCArgumentError(err_msg)
+
+ if not sdk_version:
+ user_sdk = matches[0].group('sdk')
+ return user_sdk
+
+ env_argstr = env.get('MSVC_SDK_VERSION','')
+ debug('multiple sdk version declarations: MSVC_SDK_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
+
+ err_msg = "multiple sdk version declarations: MSVC_SDK_VERSION={} and MSVC_SCRIPT_ARGS={}".format(
+ repr(env_argstr), repr(user_argstr)
+ )
+
+ raise MSVCArgumentError(err_msg)
+
+def _msvc_read_toolset_file(msvc, filename):
+ toolset_version = None
+ try:
+ with open(filename) as f:
+ toolset_version = f.readlines()[0].strip()
+ debug(
+ 'msvc_version=%s, filename=%s, toolset_version=%s',
+ repr(msvc.version), repr(filename), repr(toolset_version)
+ )
+ except OSError:
+ debug('OSError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename))
+ except IndexError:
+ debug('IndexError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename))
+ return toolset_version
+
+def _msvc_read_toolset_folders(msvc, vc_dir):
+
+ toolsets_sxs = {}
+ toolsets_full = []
+
+ build_dir = os.path.join(vc_dir, "Auxiliary", "Build")
+ sxs_toolsets = [f.name for f in os.scandir(build_dir) if f.is_dir()]
+ for sxs_toolset in sxs_toolsets:
+ filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset)
+ filepath = os.path.join(build_dir, sxs_toolset, filename)
+ debug('sxs toolset: check file=%s', repr(filepath))
+ if os.path.exists(filepath):
+ toolset_version = _msvc_read_toolset_file(msvc, filepath)
+ if not toolset_version:
+ continue
+ toolsets_sxs[sxs_toolset] = toolset_version
+ debug(
+ 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s',
+ repr(msvc.version), repr(sxs_toolset), toolset_version
+ )
+
+ toolset_dir = os.path.join(vc_dir, "Tools", "MSVC")
+ toolsets = [f.name for f in os.scandir(toolset_dir) if f.is_dir()]
+ for toolset_version in toolsets:
+ binpath = os.path.join(toolset_dir, toolset_version, "bin")
+ debug('toolset: check binpath=%s', repr(binpath))
+ if os.path.exists(binpath):
+ toolsets_full.append(toolset_version)
+ debug(
+ 'toolset: msvc_version=%s, toolset_version=%s',
+ repr(msvc.version), repr(toolset_version)
+ )
+
+ toolsets_full.sort(reverse=True)
+ debug('msvc_version=%s, toolsets=%s', repr(msvc.version), repr(toolsets_full))
+
+ return toolsets_sxs, toolsets_full
+
+def _msvc_read_toolset_default(msvc, vc_dir):
+
+ build_dir = os.path.join(vc_dir, "Auxiliary", "Build")
+
+ # VS2019+
+ filename = "Microsoft.VCToolsVersion.{}.default.txt".format(msvc.vs_def.vc_buildtools_def.vc_buildtools)
+ filepath = os.path.join(build_dir, filename)
+
+ debug('default toolset: check file=%s', repr(filepath))
+ if os.path.exists(filepath):
+ toolset_buildtools = _msvc_read_toolset_file(msvc, filepath)
+ if toolset_buildtools:
+ return toolset_buildtools
+
+ # VS2017+
+ filename = "Microsoft.VCToolsVersion.default.txt"
+ filepath = os.path.join(build_dir, filename)
+
+ debug('default toolset: check file=%s', repr(filepath))
+ if os.path.exists(filepath):
+ toolset_default = _msvc_read_toolset_file(msvc, filepath)
+ if toolset_default:
+ return toolset_default
+
+ return None
+
+_toolset_version_cache = {}
+_toolset_default_cache = {}
+
+def _reset_toolset_cache():
+ debug('reset: toolset cache')
+ _toolset_version_cache = {}
+ _toolset_default_cache = {}
+
+def _msvc_version_toolsets(msvc, vc_dir):
+
+ if msvc.version in _toolset_version_cache:
+ toolsets_sxs, toolsets_full = _toolset_version_cache[msvc.version]
+ else:
+ toolsets_sxs, toolsets_full = _msvc_read_toolset_folders(msvc, vc_dir)
+ _toolset_version_cache[msvc.version] = toolsets_sxs, toolsets_full
+
+ return toolsets_sxs, toolsets_full
+
+def _msvc_default_toolset(msvc, vc_dir):
+
+ if msvc.version in _toolset_default_cache:
+ toolset_default = _toolset_default_cache[msvc.version]
+ else:
+ toolset_default = _msvc_read_toolset_default(msvc, vc_dir)
+ _toolset_default_cache[msvc.version] = toolset_default
+
+ return toolset_default
+
+def _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version):
+
+ if toolset_version == '14.0':
+ return toolset_version
+
+ toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir)
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric == VS2019.vc_buildtools_def.vc_version_numeric:
+ # necessary to detect toolset not found
+ if toolset_version == '14.28.16.8':
+ new_toolset_version = '14.28'
+ # VS2019\Common7\Tools\vsdevcmd\ext\vcvars.bat AzDO Bug#1293526
+ # special handling of the 16.8 SxS toolset, use VC\Auxiliary\Build\14.28 directory and SxS files
+ # if SxS version 14.28 not present/installed, fallback selection of toolset VC\Tools\MSVC\14.28.nnnnn.
+ debug(
+ 'rewrite toolset_version=%s => toolset_version=%s',
+ repr(toolset_version), repr(new_toolset_version)
+ )
+ toolset_version = new_toolset_version
+
+ if toolset_version in toolsets_sxs:
+ toolset_vcvars = toolsets_sxs[toolset_version]
+ return toolset_vcvars
+
+ for toolset_full in toolsets_full:
+ if toolset_full.startswith(toolset_version):
+ toolset_vcvars = toolset_full
+ return toolset_vcvars
+
+ return None
+
+def _msvc_script_argument_toolset_constraints(msvc, toolset_version):
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: msvc version constraint: %s < %s VS2017',
+ repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(VS2017.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
+ repr(toolset_version), repr(msvc.version), repr(VS2017.vc_buildtools_def.vc_version)
+ )
+ return err_msg
+
+ m = re_toolset_version.match(toolset_version)
+ if not m:
+ debug('invalid: re_toolset_version: toolset_version=%s', repr(toolset_version))
+ err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format(
+ repr(toolset_version)
+ )
+ return err_msg
+
+ toolset_ver = m.group('version')
+ toolset_vernum = float(toolset_ver)
+
+ if toolset_vernum < VS2015.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: toolset version constraint: %s < %s VS2015',
+ repr(toolset_vernum), repr(VS2015.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < {} VS2015".format(
+ repr(toolset_version), repr(toolset_ver), repr(VS2015.vc_buildtools_def.vc_version)
+ )
+ return err_msg
+
+ if toolset_vernum > msvc.vs_def.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: toolset version constraint: toolset %s > %s msvc',
+ repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format(
+ repr(toolset_version), repr(toolset_ver), repr(msvc.version)
+ )
+ return err_msg
+
+ if toolset_vernum == VS2015.vc_buildtools_def.vc_version_numeric:
+ # tooset = 14.0
+ if re_toolset_full.match(toolset_version):
+ if not re_toolset_140.match(toolset_version):
+ debug(
+ 'invalid: toolset version 14.0 constraint: %s != 14.0',
+ repr(toolset_version)
+ )
+ err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0'".format(
+ repr(toolset_version), repr(toolset_version)
+ )
+ return err_msg
+ return None
+
+ if re_toolset_full.match(toolset_version):
+ debug('valid: re_toolset_full: toolset_version=%s', repr(toolset_version))
+ return None
+
+ if re_toolset_sxs.match(toolset_version):
+ debug('valid: re_toolset_sxs: toolset_version=%s', repr(toolset_version))
+ return None
+
+ debug('invalid: method exit: toolset_version=%s', repr(toolset_version))
+ err_msg = "MSVC_TOOLSET_VERSION ({}) format is not supported".format(repr(toolset_version))
+ return err_msg
+
+def _msvc_script_argument_toolset(env, msvc, vc_dir, arglist):
+
+ toolset_version = env['MSVC_TOOLSET_VERSION']
+ debug('MSVC_VERSION=%s, MSVC_TOOLSET_VERSION=%s', repr(msvc.version), repr(toolset_version))
+
+ if not toolset_version:
+ return None
+
+ err_msg = _msvc_script_argument_toolset_constraints(msvc, toolset_version)
+ if err_msg:
+ raise MSVCArgumentError(err_msg)
+
+ if toolset_version.startswith('14.0') and len(toolset_version) > len('14.0'):
+ new_toolset_version = '14.0'
+ debug(
+ 'rewrite toolset_version=%s => toolset_version=%s',
+ repr(toolset_version), repr(new_toolset_version)
+ )
+ toolset_version = new_toolset_version
+
+ toolset_vcvars = _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version)
+ debug(
+ 'toolset: toolset_version=%s, toolset_vcvars=%s',
+ repr(toolset_version), repr(toolset_vcvars)
+ )
+
+ if not toolset_vcvars:
+ err_msg = "MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}".format(
+ repr(toolset_version), repr(msvc.version)
+ )
+ raise MSVCArgumentError(err_msg)
+
+ argpair = (SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_vcvars))
+ arglist.append(argpair)
+
+ return toolset_vcvars
+
+def _msvc_script_default_toolset(env, msvc, vc_dir, arglist):
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric:
+ return None
+
+ toolset_default = _msvc_default_toolset(msvc, vc_dir)
+ if not toolset_default:
+ return None
+
+ debug('MSVC_VERSION=%s, toolset_default=%s', repr(msvc.version), repr(toolset_default))
+
+ argpair = (SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_default))
+ arglist.append(argpair)
+
+ return toolset_default
+
+def _user_script_argument_toolset(env, toolset_version, user_argstr):
+
+ matches = [m for m in re_vcvars_toolset.finditer(user_argstr)]
+ if not matches:
+ return None
+
+ if len(matches) > 1:
+ debug('multiple toolset version declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
+ err_msg = "multiple toolset version declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
+ raise MSVCArgumentError(err_msg)
+
+ if not toolset_version:
+ user_toolset = matches[0].group('toolset')
+ return user_toolset
+
+ env_argstr = env.get('MSVC_TOOLSET_VERSION','')
+ debug('multiple toolset version declarations: MSVC_TOOLSET_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
+
+ err_msg = "multiple toolset version declarations: MSVC_TOOLSET_VERSION={} and MSVC_SCRIPT_ARGS={}".format(
+ repr(env_argstr), repr(user_argstr)
+ )
+
+ raise MSVCArgumentError(err_msg)
+
+def _msvc_script_argument_spectre(env, msvc, arglist):
+
+ spectre_libs = env['MSVC_SPECTRE_LIBS']
+ debug('MSVC_VERSION=%s, MSVC_SPECTRE_LIBS=%s', repr(msvc.version), repr(spectre_libs))
+
+ if not spectre_libs:
+ return None
+
+ if spectre_libs not in (True, '1'):
+ return None
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: msvc version constraint: %s < %s VS2017',
+ repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(VS2017.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
+ repr(spectre_libs), repr(msvc.version), repr(VS2017.vc_buildtools_def.vc_version)
+ )
+ raise MSVCArgumentError(err_msg)
+
+ spectre_arg = 'spectre'
+
+ # spectre libs may not be installed
+ argpair = (SortOrder.SPECTRE, '-vcvars_spectre_libs={}'.format(spectre_arg))
+ arglist.append(argpair)
+
+ return spectre_arg
+
+def _msvc_script_argument_user(env, msvc, arglist):
+
+ # subst None -> empty string
+ script_args = env.subst('$MSVC_SCRIPT_ARGS')
+ debug('MSVC_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(msvc.version), repr(script_args))
+
+ if not script_args:
+ return None
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: msvc version constraint: %s < %s VS2015',
+ repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(VS2015.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
+ repr(script_args), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version)
+ )
+ raise MSVCArgumentError(err_msg)
+
+ # user arguments are not validated
+ argpair = (SortOrder.USER, script_args)
+ arglist.append(argpair)
+
+ return script_args
+
+def _user_script_argument_spectre(env, spectre, user_argstr):
+
+ matches = [m for m in re_vcvars_spectre.finditer(user_argstr)]
+ if not matches:
+ return None
+
+ if len(matches) > 1:
+ debug('multiple spectre declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
+ err_msg = "multiple spectre declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
+ raise MSVCArgumentError(err_msg)
+
+ if not spectre:
+ return None
+
+ env_argstr = env.get('MSVC_SPECTRE_LIBS','')
+ debug('multiple spectre declarations: MSVC_SPECTRE_LIBS=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
+
+ err_msg = "multiple spectre declarations: MSVC_SPECTRE_LIBS={} and MSVC_SCRIPT_ARGS={}".format(
+ repr(env_argstr), repr(user_argstr)
+ )
+
+ raise MSVCArgumentError(err_msg)
+
+def msvc_script_arguments(env, version, vc_dir, arg):
+
+ arglist = []
+
+ msvc = _msvc_version(version)
+
+ if arg:
+ argpair = (SortOrder.ARCH, arg)
+ arglist.append(argpair)
+
+ if 'MSVC_SCRIPT_ARGS' in env:
+ user_argstr = _msvc_script_argument_user(env, msvc, arglist)
+ else:
+ user_argstr = None
+
+ if 'MSVC_UWP_APP' in env:
+ uwp = _msvc_script_argument_uwp(env, msvc, arglist)
+ else:
+ uwp = None
+
+ if user_argstr:
+ _user_script_argument_uwp(env, uwp, user_argstr)
+
+ platform_type = 'uwp' if uwp else 'desktop'
+
+ if 'MSVC_SDK_VERSION' in env:
+ sdk_version = _msvc_script_argument_sdk(env, msvc, platform_type, arglist)
+ else:
+ sdk_version = None
+
+ if user_argstr:
+ user_sdk = _user_script_argument_sdk(env, sdk_version, user_argstr)
+ else:
+ user_sdk = None
+
+ if MSVC_FORCE_DEFAULT_SDK:
+ if not sdk_version and not user_sdk:
+ sdk_version = _msvc_script_default_sdk(env, msvc, platform_type, arglist)
+
+ if 'MSVC_TOOLSET_VERSION' in env:
+ toolset_version = _msvc_script_argument_toolset(env, msvc, vc_dir, arglist)
+ else:
+ toolset_version = None
+
+ if user_argstr:
+ user_toolset = _user_script_argument_toolset(env, toolset_version, user_argstr)
+ else:
+ user_toolset = None
+
+ if MSVC_FORCE_DEFAULT_TOOLSET:
+ if not toolset_version and not user_toolset:
+ toolset_version = _msvc_script_default_toolset(env, msvc, vc_dir, arglist)
+
+ if 'MSVC_SPECTRE_LIBS' in env:
+ spectre = _msvc_script_argument_spectre(env, msvc, arglist)
+ else:
+ spectre = None
+
+ if user_argstr:
+ _user_script_argument_spectre(env, spectre, user_argstr)
+
+ if arglist:
+ arglist.sort()
+ argstr = ' '.join([argpair[-1] for argpair in arglist]).strip()
+ else:
+ argstr = ''
+
+ debug('arguments: %s', repr(argstr))
+ return argstr
+
+def reset():
+ debug('')
+ _reset_toolset_cache()
+
+def verify():
+ debug('')
+ _verify_re_sdk_dispatch_map()
+
diff --git a/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py b/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py
new file mode 100644
index 0000000..8a79007
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py
@@ -0,0 +1,235 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""
+Determine if and/or when an error/warning should be issued when there
+are no versions of msvc installed. If there is at least one version of
+msvc installed, these routines do (almost) nothing.
+
+Notes:
+ * When msvc is the default compiler because there are no compilers
+ installed, a build may fail due to the cl.exe command not being
+ recognized. Currently, there is no easy way to detect during
+ msvc initialization if the default environment will be used later
+ to build a program and/or library. There is no error/warning
+ as there are legitimate SCons uses that do not require a c compiler.
+ * An error is indicated by returning a non-empty tool list from the
+ function register_iserror.
+"""
+
+import re
+
+from .. common import (
+ debug,
+)
+
+from . import Dispatcher
+
+Dispatcher.register_modulename(__name__)
+
+class _Data:
+
+ separator = r';'
+
+ need_init = True
+
+ @classmethod
+ def reset(cls):
+ debug('msvc default:init')
+ cls.n_setup = 0 # number of calls to msvc_setup_env_once
+ cls.default_ismsvc = False # is msvc the default compiler
+ cls.default_tools_re_list = [] # list of default tools regular expressions
+ cls.msvc_tools_init = set() # tools registered via msvc_exists
+ cls.msvc_tools = None # tools registered via msvc_setup_env_once
+ cls.msvc_installed = False # is msvc installed (vcs_installed > 0)
+ cls.msvc_nodefault = False # is there a default version of msvc
+ cls.need_init = True # reset initialization indicator
+
+def _initialize(env, msvc_exists_func):
+ if _Data.need_init:
+ _Data.reset()
+ _Data.need_init = False
+ _Data.msvc_installed = msvc_exists_func(env)
+ debug('msvc default:msvc_installed=%s', _Data.msvc_installed)
+
+def register_tool(env, tool, msvc_exists_func):
+ debug('msvc default:tool=%s', tool)
+ if _Data.need_init:
+ _initialize(env, msvc_exists_func)
+ if _Data.msvc_installed:
+ return None
+ if not tool:
+ return None
+ if _Data.n_setup == 0:
+ if tool not in _Data.msvc_tools_init:
+ _Data.msvc_tools_init.add(tool)
+ debug('msvc default:tool=%s, msvc_tools_init=%s', tool, _Data.msvc_tools_init)
+ return None
+ if tool not in _Data.msvc_tools:
+ _Data.msvc_tools.add(tool)
+ debug('msvc default:tool=%s, msvc_tools=%s', tool, _Data.msvc_tools)
+
+def register_setup(env, msvc_exists_func):
+ debug('msvc default')
+ if _Data.need_init:
+ _initialize(env, msvc_exists_func)
+ _Data.n_setup += 1
+ if not _Data.msvc_installed:
+ _Data.msvc_tools = set(_Data.msvc_tools_init)
+ if _Data.n_setup == 1:
+ tool_list = env.get('TOOLS', None)
+ if tool_list and tool_list[0] == 'default':
+ if len(tool_list) > 1 and tool_list[1] in _Data.msvc_tools:
+ # msvc tools are the default compiler
+ _Data.default_ismsvc = True
+ _Data.msvc_nodefault = False
+ debug(
+ 'msvc default:n_setup=%d, msvc_installed=%s, default_ismsvc=%s',
+ _Data.n_setup, _Data.msvc_installed, _Data.default_ismsvc
+ )
+
+def set_nodefault():
+ # default msvc version, msvc not installed
+ _Data.msvc_nodefault = True
+ debug('msvc default:msvc_nodefault=%s', _Data.msvc_nodefault)
+
+def register_iserror(env, tool, msvc_exists_func):
+
+ register_tool(env, tool, msvc_exists_func)
+
+ if _Data.msvc_installed:
+ # msvc installed
+ return None
+
+ if not _Data.msvc_nodefault:
+ # msvc version specified
+ return None
+
+ tool_list = env.get('TOOLS', None)
+ if not tool_list:
+ # tool list is empty
+ return None
+
+ debug(
+ 'msvc default:n_setup=%s, default_ismsvc=%s, msvc_tools=%s, tool_list=%s',
+ _Data.n_setup, _Data.default_ismsvc, _Data.msvc_tools, tool_list
+ )
+
+ if not _Data.default_ismsvc:
+
+ # Summary:
+ # * msvc is not installed
+ # * msvc version not specified (default)
+ # * msvc is not the default compiler
+
+ # construct tools set
+ tools_set = set(tool_list)
+
+ else:
+
+ if _Data.n_setup == 1:
+ # first setup and msvc is default compiler:
+ # build default tools regex for current tool state
+ tools = _Data.separator.join(tool_list)
+ tools_nchar = len(tools)
+ debug('msvc default:add regex:nchar=%d, tools=%s', tools_nchar, tools)
+ re_default_tools = re.compile(re.escape(tools))
+ _Data.default_tools_re_list.insert(0, (tools_nchar, re_default_tools))
+ # early exit: no error for default environment when msvc is not installed
+ return None
+
+ # Summary:
+ # * msvc is not installed
+ # * msvc version not specified (default)
+ # * environment tools list is not empty
+ # * default tools regex list constructed
+ # * msvc tools set constructed
+ #
+ # Algorithm using tools string and sets:
+ # * convert environment tools list to a string
+ # * iteratively remove default tools sequences via regex
+ # substition list built from longest sequence (first)
+ # to shortest sequence (last)
+ # * build environment tools set with remaining tools
+ # * compute intersection of environment tools and msvc tools sets
+ # * if the intersection is:
+ # empty - no error: default tools and/or no additional msvc tools
+ # not empty - error: user specified one or more msvc tool(s)
+ #
+ # This will not produce an error or warning when there are no
+ # msvc installed instances nor any other recognized compilers
+ # and the default environment is needed for a build. The msvc
+ # compiler is forcibly added to the environment tools list when
+ # there are no compilers installed on win32. In this case, cl.exe
+ # will not be found on the path resulting in a failed build.
+
+ # construct tools string
+ tools = _Data.separator.join(tool_list)
+ tools_nchar = len(tools)
+
+ debug('msvc default:check tools:nchar=%d, tools=%s', tools_nchar, tools)
+
+ # iteratively remove default tool sequences (longest to shortest)
+ re_nchar_min, re_tools_min = _Data.default_tools_re_list[-1]
+ if tools_nchar >= re_nchar_min and re_tools_min.search(tools):
+ # minimum characters satisfied and minimum pattern exists
+ for re_nchar, re_default_tool in _Data.default_tools_re_list:
+ if tools_nchar < re_nchar:
+ # not enough characters for pattern
+ continue
+ tools = re_default_tool.sub('', tools).strip(_Data.separator)
+ tools_nchar = len(tools)
+ debug('msvc default:check tools:nchar=%d, tools=%s', tools_nchar, tools)
+ if tools_nchar < re_nchar_min or not re_tools_min.search(tools):
+ # less than minimum characters or minimum pattern does not exist
+ break
+
+ # construct non-default list(s) tools set
+ tools_set = {msvc_tool for msvc_tool in tools.split(_Data.separator) if msvc_tool}
+
+ debug('msvc default:tools=%s', tools_set)
+ if not tools_set:
+ return None
+
+ # compute intersection of remaining tools set and msvc tools set
+ tools_found = _Data.msvc_tools.intersection(tools_set)
+ debug('msvc default:tools_exist=%s', tools_found)
+ if not tools_found:
+ return None
+
+ # construct in same order as tools list
+ tools_found_list = []
+ seen_tool = set()
+ for tool in tool_list:
+ if tool not in seen_tool:
+ seen_tool.add(tool)
+ if tool in tools_found:
+ tools_found_list.append(tool)
+
+ # return tool list in order presented
+ return tools_found_list
+
+def reset():
+ debug('')
+ _Data.reset()
+
diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py
new file mode 100644
index 0000000..15abdcd
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/Util.py
@@ -0,0 +1,55 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""
+Helper functions for Microsoft Visual C/C++.
+"""
+
+import os
+import re
+
+def listdir_dirs(p):
+ dirs = []
+ for dir_name in os.listdir(p):
+ dir_path = os.path.join(p, dir_name)
+ if os.path.isdir(dir_path):
+ dirs.append((dir_name, dir_path))
+ return dirs
+
+def process_path(p):
+ if p:
+ p = os.path.normpath(p)
+ p = os.path.realpath(p)
+ p = os.path.normcase(p)
+ return p
+
+re_version_prefix = re.compile(r'^(?P[0-9.]+).*')
+
+def get_version_prefix(version):
+ m = re_version_prefix.match(version)
+ if m:
+ rval = m.group('version')
+ else:
+ rval = ''
+ return rval
+
diff --git a/SCons/Tool/MSCommon/MSVC/WinSDK.py b/SCons/Tool/MSCommon/MSVC/WinSDK.py
new file mode 100644
index 0000000..e95c72e
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/WinSDK.py
@@ -0,0 +1,250 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""
+Windows SDK functions for Microsoft Visual C/C++.
+"""
+
+import os
+
+from ..common import (
+ debug,
+)
+
+from . import Dispatcher
+from . import Util
+from . import Config
+from . import Registry
+
+from .Exceptions import (
+ MSVCInternalError,
+)
+
+Dispatcher.register_modulename(__name__)
+
+def _new_sdk_map():
+ sdk_map = {
+ 'desktop': [],
+ 'uwp': [],
+ }
+ return sdk_map
+
+def _sdk_10_layout(version):
+
+ folder_prefix = version + '.'
+
+ sdk_map = _new_sdk_map()
+
+ sdk_roots = Registry.sdk_query_paths(version)
+
+ sdk_version_platform_seen = set()
+ sdk_roots_seen = set()
+
+ for sdk_t in sdk_roots:
+
+ sdk_root = sdk_t[0]
+ if sdk_root in sdk_roots_seen:
+ continue
+ sdk_roots_seen.add(sdk_root)
+
+ if not os.path.exists(sdk_root):
+ continue
+
+ sdk_include_path = os.path.join(sdk_root, 'include')
+ if not os.path.exists(sdk_include_path):
+ continue
+
+ for version_nbr, version_nbr_path in Util.listdir_dirs(sdk_include_path):
+
+ if not version_nbr.startswith(folder_prefix):
+ continue
+
+ sdk_inc_path = Util.process_path(os.path.join(version_nbr_path, 'um'))
+ if not os.path.exists(sdk_inc_path):
+ continue
+
+ for platform_type, sdk_inc_file in [
+ ('desktop', 'winsdkver.h'),
+ ('uwp', 'windows.h'),
+ ]:
+
+ if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)):
+ continue
+
+ key = (version_nbr, platform_type)
+ if key in sdk_version_platform_seen:
+ continue
+ sdk_version_platform_seen.add(key)
+
+ sdk_map[platform_type].append(version_nbr)
+
+ for key, val in sdk_map.items():
+ val.sort(reverse=True)
+
+ return sdk_map
+
+def _sdk_81_layout(version):
+
+ version_nbr = version
+
+ sdk_map = _new_sdk_map()
+
+ sdk_roots = Registry.sdk_query_paths(version)
+
+ sdk_version_platform_seen = set()
+ sdk_roots_seen = set()
+
+ for sdk_t in sdk_roots:
+
+ sdk_root = sdk_t[0]
+ if sdk_root in sdk_roots_seen:
+ continue
+ sdk_roots_seen.add(sdk_root)
+
+ # msvc does not check for existence of root or other files
+
+ sdk_inc_path = Util.process_path(os.path.join(sdk_root, r'include\um'))
+ if not os.path.exists(sdk_inc_path):
+ continue
+
+ for platform_type, sdk_inc_file in [
+ ('desktop', 'winsdkver.h'),
+ ('uwp', 'windows.h'),
+ ]:
+
+ if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)):
+ continue
+
+ key = (version_nbr, platform_type)
+ if key in sdk_version_platform_seen:
+ continue
+ sdk_version_platform_seen.add(key)
+
+ sdk_map[platform_type].append(version_nbr)
+
+ for key, val in sdk_map.items():
+ val.sort(reverse=True)
+
+ return sdk_map
+
+_sdk_map_cache = {}
+_sdk_cache = {}
+
+def _reset_sdk_cache():
+ debug('')
+ _sdk_map_cache = {}
+ _sdk_cache = {}
+
+def _sdk_10(key, reg_version):
+ if key in _sdk_map_cache:
+ sdk_map = _sdk_map_cache[key]
+ else:
+ sdk_map = _sdk_10_layout(reg_version)
+ _sdk_map_cache[key] = sdk_map
+ return sdk_map
+
+def _sdk_81(key, reg_version):
+ if key in _sdk_map_cache:
+ sdk_map = _sdk_map_cache[key]
+ else:
+ sdk_map = _sdk_81_layout(reg_version)
+ _sdk_map_cache[key] = sdk_map
+ return sdk_map
+
+def _combine_sdk_map_list(sdk_map_list):
+ combined_sdk_map = _new_sdk_map()
+ for sdk_map in sdk_map_list:
+ for key, val in sdk_map.items():
+ combined_sdk_map[key].extend(val)
+ return combined_sdk_map
+
+_sdk_dispatch_map = {
+ '10.0': (_sdk_10, '10.0'),
+ '8.1': (_sdk_81, '8.1'),
+}
+
+def _verify_sdk_dispatch_map():
+ debug('')
+ for sdk_version in Config.MSVC_SDK_VERSIONS:
+ if sdk_version in _sdk_dispatch_map:
+ continue
+ err_msg = 'sdk version {} not in sdk_dispatch_map'.format(sdk_version)
+ raise MSVCInternalError(err_msg)
+ return None
+
+def _version_list_sdk_map(version_list):
+
+ sdk_map_list = []
+ for version in version_list:
+ func, reg_version = _sdk_dispatch_map[version]
+ sdk_map = func(version, reg_version)
+ sdk_map_list.append(sdk_map)
+
+ combined_sdk_map = _combine_sdk_map_list(sdk_map_list)
+ return combined_sdk_map
+
+def _sdk_map(version_list):
+ key = tuple(version_list)
+ if key in _sdk_cache:
+ sdk_map = _sdk_cache[key]
+ else:
+ version_numlist = [float(v) for v in version_list]
+ version_numlist.sort(reverse=True)
+ key = tuple([str(v) for v in version_numlist])
+ sdk_map = _version_list_sdk_map(key)
+ _sdk_cache[key] = sdk_map
+ return sdk_map
+
+def get_sdk_version_list(version_list, platform_type):
+ sdk_map = _sdk_map(version_list)
+ sdk_list = sdk_map.get(platform_type, [])
+ return sdk_list
+
+def get_msvc_sdk_version_list(msvc_version, msvc_uwp_app=False):
+ debug('msvc_version=%s, msvc_uwp_app=%s', repr(msvc_version), repr(msvc_uwp_app))
+
+ sdk_versions = []
+
+ verstr = Util.get_version_prefix(msvc_version)
+ vs_def = Config.MSVC_VERSION_EXTERNAL.get(verstr, None)
+ if not vs_def:
+ debug('vs_def is not defined')
+ return sdk_versions
+
+ is_uwp = True if msvc_uwp_app in Config.BOOLEAN_SYMBOLS[True] else False
+ platform_type = 'uwp' if is_uwp else 'desktop'
+ sdk_list = get_sdk_version_list(vs_def.vc_sdk_versions, platform_type)
+
+ sdk_versions.extend(sdk_list)
+ debug('sdk_versions=%s', repr(sdk_versions))
+
+ return sdk_versions
+
+def reset():
+ debug('')
+ _reset_sdk_cache()
+
+def verify():
+ debug('')
+ _verify_sdk_dispatch_map()
+
diff --git a/SCons/Tool/MSCommon/MSVC/__init__.py b/SCons/Tool/MSCommon/MSVC/__init__.py
new file mode 100644
index 0000000..afd993f
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/__init__.py
@@ -0,0 +1,50 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""
+Functions for Microsoft Visual C/C++.
+"""
+
+from . import Exceptions
+from . import Util
+
+from . import Dispatcher as _Dispatcher
+
+from . import Config
+from . import Registry
+from . import SetupEnvDefault
+from . import NotFound
+from . import WinSDK
+from . import ScriptArguments
+
+from .NotFound import (
+ set_msvc_notfound_policy,
+ get_msvc_notfound_policy,
+)
+
+def reset():
+ _Dispatcher.reset()
+
+#reset() # testing
+_Dispatcher.verify()
+
diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py
index de78f84..9f35e94 100644
--- a/SCons/Tool/MSCommon/__init__.py
+++ b/SCons/Tool/MSCommon/__init__.py
@@ -32,14 +32,17 @@ import SCons.Util
from SCons.Tool.MSCommon.sdk import mssdk_exists, mssdk_setup_env
+from SCons.Tool.MSCommon.MSVC import (
+ set_msvc_notfound_policy,
+ get_msvc_notfound_policy,
+)
+
from SCons.Tool.MSCommon.vc import (
msvc_exists,
msvc_setup_env_tool,
msvc_setup_env_once,
msvc_version_to_maj_min,
msvc_find_vswhere,
- set_msvc_notfound_policy,
- get_msvc_notfound_policy,
get_msvc_sdk_versions,
)
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index ab05323..5a27f44 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -51,7 +51,6 @@ from collections import (
namedtuple,
OrderedDict,
)
-import enum
import SCons.Util
import SCons.Warnings
@@ -61,9 +60,10 @@ from . import common
from .common import CONFIG_CACHE, debug
from .sdk import get_installed_sdks
-
-class VisualCException(Exception):
- pass
+from . import MSVC
+from .MSVC.Exceptions import (
+ VisualCException
+)
class UnsupportedVersion(VisualCException):
pass
@@ -89,296 +89,10 @@ class MSVCScriptNotFound(VisualCException):
class MSVCUseSettingsError(VisualCException):
pass
-class MSVCVersionNotFound(VisualCException):
- pass
-
-class MSVCArgumentError(VisualCException):
- pass
-
-class MSVCInternalError(VisualCException):
- pass
-
class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault):
pass
-class _Dispatcher:
-
- classrefs = []
-
- @classmethod
- def register(cls, classref):
- cls.classrefs.append(classref)
-
- @classmethod
- def reset(cls):
- debug('reset %s', cls.__name__)
- for classref in cls.classrefs:
- for method in ['reset', '_reset']:
- if not hasattr(classref, method) or not callable(getattr(classref, method, None)):
- continue
- debug('call %s.%s()', classref.__name__, method)
- func = getattr(classref, method)
- func()
-
- @classmethod
- def verify(cls):
- debug('verify %s', cls.__name__)
- for classref in cls.classrefs:
- for method in ['verify', '_verify']:
- if not hasattr(classref, method) or not callable(getattr(classref, method, None)):
- continue
- debug('call %s.%s()', classref.__name__, method)
- func = getattr(classref, method)
- func()
-
-class _Config:
-
- BOOLEAN_SYMBOLS = {}
- BOOLEAN_EXTERNAL = {}
-
- for bool, symbol_list, symbol_case_list in [
- (False, (False, 0, '0', None, ''), ('False', 'No', 'F', 'N')),
- (True, (True, 1, '1'), ('True', 'Yes', 'T', 'Y')),
- ]:
- BOOLEAN_SYMBOLS[bool] = list(symbol_list)
- for symbol in symbol_case_list:
- BOOLEAN_SYMBOLS[bool].extend([symbol, symbol.lower(), symbol.upper()])
-
- for symbol in BOOLEAN_SYMBOLS[bool]:
- BOOLEAN_EXTERNAL[symbol] = bool
-
- MSVC_RUNTIME_DEFINITION = namedtuple('MSVCRuntime', [
- 'vc_runtime',
- 'vc_runtime_numeric',
- 'vc_runtime_alias_list',
- 'vc_runtime_vsdef_list',
- ])
-
- MSVC_RUNTIME_DEFINITION_LIST = []
-
- MSVC_RUNTIME_INTERNAL = {}
- MSVC_RUNTIME_EXTERNAL = {}
-
- for vc_runtime, vc_runtime_numeric, vc_runtime_alias_list in [
- ('140', 140, ['ucrt']),
- ('120', 120, ['msvcr120']),
- ('110', 110, ['msvcr110']),
- ('100', 100, ['msvcr100']),
- ('90', 90, ['msvcr90']),
- ('80', 80, ['msvcr80']),
- ('71', 71, ['msvcr71']),
- ('70', 70, ['msvcr70']),
- ('60', 60, ['msvcrt']),
- ]:
- vc_runtime_def = MSVC_RUNTIME_DEFINITION(
- vc_runtime = vc_runtime,
- vc_runtime_numeric = vc_runtime_numeric,
- vc_runtime_alias_list = vc_runtime_alias_list,
- vc_runtime_vsdef_list = [],
- )
-
- MSVC_RUNTIME_DEFINITION_LIST.append(vc_runtime_def)
-
- MSVC_RUNTIME_INTERNAL[vc_runtime] = vc_runtime_def
- MSVC_RUNTIME_EXTERNAL[vc_runtime] = vc_runtime_def
-
- for vc_runtime_alias in vc_runtime_alias_list:
- MSVC_RUNTIME_EXTERNAL[vc_runtime_alias] = vc_runtime_def
-
- MSVC_BUILDTOOLS_DEFINITION = namedtuple('MSVCBuildtools', [
- 'vc_buildtools',
- 'vc_buildtools_numeric',
- 'vc_version',
- 'vc_version_numeric',
- 'cl_version',
- 'cl_version_numeric',
- 'vc_runtime_def',
- ])
-
- MSVC_BUILDTOOLS_DEFINITION_LIST = []
-
- MSVC_BUILDTOOLS_INTERNAL = {}
- MSVC_BUILDTOOLS_EXTERNAL = {}
-
- VC_VERSION_MAP = {}
-
- for vc_buildtools, vc_version, cl_version, vc_runtime in [
- ('v143', '14.3', '19.3', '140'),
- ('v142', '14.2', '19.2', '140'),
- ('v141', '14.1', '19.1', '140'),
- ('v140', '14.0', '19.0', '140'),
- ('v120', '12.0', '18.0', '120'),
- ('v110', '11.0', '17.0', '110'),
- ('v100', '10.0', '16.0', '100'),
- ('v90', '9.0', '15.0', '90'),
- ('v80', '8.0', '14.0', '80'),
- ('v71', '7.1', '13.1', '71'),
- ('v70', '7.0', '13.0', '70'),
- ('v60', '6.0', '12.0', '60'),
- ]:
-
- vc_runtime_def = MSVC_RUNTIME_INTERNAL[vc_runtime]
-
- vc_buildtools_def = MSVC_BUILDTOOLS_DEFINITION(
- vc_buildtools = vc_buildtools,
- vc_buildtools_numeric = int(vc_buildtools[1:]),
- vc_version = vc_version,
- vc_version_numeric = float(vc_version),
- cl_version = cl_version,
- cl_version_numeric = float(cl_version),
- vc_runtime_def = vc_runtime_def,
- )
-
- MSVC_BUILDTOOLS_DEFINITION_LIST.append(vc_buildtools_def)
-
- MSVC_BUILDTOOLS_INTERNAL[vc_buildtools] = vc_buildtools_def
- MSVC_BUILDTOOLS_EXTERNAL[vc_buildtools] = vc_buildtools_def
- MSVC_BUILDTOOLS_EXTERNAL[vc_version] = vc_buildtools_def
-
- VC_VERSION_MAP[vc_version] = vc_buildtools_def
-
- MSVS_VERSION_INTERNAL = {}
- MSVS_VERSION_EXTERNAL = {}
-
- MSVC_VERSION_INTERNAL = {}
- MSVC_VERSION_EXTERNAL = {}
-
- MSVS_VERSION_MAJOR_MAP = {}
-
- CL_VERSION_MAP = {}
-
- MSVC_SDK_VERSIONS = set()
-
- VISUALSTUDIO_DEFINITION = namedtuple('VisualStudioDefinition', [
- 'vs_product',
- 'vs_product_alias_list',
- 'vs_version',
- 'vs_version_major',
- 'vs_envvar',
- 'vs_express',
- 'vs_lookup',
- 'vc_sdk_versions',
- 'vc_ucrt_versions',
- 'vc_uwp',
- 'vc_buildtools_def',
- 'vc_buildtools_all',
- ])
-
- VISUALSTUDIO_DEFINITION_LIST = []
-
- VS_PRODUCT_ALIAS = {
- '1998': ['6']
- }
-
- # vs_envvar: VisualStudioVersion defined in environment for MSVS 2012 and later
- # MSVS 2010 and earlier cl_version -> vs_def is a 1:1 mapping
- # SDK attached to product or buildtools?
- for vs_product, vs_version, vs_envvar, vs_express, vs_lookup, vc_sdk, vc_ucrt, vc_uwp, vc_buildtools_all in [
- ('2022', '17.0', True, False, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v143', 'v142', 'v141', 'v140']),
- ('2019', '16.0', True, False, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v142', 'v141', 'v140']),
- ('2017', '15.0', True, True, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v141', 'v140']),
- ('2015', '14.0', True, True, 'registry', ['10.0', '8.1'], ['10'], 'store', ['v140']),
- ('2013', '12.0', True, True, 'registry', None, None, None, ['v120']),
- ('2012', '11.0', True, True, 'registry', None, None, None, ['v110']),
- ('2010', '10.0', False, True, 'registry', None, None, None, ['v100']),
- ('2008', '9.0', False, True, 'registry', None, None, None, ['v90']),
- ('2005', '8.0', False, True, 'registry', None, None, None, ['v80']),
- ('2003', '7.1', False, False, 'registry', None, None, None, ['v71']),
- ('2002', '7.0', False, False, 'registry', None, None, None, ['v70']),
- ('1998', '6.0', False, False, 'registry', None, None, None, ['v60']),
- ]:
-
- vs_version_major = vs_version.split('.')[0]
-
- vc_buildtools_def = MSVC_BUILDTOOLS_INTERNAL[vc_buildtools_all[0]]
-
- vs_def = VISUALSTUDIO_DEFINITION(
- vs_product = vs_product,
- vs_product_alias_list = [],
- vs_version = vs_version,
- vs_version_major = vs_version_major,
- vs_envvar = vs_envvar,
- vs_express = vs_express,
- vs_lookup = vs_lookup,
- vc_sdk_versions = vc_sdk,
- vc_ucrt_versions = vc_ucrt,
- vc_uwp = vc_uwp,
- vc_buildtools_def = vc_buildtools_def,
- vc_buildtools_all = vc_buildtools_all,
- )
-
- VISUALSTUDIO_DEFINITION_LIST.append(vs_def)
-
- vc_buildtools_def.vc_runtime_def.vc_runtime_vsdef_list.append(vs_def)
-
- MSVS_VERSION_INTERNAL[vs_product] = vs_def
- MSVS_VERSION_EXTERNAL[vs_product] = vs_def
- MSVS_VERSION_EXTERNAL[vs_version] = vs_def
-
- MSVC_VERSION_INTERNAL[vc_buildtools_def.vc_version] = vs_def
- MSVC_VERSION_EXTERNAL[vs_product] = vs_def
- MSVC_VERSION_EXTERNAL[vc_buildtools_def.vc_version] = vs_def
- MSVC_VERSION_EXTERNAL[vc_buildtools_def.vc_buildtools] = vs_def
-
- if vs_product in VS_PRODUCT_ALIAS:
- for vs_product_alias in VS_PRODUCT_ALIAS[vs_product]:
- vs_def.vs_product_alias_list.append(vs_product_alias)
- MSVS_VERSION_EXTERNAL[vs_product_alias] = vs_def
- MSVC_VERSION_EXTERNAL[vs_product_alias] = vs_def
-
- MSVS_VERSION_MAJOR_MAP[vs_version_major] = vs_def
-
- CL_VERSION_MAP[vc_buildtools_def.cl_version] = vs_def
-
- if not vc_sdk:
- continue
-
- MSVC_SDK_VERSIONS.update(vc_sdk)
-
- # convert string version set to string version list ranked in descending order
- MSVC_SDK_VERSIONS = [str(f) for f in sorted([float(s) for s in MSVC_SDK_VERSIONS], reverse=True)]
-
- MSVS_VERSION_LEGACY = {}
- MSVC_VERSION_LEGACY = {}
-
- for vdict in (MSVS_VERSION_EXTERNAL, MSVC_VERSION_INTERNAL):
- for key, vs_def in vdict.items():
- if key not in MSVS_VERSION_LEGACY:
- MSVS_VERSION_LEGACY[key] = vs_def
- MSVC_VERSION_LEGACY[key] = vs_def
-
-# MSVC_NOTFOUND_POLICY definition:
-# error: raise exception
-# warning: issue warning and continue
-# ignore: continue
-
-_MSVC_NOTFOUND_POLICY_DEFINITION = namedtuple('MSVCNotFoundPolicyDefinition', [
- 'value',
- 'symbol',
-])
-
-_MSVC_NOTFOUND_POLICY_INTERNAL = {}
-_MSVC_NOTFOUND_POLICY_EXTERNAL = {}
-
-for policy_value, policy_symbol_list in [
- (True, ['Error', 'Exception']),
- (False, ['Warning', 'Warn']),
- (None, ['Ignore', 'Suppress']),
-]:
-
- policy_symbol = policy_symbol_list[0].lower()
- policy_def = _MSVC_NOTFOUND_POLICY_DEFINITION(policy_value, policy_symbol)
-
- _MSVC_NOTFOUND_POLICY_INTERNAL[policy_symbol] = policy_def
-
- for policy_symbol in policy_symbol_list:
- _MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.lower()] = policy_def
- _MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol] = policy_def
- _MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.upper()] = policy_def
-
-_MSVC_NOTFOUND_POLICY_DEF = _MSVC_NOTFOUND_POLICY_INTERNAL['warning']
-
# Dict to 'canonalize' the arch
_ARCH_TO_CANONICAL = {
"amd64" : "amd64",
@@ -1253,7 +967,7 @@ def reset_installed_vcs():
"""Make it try again to find VC. This is just for the tests."""
global __INSTALLED_VCS_RUN
__INSTALLED_VCS_RUN = None
- _Dispatcher.reset()
+ MSVC.reset()
def get_default_installed_msvc(env=None):
vcs = get_installed_vcs(env)
@@ -1344,295 +1058,6 @@ def script_env(script, args=None):
return cache_data
-def _msvc_notfound_policy_lookup(symbol):
-
- try:
- notfound_policy_def = _MSVC_NOTFOUND_POLICY_EXTERNAL[symbol]
- except KeyError:
- err_msg = "Value specified for MSVC_NOTFOUND_POLICY is not supported: {}.\n" \
- " Valid values are: {}".format(
- repr(symbol),
- ', '.join([repr(s) for s in _MSVC_NOTFOUND_POLICY_EXTERNAL.keys()])
- )
- raise ValueError(err_msg)
-
- return notfound_policy_def
-
-def set_msvc_notfound_policy(MSVC_NOTFOUND_POLICY=None):
- """ Set the default policy when MSVC is not found.
-
- Args:
- MSVC_NOTFOUND_POLICY:
- string representing the policy behavior
- when MSVC is not found or None
-
- Returns:
- The previous policy is returned when the MSVC_NOTFOUND_POLICY argument
- is not None. The active policy is returned when the MSVC_NOTFOUND_POLICY
- argument is None.
-
- """
- global _MSVC_NOTFOUND_POLICY_DEF
-
- prev_policy = _MSVC_NOTFOUND_POLICY_DEF.symbol
-
- policy = MSVC_NOTFOUND_POLICY
- if policy is not None:
- _MSVC_NOTFOUND_POLICY_DEF = _msvc_notfound_policy_lookup(policy)
-
- debug(
- 'prev_policy=%s, set_policy=%s, policy.symbol=%s, policy.value=%s',
- repr(prev_policy), repr(policy),
- repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value)
- )
-
- return prev_policy
-
-def get_msvc_notfound_policy():
- """Return the active policy when MSVC is not found."""
- debug(
- 'policy.symbol=%s, policy.value=%s',
- repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value)
- )
- return _MSVC_NOTFOUND_POLICY_DEF.symbol
-
-def _msvc_notfound_policy_handler(env, msg):
-
- if env and 'MSVC_NOTFOUND_POLICY' in env:
- # environment setting
- notfound_policy_src = 'environment'
- policy = env['MSVC_NOTFOUND_POLICY']
- if policy is not None:
- # user policy request
- notfound_policy_def = _msvc_notfound_policy_lookup(policy)
- else:
- # active global setting
- notfound_policy_def = _MSVC_NOTFOUND_POLICY_DEF
- else:
- # active global setting
- notfound_policy_src = 'default'
- policy = None
- notfound_policy_def = _MSVC_NOTFOUND_POLICY_DEF
-
- debug(
- 'source=%s, set_policy=%s, policy.symbol=%s, policy.value=%s',
- notfound_policy_src, repr(policy), repr(notfound_policy_def.symbol), repr(notfound_policy_def.value)
- )
-
- if notfound_policy_def.value is None:
- # ignore
- pass
- elif notfound_policy_def.value:
- raise MSVCVersionNotFound(msg)
- else:
- SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
-
-class _MSVCSetupEnvDefault:
- """
- Determine if and/or when an error/warning should be issued when there
- are no versions of msvc installed. If there is at least one version of
- msvc installed, these routines do (almost) nothing.
-
- Notes:
- * When msvc is the default compiler because there are no compilers
- installed, a build may fail due to the cl.exe command not being
- recognized. Currently, there is no easy way to detect during
- msvc initialization if the default environment will be used later
- to build a program and/or library. There is no error/warning
- as there are legitimate SCons uses that do not require a c compiler.
- * As implemented, the default is that a warning is issued. This can
- be changed globally via the function set_msvc_notfound_policy and/or
- through the environment via the MSVC_NOTFOUND_POLICY variable.
- """
-
- separator = r';'
-
- need_init = True
-
- @classmethod
- def reset(cls):
- debug('msvc default:init')
- cls.n_setup = 0 # number of calls to msvc_setup_env_once
- cls.default_ismsvc = False # is msvc the default compiler
- cls.default_tools_re_list = [] # list of default tools regular expressions
- cls.msvc_tools_init = set() # tools registered via msvc_exists
- cls.msvc_tools = None # tools registered via msvc_setup_env_once
- cls.msvc_installed = False # is msvc installed (vcs_installed > 0)
- cls.msvc_nodefault = False # is there a default version of msvc
- cls.need_init = True # reset initialization indicator
-
- @classmethod
- def _initialize(cls, env):
- if cls.need_init:
- cls.reset()
- cls.need_init = False
- vcs = get_installed_vcs(env)
- cls.msvc_installed = len(vcs) > 0
- debug('msvc default:msvc_installed=%s', cls.msvc_installed)
-
- @classmethod
- def register_tool(cls, env, tool):
- debug('msvc default:tool=%s', tool)
- if cls.need_init:
- cls._initialize(env)
- if cls.msvc_installed:
- return None
- if not tool:
- return None
- if cls.n_setup == 0:
- if tool not in cls.msvc_tools_init:
- cls.msvc_tools_init.add(tool)
- debug('msvc default:tool=%s, msvc_tools_init=%s', tool, cls.msvc_tools_init)
- return None
- if tool not in cls.msvc_tools:
- cls.msvc_tools.add(tool)
- debug('msvc default:tool=%s, msvc_tools=%s', tool, cls.msvc_tools)
-
- @classmethod
- def register_setup(cls, env):
- debug('msvc default')
- if cls.need_init:
- cls._initialize(env)
- cls.n_setup += 1
- if not cls.msvc_installed:
- cls.msvc_tools = set(cls.msvc_tools_init)
- if cls.n_setup == 1:
- tool_list = env.get('TOOLS', None)
- if tool_list and tool_list[0] == 'default':
- if len(tool_list) > 1 and tool_list[1] in cls.msvc_tools:
- # msvc tools are the default compiler
- cls.default_ismsvc = True
- cls.msvc_nodefault = False
- debug(
- 'msvc default:n_setup=%d, msvc_installed=%s, default_ismsvc=%s',
- cls.n_setup, cls.msvc_installed, cls.default_ismsvc
- )
-
- @classmethod
- def set_nodefault(cls):
- # default msvc version, msvc not installed
- cls.msvc_nodefault = True
- debug('msvc default:msvc_nodefault=%s', cls.msvc_nodefault)
-
- @classmethod
- def register_iserror(cls, env, tool):
-
- cls.register_tool(env, tool)
-
- if cls.msvc_installed:
- # msvc installed
- return None
-
- if not cls.msvc_nodefault:
- # msvc version specified
- return None
-
- tool_list = env.get('TOOLS', None)
- if not tool_list:
- # tool list is empty
- return None
-
- debug(
- 'msvc default:n_setup=%s, default_ismsvc=%s, msvc_tools=%s, tool_list=%s',
- cls.n_setup, cls.default_ismsvc, cls.msvc_tools, tool_list
- )
-
- if not cls.default_ismsvc:
-
- # Summary:
- # * msvc is not installed
- # * msvc version not specified (default)
- # * msvc is not the default compiler
-
- # construct tools set
- tools_set = set(tool_list)
-
- else:
-
- if cls.n_setup == 1:
- # first setup and msvc is default compiler:
- # build default tools regex for current tool state
- tools = cls.separator.join(tool_list)
- tools_nchar = len(tools)
- debug('msvc default:add regex:nchar=%d, tools=%s', tools_nchar, tools)
- re_default_tools = re.compile(re.escape(tools))
- cls.default_tools_re_list.insert(0, (tools_nchar, re_default_tools))
- # early exit: no error for default environment when msvc is not installed
- return None
-
- # Summary:
- # * msvc is not installed
- # * msvc version not specified (default)
- # * environment tools list is not empty
- # * default tools regex list constructed
- # * msvc tools set constructed
- #
- # Algorithm using tools string and sets:
- # * convert environment tools list to a string
- # * iteratively remove default tools sequences via regex
- # substition list built from longest sequence (first)
- # to shortest sequence (last)
- # * build environment tools set with remaining tools
- # * compute intersection of environment tools and msvc tools sets
- # * if the intersection is:
- # empty - no error: default tools and/or no additional msvc tools
- # not empty - error: user specified one or more msvc tool(s)
- #
- # This will not produce an error or warning when there are no
- # msvc installed instances nor any other recognized compilers
- # and the default environment is needed for a build. The msvc
- # compiler is forcibly added to the environment tools list when
- # there are no compilers installed on win32. In this case, cl.exe
- # will not be found on the path resulting in a failed build.
-
- # construct tools string
- tools = cls.separator.join(tool_list)
- tools_nchar = len(tools)
-
- debug('msvc default:check tools:nchar=%d, tools=%s', tools_nchar, tools)
-
- # iteratively remove default tool sequences (longest to shortest)
- re_nchar_min, re_tools_min = cls.default_tools_re_list[-1]
- if tools_nchar >= re_nchar_min and re_tools_min.search(tools):
- # minimum characters satisfied and minimum pattern exists
- for re_nchar, re_default_tool in cls.default_tools_re_list:
- if tools_nchar < re_nchar:
- # not enough characters for pattern
- continue
- tools = re_default_tool.sub('', tools).strip(cls.separator)
- tools_nchar = len(tools)
- debug('msvc default:check tools:nchar=%d, tools=%s', tools_nchar, tools)
- if tools_nchar < re_nchar_min or not re_tools_min.search(tools):
- # less than minimum characters or minimum pattern does not exist
- break
-
- # construct non-default list(s) tools set
- tools_set = {msvc_tool for msvc_tool in tools.split(cls.separator) if msvc_tool}
-
- debug('msvc default:tools=%s', tools_set)
- if not tools_set:
- return None
-
- # compute intersection of remaining tools set and msvc tools set
- tools_found = cls.msvc_tools.intersection(tools_set)
- debug('msvc default:tools_exist=%s', tools_found)
- if not tools_found:
- return None
-
- # construct in same order as tools list
- tools_found_list = []
- seen_tool = set()
- for tool in tool_list:
- if tool not in seen_tool:
- seen_tool.add(tool)
- if tool in tools_found:
- tools_found_list.append(tool)
-
- # return tool list in order presented
- return tools_found_list
-
-_Dispatcher.register(_MSVCSetupEnvDefault)
-
def get_default_version(env):
msvc_version = env.get('MSVC_VERSION')
msvs_version = env.get('MSVS_VERSION')
@@ -1673,16 +1098,16 @@ def msvc_setup_env_once(env, tool=None):
if not has_run:
debug('tool=%s', repr(tool))
- _MSVCSetupEnvDefault.register_setup(env)
+ MSVC.SetupEnvDefault.register_setup(env, msvc_exists)
msvc_setup_env(env)
env["MSVC_SETUP_RUN"] = True
- req_tools = _MSVCSetupEnvDefault.register_iserror(env, tool)
+ req_tools = MSVC.SetupEnvDefault.register_iserror(env, tool, msvc_exists)
if req_tools:
msg = "No versions of the MSVC compiler were found.\n" \
" Visual Studio C/C++ compilers may not be set correctly.\n" \
" Requested tool(s) are: {}".format(req_tools)
- _msvc_notfound_policy_handler(env, msg)
+ MSVC.Notfound.policy_handler(env, msg)
def msvc_find_valid_batch_script(env, version):
"""Find and execute appropriate batch script to set up build env.
@@ -1724,7 +1149,7 @@ def msvc_find_valid_batch_script(env, version):
debug('use_script 2 %s, args:%s', repr(vc_script), arg)
found = None
if vc_script:
- arg = _ScriptArguments.msvc_script_arguments(env, version, vc_dir, arg)
+ arg = MSVC.ScriptArguments.msvc_script_arguments(env, version, vc_dir, arg)
try:
d = script_env(vc_script, args=arg)
found = vc_script
@@ -1769,7 +1194,7 @@ def msvc_find_valid_batch_script(env, version):
" No versions of the MSVC compiler were found.\n" \
" Visual Studio C/C++ compilers may not be set correctly".format(version)
- _msvc_notfound_policy_handler(env, msg)
+ MSVC.NotFound.policy_handler(env, msg)
return d
@@ -1805,7 +1230,7 @@ def msvc_setup_env(env):
version = get_default_version(env)
if version is None:
if not msvc_setup_env_user(env):
- _MSVCSetupEnvDefault.set_nodefault()
+ MSVC.SetupEnvDefault.set_nodefault()
return None
# XXX: we set-up both MSVS version for backward
@@ -1899,7 +1324,7 @@ def msvc_setup_env_user(env=None):
def msvc_setup_env_tool(env=None, version=None, tool=None):
debug('tool=%s, version=%s', repr(tool), repr(version))
- _MSVCSetupEnvDefault.register_tool(env, tool)
+ MSVC.SetupEnvDefault.register_tool(env, tool, msvc_exists)
rval = False
if not rval and msvc_exists(env, version):
rval = True
@@ -1920,1043 +1345,6 @@ def get_msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False):
debug('no msvc versions detected')
return rval
- rval = _WindowsSDK.get_msvc_sdk_version_list(msvc_version, msvc_uwp_app)
+ rval = MSVC.WinSDK.get_msvc_sdk_version_list(msvc_version, msvc_uwp_app)
return rval
-class _Util:
-
- @staticmethod
- def listdir_dirs(p):
- dirs = []
- for dir_name in os.listdir(p):
- dir_path = os.path.join(p, dir_name)
- if os.path.isdir(dir_path):
- dirs.append((dir_name, dir_path))
- return dirs
-
- @staticmethod
- def process_path(p):
- if p:
- p = os.path.normpath(p)
- p = os.path.realpath(p)
- p = os.path.normcase(p)
- return p
-
-class _Registry:
-
- def read_value(hkey, subkey_valname):
- try:
- rval = common.read_reg(subkey_valname, hkroot=hkey)
- except OSError:
- debug('OSError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname))
- return None
- except IndexError:
- debug('IndexError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname))
- return None
- debug('hkey=%s, subkey=%s, rval=%s', repr(hkey), repr(subkey_valname), repr(rval))
- return rval
-
- @classmethod
- def registry_query_path(cls, key, val, suffix):
- extval = val + '\\' + suffix if suffix else val
- qpath = cls.read_value(key, extval)
- if qpath and os.path.exists(qpath):
- qpath = _Util.process_path(qpath)
- else:
- qpath = None
- return (qpath, key, val, extval)
-
- REG_SOFTWARE_MICROSOFT = [
- (SCons.Util.HKEY_LOCAL_MACHINE, r'Software\Wow6432Node\Microsoft'),
- (SCons.Util.HKEY_CURRENT_USER, r'Software\Wow6432Node\Microsoft'), # SDK queries
- (SCons.Util.HKEY_LOCAL_MACHINE, r'Software\Microsoft'),
- (SCons.Util.HKEY_CURRENT_USER, r'Software\Microsoft'),
- ]
-
- @classmethod
- def microsoft_query_paths(cls, suffix, usrval=None):
- paths = []
- records = []
- for key, val in cls.REG_SOFTWARE_MICROSOFT:
- extval = val + '\\' + suffix if suffix else val
- qpath = cls.read_value(key, extval)
- if qpath and os.path.exists(qpath):
- qpath = _Util.process_path(qpath)
- if qpath not in paths:
- paths.append(qpath)
- records.append((qpath, key, val, extval, usrval))
- return records
-
- @classmethod
- def microsoft_query_keys(cls, suffix, usrval=None):
- records = []
- for key, val in cls.REG_SOFTWARE_MICROSOFT:
- extval = val + '\\' + suffix if suffix else val
- rval = cls.read_value(key, extval)
- if rval:
- records.append((key, val, extval, usrval))
- return records
-
- @classmethod
- def microsoft_sdks(cls, version):
- return '\\'.join([r'Microsoft SDKs\Windows', 'v' + version, r'InstallationFolder'])
-
- @classmethod
- def sdk_query_paths(cls, version):
- q = cls.microsoft_sdks(version)
- return cls.microsoft_query_paths(q)
-
- @classmethod
- def windows_kits(cls, version):
- return r'Windows Kits\Installed Roots\KitsRoot' + version
-
- @classmethod
- def windows_kit_query_paths(cls, version):
- q = cls.windows_kits(version)
- return cls.microsoft_query_paths(q)
-
-class _WindowsSDK:
-
- @classmethod
- def _new_sdk_map(cls):
- sdk_map = {
- 'desktop': [],
- 'uwp': [],
- }
- return sdk_map
-
- @classmethod
- def _sdk_10_layout(cls, version):
-
- folder_prefix = version + '.'
-
- sdk_map = cls._new_sdk_map()
-
- sdk_roots = _Registry.sdk_query_paths(version)
-
- sdk_version_platform_seen = set()
- sdk_roots_seen = set()
-
- for sdk_t in sdk_roots:
-
- sdk_root = sdk_t[0]
- if sdk_root in sdk_roots_seen:
- continue
- sdk_roots_seen.add(sdk_root)
-
- if not os.path.exists(sdk_root):
- continue
-
- sdk_include_path = os.path.join(sdk_root, 'include')
- if not os.path.exists(sdk_include_path):
- continue
-
- for version_nbr, version_nbr_path in _Util.listdir_dirs(sdk_include_path):
-
- if not version_nbr.startswith(folder_prefix):
- continue
-
- sdk_inc_path = _Util.process_path(os.path.join(version_nbr_path, 'um'))
- if not os.path.exists(sdk_inc_path):
- continue
-
- for platform_type, sdk_inc_file in [
- ('desktop', 'winsdkver.h'),
- ('uwp', 'windows.h'),
- ]:
-
- if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)):
- continue
-
- key = (version_nbr, platform_type)
- if key in sdk_version_platform_seen:
- continue
- sdk_version_platform_seen.add(key)
-
- sdk_map[platform_type].append(version_nbr)
-
- for key, val in sdk_map.items():
- val.sort(reverse=True)
-
- return sdk_map
-
- @classmethod
- def _sdk_81_layout(cls, version):
-
- version_nbr = version
-
- sdk_map = cls._new_sdk_map()
-
- sdk_roots = _Registry.sdk_query_paths(version)
-
- sdk_version_platform_seen = set()
- sdk_roots_seen = set()
-
- for sdk_t in sdk_roots:
-
- sdk_root = sdk_t[0]
- if sdk_root in sdk_roots_seen:
- continue
- sdk_roots_seen.add(sdk_root)
-
- # msvc does not check for existence of root or other files
-
- sdk_inc_path = _Util.process_path(os.path.join(sdk_root, r'include\um'))
- if not os.path.exists(sdk_inc_path):
- continue
-
- for platform_type, sdk_inc_file in [
- ('desktop', 'winsdkver.h'),
- ('uwp', 'windows.h'),
- ]:
-
- if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)):
- continue
-
- key = (version_nbr, platform_type)
- if key in sdk_version_platform_seen:
- continue
- sdk_version_platform_seen.add(key)
-
- sdk_map[platform_type].append(version_nbr)
-
- for key, val in sdk_map.items():
- val.sort(reverse=True)
-
- return sdk_map
-
- sdk_map_cache = {}
- sdk_cache = {}
-
- @classmethod
- def _reset_sdk_cache(cls):
- debug('reset %s: sdk cache', cls.__name__)
- cls._sdk_map_cache = {}
- cls._sdk_cache = {}
-
- @classmethod
- def _sdk_10(cls, key, reg_version):
- if key in cls.sdk_map_cache:
- sdk_map = cls.sdk_map_cache[key]
- else:
- sdk_map = cls._sdk_10_layout(reg_version)
- cls.sdk_map_cache[key] = sdk_map
- return sdk_map
-
- @classmethod
- def _sdk_81(cls, key, reg_version):
- if key in cls.sdk_map_cache:
- sdk_map = cls.sdk_map_cache[key]
- else:
- sdk_map = cls._sdk_81_layout(reg_version)
- cls.sdk_map_cache[key] = sdk_map
- return sdk_map
-
- @classmethod
- def _combine_sdk_map_list(cls, sdk_map_list):
- combined_sdk_map = cls._new_sdk_map()
- for sdk_map in sdk_map_list:
- for key, val in sdk_map.items():
- combined_sdk_map[key].extend(val)
- return combined_sdk_map
-
- sdk_dispatch_map = None
-
- @classmethod
- def _init_sdk_dispatch_map(cls):
- cls.sdk_dispatch_map = {
- '10.0': (cls._sdk_10, '10.0'),
- '8.1': (cls._sdk_81, '8.1'),
- }
-
- @classmethod
- def _verify_sdk_dispatch_map(cls):
- debug('%s verify sdk_dispatch_map', cls.__name__)
- cls._init_sdk_dispatch_map()
- for sdk_version in _Config.MSVC_SDK_VERSIONS:
- if sdk_version in cls.sdk_dispatch_map:
- continue
- err_msg = 'sdk version {} not in {}.sdk_dispatch_map'.format(sdk_version, cls.__name__)
- raise MSVCInternalError(err_msg)
- return None
-
- @classmethod
- def _version_list_sdk_map(cls, version_list):
-
- if not cls.sdk_dispatch_map:
- cls._init_sdk_dispatch_map()
-
- sdk_map_list = []
- for version in version_list:
- func, reg_version = cls.sdk_dispatch_map[version]
- sdk_map = func(version, reg_version)
- sdk_map_list.append(sdk_map)
-
- combined_sdk_map = cls._combine_sdk_map_list(sdk_map_list)
- return combined_sdk_map
-
- @classmethod
- def _sdk_map(cls, version_list):
- key = tuple(version_list)
- if key in cls.sdk_cache:
- sdk_map = cls.sdk_cache[key]
- else:
- version_numlist = [float(v) for v in version_list]
- version_numlist.sort(reverse=True)
- key = tuple([str(v) for v in version_numlist])
- sdk_map = cls._version_list_sdk_map(key)
- cls.sdk_cache[key] = sdk_map
- return sdk_map
-
- @classmethod
- def get_sdk_version_list(cls, version_list, platform_type):
- sdk_map = cls._sdk_map(version_list)
- sdk_list = sdk_map.get(platform_type, [])
- return sdk_list
-
- @classmethod
- def get_msvc_sdk_version_list(cls, msvc_version=None, msvc_uwp_app=False):
- debug('msvc_version=%s, msvc_uwp_app=%s', repr(msvc_version), repr(msvc_uwp_app))
-
- sdk_versions = []
-
- verstr = get_msvc_version_numeric(msvc_version)
- vs_def = _Config.MSVC_VERSION_EXTERNAL.get(verstr, None)
- if not vs_def:
- debug('vs_def is not defined')
- return sdk_versions
-
- is_uwp = True if msvc_uwp_app in _Config.BOOLEAN_SYMBOLS[True] else False
- platform_type = 'uwp' if is_uwp else 'desktop'
- sdk_list = _WindowsSDK.get_sdk_version_list(vs_def.vc_sdk_versions, platform_type)
-
- sdk_versions.extend(sdk_list)
- debug('sdk_versions=%s', repr(sdk_versions))
-
- return sdk_versions
-
- @classmethod
- def reset(cls):
- debug('reset %s', cls.__name__)
- cls._reset_sdk_cache()
-
- @classmethod
- def verify(cls):
- debug('verify %s', cls.__name__)
- cls._verify_sdk_dispatch_map()
-
-_Dispatcher.register(_WindowsSDK)
-
-class _ScriptArguments:
-
- # TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last?
- re_sdk_version_100 = re.compile(r'^10[.][0-9][.][0-9]{5}[.][0-9]{1}$')
- re_sdk_version_81 = re.compile(r'^8[.]1$')
-
- re_sdk_dispatch_map = {
- '10.0': re_sdk_version_100,
- '8.1': re_sdk_version_81,
- }
-
- @classmethod
- def _verify_re_sdk_dispatch_map(cls):
- debug('%s verify re_sdk_dispatch_map', cls.__name__)
- for sdk_version in _Config.MSVC_SDK_VERSIONS:
- if sdk_version in cls.re_sdk_dispatch_map:
- continue
- err_msg = 'sdk version {} not in {}.re_sdk_dispatch_map'.format(sdk_version, cls.__name__)
- raise MSVCInternalError(err_msg)
- return None
-
- # capture msvc version
- re_toolset_version = re.compile(r'^(?P[1-9][0-9]?[.][0-9])[0-9.]*$', re.IGNORECASE)
-
- re_toolset_full = re.compile(r'''^(?:
- (?:[1-9][0-9][.][0-9]{1,2})| # XX.Y - XX.YY
- (?:[1-9][0-9][.][0-9]{2}[.][0-9]{1,5}) # XX.YY.Z - XX.YY.ZZZZZ
- )$''', re.VERBOSE)
-
- re_toolset_140 = re.compile(r'''^(?:
- (?:14[.]0{1,2})| # 14.0 - 14.00
- (?:14[.]0{2}[.]0{1,5}) # 14.00.0 - 14.00.00000
- )$''', re.VERBOSE)
-
- # valid SxS formats will be matched with re_toolset_full: match 3 '.' format
- re_toolset_sxs = re.compile(r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$')
-
- # MSVC_SCRIPT_ARGS
- re_vcvars_uwp = re.compile(r'(?:(?(?:uwp|store))(?:(?!\S)|$)',re.IGNORECASE)
- re_vcvars_sdk = re.compile(r'(?:(?(?:[1-9][0-9]*[.]\S*))(?:(?!\S)|$)',re.IGNORECASE)
- re_vcvars_toolset = re.compile(r'(?:(?(?:[-]{1,2}|[/])vcvars_ver[=](?P\S*))(?:(?!\S)|$)', re.IGNORECASE)
- re_vcvars_spectre = re.compile(r'(?:(?(?:[-]{1,2}|[/])vcvars_spectre_libs[=](?P\S*))(?:(?!\S)|$)',re.IGNORECASE)
-
- # Force default sdk argument
- MSVC_FORCE_DEFAULT_SDK = False
-
- # Force default toolset argument
- MSVC_FORCE_DEFAULT_TOOLSET = False
-
- # MSVC batch file arguments:
- #
- # VS2022: UWP, SDK, TOOLSET, SPECTRE
- # VS2019: UWP, SDK, TOOLSET, SPECTRE
- # VS2017: UWP, SDK, TOOLSET, SPECTRE
- # VS2015: UWP, SDK
- #
- # MSVC_SCRIPT_ARGS: VS2015+
- #
- # MSVC_UWP_APP: VS2015+
- # MSVC_SDK_VERSION: VS2015+
- # MSVC_TOOLSET_VERSION: VS2017+
- # MSVC_SPECTRE_LIBS: VS2017+
-
- @enum.unique
- class SortOrder(enum.IntEnum):
- ARCH = 0 # arch
- UWP = 1 # MSVC_UWP_APP
- SDK = 2 # MSVC_SDK_VERSION
- TOOLSET = 3 # MSVC_TOOLSET_VERSION
- SPECTRE = 4 # MSVC_SPECTRE_LIBS
- USER = 5 # MSVC_SCRIPT_ARGS
-
- VS2019 = _Config.MSVS_VERSION_INTERNAL['2019']
- VS2017 = _Config.MSVS_VERSION_INTERNAL['2017']
- VS2015 = _Config.MSVS_VERSION_INTERNAL['2015']
-
- MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [
- 'version', # fully qualified msvc version (e.g., '14.1Exp')
- 'vs_def',
- ])
-
- @classmethod
- def _msvc_version(cls, version):
-
- verstr = get_msvc_version_numeric(version)
- vs_def = _Config.MSVC_VERSION_INTERNAL[verstr]
-
- version_args = cls.MSVC_VERSION_ARGS_DEFINITION(
- version = version,
- vs_def = vs_def,
- )
-
- return version_args
-
- @classmethod
- def _msvc_script_argument_uwp(cls, env, msvc, arglist):
-
- uwp_app = env['MSVC_UWP_APP']
- debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app))
-
- if not uwp_app:
- return None
-
- if uwp_app not in _Config.BOOLEAN_SYMBOLS[True]:
- return None
-
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric:
- debug(
- 'invalid: msvc version constraint: %s < %s VS2015',
- repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
- repr(cls.VS2015.vc_buildtools_def.vc_version_numeric)
- )
- err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
- repr(uwp_app), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version)
- )
- raise MSVCArgumentError(err_msg)
-
- # VS2017+ rewrites uwp => store for 14.0 toolset
- uwp_arg = msvc.vs_def.vc_uwp
-
- # store/uwp may not be fully installed
- argpair = (cls.SortOrder.UWP, uwp_arg)
- arglist.append(argpair)
-
- return uwp_arg
-
- @classmethod
- def _user_script_argument_uwp(cls, env, uwp, user_argstr):
-
- matches = [m for m in cls.re_vcvars_uwp.finditer(user_argstr)]
- if not matches:
- return None
-
- if len(matches) > 1:
- debug('multiple uwp declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
- err_msg = "multiple uwp declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
- raise MSVCArgumentError(err_msg)
-
- if not uwp:
- return None
-
- env_argstr = env.get('MSVC_UWP_APP','')
- debug('multiple uwp declarations: MSVC_UWP_APP=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
-
- err_msg = "multiple uwp declarations: MSVC_UWP_APP={} and MSVC_SCRIPT_ARGS={}".format(
- repr(env_argstr), repr(user_argstr)
- )
-
- raise MSVCArgumentError(err_msg)
-
- @classmethod
- def _msvc_script_argument_sdk_constraints(cls, msvc, sdk_version):
-
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric:
- debug(
- 'invalid: msvc_version constraint: %s < %s VS2015',
- repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
- repr(cls.VS2015.vc_buildtools_def.vc_version_numeric)
- )
- err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
- repr(sdk_version), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version)
- )
- return err_msg
-
- for msvc_sdk_version in msvc.vs_def.vc_sdk_versions:
- re_sdk_version = cls.re_sdk_dispatch_map[msvc_sdk_version]
- if re_sdk_version.match(sdk_version):
- debug('valid: sdk_version=%s', repr(sdk_version))
- return None
-
- debug('invalid: method exit: sdk_version=%s', repr(sdk_version))
- err_msg = "MSVC_SDK_VERSION ({}) is not supported".format(repr(sdk_version))
- return err_msg
-
- @classmethod
- def _msvc_script_argument_sdk(cls, env, msvc, platform_type, arglist):
-
- sdk_version = env['MSVC_SDK_VERSION']
- debug(
- 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, platform_type=%s',
- repr(msvc.version), repr(sdk_version), repr(platform_type)
- )
-
- if not sdk_version:
- return None
-
- err_msg = cls._msvc_script_argument_sdk_constraints(msvc, sdk_version)
- if err_msg:
- raise MSVCArgumentError(err_msg)
-
- sdk_list = _WindowsSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type)
-
- if sdk_version not in sdk_list:
- err_msg = "MSVC_SDK_VERSION {} not found for platform type {}".format(
- repr(sdk_version), repr(platform_type)
- )
- raise MSVCArgumentError(err_msg)
-
- argpair = (cls.SortOrder.SDK, sdk_version)
- arglist.append(argpair)
-
- return sdk_version
-
- @classmethod
- def _msvc_script_default_sdk(cls, env, msvc, platform_type, arglist):
-
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric:
- return None
-
- sdk_list = _WindowsSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type)
- if not len(sdk_list):
- return None
-
- sdk_default = sdk_list[0]
-
- debug(
- 'MSVC_VERSION=%s, sdk_default=%s, platform_type=%s',
- repr(msvc.version), repr(sdk_default), repr(platform_type)
- )
-
- argpair = (cls.SortOrder.SDK, sdk_default)
- arglist.append(argpair)
-
- return sdk_default
-
- @classmethod
- def _user_script_argument_sdk(cls, env, sdk_version, user_argstr):
-
- matches = [m for m in cls.re_vcvars_sdk.finditer(user_argstr)]
- if not matches:
- return None
-
- if len(matches) > 1:
- debug('multiple sdk version declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
- err_msg = "multiple sdk version declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
- raise MSVCArgumentError(err_msg)
-
- if not sdk_version:
- user_sdk = matches[0].group('sdk')
- return user_sdk
-
- env_argstr = env.get('MSVC_SDK_VERSION','')
- debug('multiple sdk version declarations: MSVC_SDK_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
-
- err_msg = "multiple sdk version declarations: MSVC_SDK_VERSION={} and MSVC_SCRIPT_ARGS={}".format(
- repr(env_argstr), repr(user_argstr)
- )
-
- raise MSVCArgumentError(err_msg)
-
- @classmethod
- def _msvc_read_toolset_file(cls, msvc, filename):
- toolset_version = None
- try:
- with open(filename) as f:
- toolset_version = f.readlines()[0].strip()
- debug(
- 'msvc_version=%s, filename=%s, toolset_version=%s',
- repr(msvc.version), repr(filename), repr(toolset_version)
- )
- except OSError:
- debug('OSError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename))
- except IndexError:
- debug('IndexError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename))
- return toolset_version
-
- @classmethod
- def _msvc_read_toolset_folders(cls, msvc, vc_dir):
-
- toolsets_sxs = {}
- toolsets_full = []
-
- build_dir = os.path.join(vc_dir, "Auxiliary", "Build")
- sxs_toolsets = [f.name for f in os.scandir(build_dir) if f.is_dir()]
- for sxs_toolset in sxs_toolsets:
- filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset)
- filepath = os.path.join(build_dir, sxs_toolset, filename)
- debug('sxs toolset: check file=%s', repr(filepath))
- if os.path.exists(filepath):
- toolset_version = cls._msvc_read_toolset_file(msvc, filepath)
- if not toolset_version:
- continue
- toolsets_sxs[sxs_toolset] = toolset_version
- debug(
- 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s',
- repr(msvc.version), repr(sxs_toolset), toolset_version
- )
-
- toolset_dir = os.path.join(vc_dir, "Tools", "MSVC")
- toolsets = [f.name for f in os.scandir(toolset_dir) if f.is_dir()]
- for toolset_version in toolsets:
- binpath = os.path.join(toolset_dir, toolset_version, "bin")
- debug('toolset: check binpath=%s', repr(binpath))
- if os.path.exists(binpath):
- toolsets_full.append(toolset_version)
- debug(
- 'toolset: msvc_version=%s, toolset_version=%s',
- repr(msvc.version), repr(toolset_version)
- )
-
- toolsets_full.sort(reverse=True)
- debug('msvc_version=%s, toolsets=%s', repr(msvc.version), repr(toolsets_full))
-
- return toolsets_sxs, toolsets_full
-
- @classmethod
- def _msvc_read_toolset_default(cls, msvc, vc_dir):
-
- build_dir = os.path.join(vc_dir, "Auxiliary", "Build")
-
- # VS2019+
- filename = "Microsoft.VCToolsVersion.{}.default.txt".format(msvc.vs_def.vc_buildtools_def.vc_buildtools)
- filepath = os.path.join(build_dir, filename)
-
- debug('default toolset: check file=%s', repr(filepath))
- if os.path.exists(filepath):
- toolset_buildtools = cls._msvc_read_toolset_file(msvc, filepath)
- if toolset_buildtools:
- return toolset_buildtools
-
- # VS2017+
- filename = "Microsoft.VCToolsVersion.default.txt"
- filepath = os.path.join(build_dir, filename)
-
- debug('default toolset: check file=%s', repr(filepath))
- if os.path.exists(filepath):
- toolset_default = cls._msvc_read_toolset_file(msvc, filepath)
- if toolset_default:
- return toolset_default
-
- return None
-
- _toolset_version_cache = {}
- _toolset_default_cache = {}
-
- @classmethod
- def _reset_toolset_cache(cls):
- debug('reset %s: toolset cache', cls.__name__)
- cls._toolset_version_cache = {}
- cls._toolset_default_cache = {}
-
- @classmethod
- def _msvc_version_toolsets(cls, msvc, vc_dir):
-
- if msvc.version in cls._toolset_version_cache:
- toolsets_sxs, toolsets_full = cls._toolset_version_cache[msvc.version]
- else:
- toolsets_sxs, toolsets_full = cls._msvc_read_toolset_folders(msvc, vc_dir)
- cls._toolset_version_cache[msvc.version] = toolsets_sxs, toolsets_full
-
- return toolsets_sxs, toolsets_full
-
- @classmethod
- def _msvc_default_toolset(cls, msvc, vc_dir):
-
- if msvc.version in cls._toolset_default_cache:
- toolset_default = cls._toolset_default_cache[msvc.version]
- else:
- toolset_default = cls._msvc_read_toolset_default(msvc, vc_dir)
- cls._toolset_default_cache[msvc.version] = toolset_default
-
- return toolset_default
-
- @classmethod
- def _msvc_version_toolset_vcvars(cls, msvc, vc_dir, toolset_version):
-
- if toolset_version == '14.0':
- return toolset_version
-
- toolsets_sxs, toolsets_full = cls._msvc_version_toolsets(msvc, vc_dir)
-
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric == cls.VS2019.vc_buildtools_def.vc_version_numeric:
- # necessary to detect toolset not found
- if toolset_version == '14.28.16.8':
- new_toolset_version = '14.28'
- # VS2019\Common7\Tools\vsdevcmd\ext\vcvars.bat AzDO Bug#1293526
- # special handling of the 16.8 SxS toolset, use VC\Auxiliary\Build\14.28 directory and SxS files
- # if SxS version 14.28 not present/installed, fallback selection of toolset VC\Tools\MSVC\14.28.nnnnn.
- debug(
- 'rewrite toolset_version=%s => toolset_version=%s',
- repr(toolset_version), repr(new_toolset_version)
- )
- toolset_version = new_toolset_version
-
- if toolset_version in toolsets_sxs:
- toolset_vcvars = toolsets_sxs[toolset_version]
- return toolset_vcvars
-
- for toolset_full in toolsets_full:
- if toolset_full.startswith(toolset_version):
- toolset_vcvars = toolset_full
- return toolset_vcvars
-
- return None
-
- @classmethod
- def _msvc_script_argument_toolset_constraints(cls, msvc, toolset_version):
-
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric:
- debug(
- 'invalid: msvc version constraint: %s < %s VS2017',
- repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
- repr(cls.VS2017.vc_buildtools_def.vc_version_numeric)
- )
- err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
- repr(toolset_version), repr(msvc.version), repr(cls.VS2017.vc_buildtools_def.vc_version)
- )
- return err_msg
-
- m = cls.re_toolset_version.match(toolset_version)
- if not m:
- debug('invalid: re_toolset_version: toolset_version=%s', repr(toolset_version))
- err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format(
- repr(toolset_version)
- )
- return err_msg
-
- toolset_ver = m.group('version')
- toolset_vernum = float(toolset_ver)
-
- if toolset_vernum < cls.VS2015.vc_buildtools_def.vc_version_numeric:
- debug(
- 'invalid: toolset version constraint: %s < %s VS2015',
- repr(toolset_vernum), repr(cls.VS2015.vc_buildtools_def.vc_version_numeric)
- )
- err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < {} VS2015".format(
- repr(toolset_version), repr(toolset_ver), repr(cls.VS2015.vc_buildtools_def.vc_version)
- )
- return err_msg
-
- if toolset_vernum > msvc.vs_def.vc_buildtools_def.vc_version_numeric:
- debug(
- 'invalid: toolset version constraint: toolset %s > %s msvc',
- repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric)
- )
- err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format(
- repr(toolset_version), repr(toolset_ver), repr(msvc.version)
- )
- return err_msg
-
- if toolset_vernum == cls.VS2015.vc_buildtools_def.vc_version_numeric:
- # tooset = 14.0
- if cls.re_toolset_full.match(toolset_version):
- if not cls.re_toolset_140.match(toolset_version):
- debug(
- 'invalid: toolset version 14.0 constraint: %s != 14.0',
- repr(toolset_version)
- )
- err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0'".format(
- repr(toolset_version), repr(toolset_version)
- )
- return err_msg
- return None
-
- if cls.re_toolset_full.match(toolset_version):
- debug('valid: re_toolset_full: toolset_version=%s', repr(toolset_version))
- return None
-
- if cls.re_toolset_sxs.match(toolset_version):
- debug('valid: re_toolset_sxs: toolset_version=%s', repr(toolset_version))
- return None
-
- debug('invalid: method exit: toolset_version=%s', repr(toolset_version))
- err_msg = "MSVC_TOOLSET_VERSION ({}) format is not supported".format(repr(toolset_version))
- return err_msg
-
- @classmethod
- def _msvc_script_argument_toolset(cls, env, msvc, vc_dir, arglist):
-
- toolset_version = env['MSVC_TOOLSET_VERSION']
- debug('MSVC_VERSION=%s, MSVC_TOOLSET_VERSION=%s', repr(msvc.version), repr(toolset_version))
-
- if not toolset_version:
- return None
-
- err_msg = cls._msvc_script_argument_toolset_constraints(msvc, toolset_version)
- if err_msg:
- raise MSVCArgumentError(err_msg)
-
- if toolset_version.startswith('14.0') and len(toolset_version) > len('14.0'):
- new_toolset_version = '14.0'
- debug(
- 'rewrite toolset_version=%s => toolset_version=%s',
- repr(toolset_version), repr(new_toolset_version)
- )
- toolset_version = new_toolset_version
-
- toolset_vcvars = cls._msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version)
- debug(
- 'toolset: toolset_version=%s, toolset_vcvars=%s',
- repr(toolset_version), repr(toolset_vcvars)
- )
-
- if not toolset_vcvars:
- err_msg = "MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}".format(
- repr(toolset_version), repr(msvc.version)
- )
- raise MSVCArgumentError(err_msg)
-
- argpair = (cls.SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_vcvars))
- arglist.append(argpair)
-
- return toolset_vcvars
-
- @classmethod
- def _msvc_script_default_toolset(cls, env, msvc, vc_dir, arglist):
-
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric:
- return None
-
- toolset_default = cls._msvc_default_toolset(msvc, vc_dir)
- if not toolset_default:
- return None
-
- debug('MSVC_VERSION=%s, toolset_default=%s', repr(msvc.version), repr(toolset_default))
-
- argpair = (cls.SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_default))
- arglist.append(argpair)
-
- return toolset_default
-
- @classmethod
- def _user_script_argument_toolset(cls, env, toolset_version, user_argstr):
-
- matches = [m for m in cls.re_vcvars_toolset.finditer(user_argstr)]
- if not matches:
- return None
-
- if len(matches) > 1:
- debug('multiple toolset version declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
- err_msg = "multiple toolset version declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
- raise MSVCArgumentError(err_msg)
-
- if not toolset_version:
- user_toolset = matches[0].group('toolset')
- return user_toolset
-
- env_argstr = env.get('MSVC_TOOLSET_VERSION','')
- debug('multiple toolset version declarations: MSVC_TOOLSET_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
-
- err_msg = "multiple toolset version declarations: MSVC_TOOLSET_VERSION={} and MSVC_SCRIPT_ARGS={}".format(
- repr(env_argstr), repr(user_argstr)
- )
-
- raise MSVCArgumentError(err_msg)
-
- @classmethod
- def _msvc_script_argument_spectre(cls, env, msvc, arglist):
-
- spectre_libs = env['MSVC_SPECTRE_LIBS']
- debug('MSVC_VERSION=%s, MSVC_SPECTRE_LIBS=%s', repr(msvc.version), repr(spectre_libs))
-
- if not spectre_libs:
- return None
-
- if spectre_libs not in (True, '1'):
- return None
-
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric:
- debug(
- 'invalid: msvc version constraint: %s < %s VS2017',
- repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
- repr(cls.VS2017.vc_buildtools_def.vc_version_numeric)
- )
- err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
- repr(spectre_libs), repr(msvc.version), repr(cls.VS2017.vc_buildtools_def.vc_version)
- )
- raise MSVCArgumentError(err_msg)
-
- spectre_arg = 'spectre'
-
- # spectre libs may not be installed
- argpair = (cls.SortOrder.SPECTRE, '-vcvars_spectre_libs={}'.format(spectre_arg))
- arglist.append(argpair)
-
- return spectre_arg
-
- @classmethod
- def _msvc_script_argument_user(cls, env, msvc, arglist):
-
- # subst None -> empty string
- script_args = env.subst('$MSVC_SCRIPT_ARGS')
- debug('MSVC_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(msvc.version), repr(script_args))
-
- if not script_args:
- return None
-
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric:
- debug(
- 'invalid: msvc version constraint: %s < %s VS2015',
- repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
- repr(cls.VS2015.vc_buildtools_def.vc_version_numeric)
- )
- err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
- repr(script_args), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version)
- )
- raise MSVCArgumentError(err_msg)
-
- # user arguments are not validated
- argpair = (cls.SortOrder.USER, script_args)
- arglist.append(argpair)
-
- return script_args
-
- @classmethod
- def _user_script_argument_spectre(cls, env, spectre, user_argstr):
-
- matches = [m for m in cls.re_vcvars_spectre.finditer(user_argstr)]
- if not matches:
- return None
-
- if len(matches) > 1:
- debug('multiple spectre declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
- err_msg = "multiple spectre declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
- raise MSVCArgumentError(err_msg)
-
- if not spectre:
- return None
-
- env_argstr = env.get('MSVC_SPECTRE_LIBS','')
- debug('multiple spectre declarations: MSVC_SPECTRE_LIBS=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
-
- err_msg = "multiple spectre declarations: MSVC_SPECTRE_LIBS={} and MSVC_SCRIPT_ARGS={}".format(
- repr(env_argstr), repr(user_argstr)
- )
-
- raise MSVCArgumentError(err_msg)
-
- @classmethod
- def msvc_script_arguments(cls, env, version, vc_dir, arg):
-
- arglist = []
-
- msvc = cls._msvc_version(version)
-
- if arg:
- argpair = (cls.SortOrder.ARCH, arg)
- arglist.append(argpair)
-
- if 'MSVC_SCRIPT_ARGS' in env:
- user_argstr = cls._msvc_script_argument_user(env, msvc, arglist)
- else:
- user_argstr = None
-
- if 'MSVC_UWP_APP' in env:
- uwp = cls._msvc_script_argument_uwp(env, msvc, arglist)
- else:
- uwp = None
-
- if user_argstr:
- cls._user_script_argument_uwp(env, uwp, user_argstr)
-
- platform_type = 'uwp' if uwp else 'desktop'
-
- if 'MSVC_SDK_VERSION' in env:
- sdk_version = cls._msvc_script_argument_sdk(env, msvc, platform_type, arglist)
- else:
- sdk_version = None
-
- if user_argstr:
- user_sdk = cls._user_script_argument_sdk(env, sdk_version, user_argstr)
- else:
- user_sdk = None
-
- if cls.MSVC_FORCE_DEFAULT_SDK:
- if not sdk_version and not user_sdk:
- sdk_version = cls._msvc_script_default_sdk(env, msvc, platform_type, arglist)
-
- if 'MSVC_TOOLSET_VERSION' in env:
- toolset_version = cls._msvc_script_argument_toolset(env, msvc, vc_dir, arglist)
- else:
- toolset_version = None
-
- if user_argstr:
- user_toolset = cls._user_script_argument_toolset(env, toolset_version, user_argstr)
- else:
- user_toolset = None
-
- if cls.MSVC_FORCE_DEFAULT_TOOLSET:
- if not toolset_version and not user_toolset:
- toolset_version = cls._msvc_script_default_toolset(env, msvc, vc_dir, arglist)
-
- if 'MSVC_SPECTRE_LIBS' in env:
- spectre = cls._msvc_script_argument_spectre(env, msvc, arglist)
- else:
- spectre = None
-
- if user_argstr:
- cls._user_script_argument_spectre(env, spectre, user_argstr)
-
- if arglist:
- arglist.sort()
- argstr = ' '.join([argpair[-1] for argpair in arglist]).strip()
- else:
- argstr = ''
-
- debug('arguments: %s', repr(argstr))
- return argstr
-
- @classmethod
- def reset(cls):
- debug('reset %s', cls.__name__)
- cls._reset_toolset_cache()
-
- @classmethod
- def verify(cls):
- debug('verify %s', cls.__name__)
- cls._verify_re_sdk_dispatch_map()
-
-_Dispatcher.register(_ScriptArguments)
-
-# internal consistency check
-_Dispatcher.verify()
--
cgit v0.12
From 14ee60c050d577704004ba8ade7e5347e356f9e9 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Mon, 20 Jun 2022 17:08:34 -0400
Subject: Fix typo in module name
---
SCons/Tool/MSCommon/vc.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index 5a27f44..2e5d542 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -1107,7 +1107,7 @@ def msvc_setup_env_once(env, tool=None):
msg = "No versions of the MSVC compiler were found.\n" \
" Visual Studio C/C++ compilers may not be set correctly.\n" \
" Requested tool(s) are: {}".format(req_tools)
- MSVC.Notfound.policy_handler(env, msg)
+ MSVC.NotFound.policy_handler(env, msg)
def msvc_find_valid_batch_script(env, version):
"""Find and execute appropriate batch script to set up build env.
--
cgit v0.12
From 9619adbcf75cf9f6c851e577f49d92b021012de3 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Mon, 20 Jun 2022 17:50:03 -0400
Subject: Cleanup MSCommon/vc imports and move Dispatcher imports and
registration
---
SCons/Tool/MSCommon/MSVC/Config.py | 2 +-
SCons/Tool/MSCommon/MSVC/Dispatcher.py | 1 +
SCons/Tool/MSCommon/MSVC/NotFound.py | 3 ++-
SCons/Tool/MSCommon/MSVC/Registry.py | 3 ++-
SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 3 ++-
SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py | 2 +-
SCons/Tool/MSCommon/MSVC/WinSDK.py | 3 ++-
SCons/Tool/MSCommon/MSVC/__init__.py | 7 +------
SCons/Tool/MSCommon/__init__.py | 6 ------
SCons/Tool/MSCommon/vc.py | 6 ++++++
10 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py
index 476dcb3..8f3a2cc 100644
--- a/SCons/Tool/MSCommon/MSVC/Config.py
+++ b/SCons/Tool/MSCommon/MSVC/Config.py
@@ -30,9 +30,9 @@ from collections import (
)
from . import Dispatcher
-
Dispatcher.register_modulename(__name__)
+
UNDEFINED = object()
BOOLEAN_SYMBOLS = {}
diff --git a/SCons/Tool/MSCommon/MSVC/Dispatcher.py b/SCons/Tool/MSCommon/MSVC/Dispatcher.py
index ebcd704..0b216ca 100644
--- a/SCons/Tool/MSCommon/MSVC/Dispatcher.py
+++ b/SCons/Tool/MSCommon/MSVC/Dispatcher.py
@@ -31,6 +31,7 @@ from ..common import (
debug,
)
+
_refs = []
def register_class(ref):
diff --git a/SCons/Tool/MSCommon/MSVC/NotFound.py b/SCons/Tool/MSCommon/MSVC/NotFound.py
index 7abe5ad..6ade285 100644
--- a/SCons/Tool/MSCommon/MSVC/NotFound.py
+++ b/SCons/Tool/MSCommon/MSVC/NotFound.py
@@ -36,15 +36,16 @@ from ..common import (
debug,
)
-from . import Dispatcher
from . import Config
from .Exceptions import (
MSVCVersionNotFound,
)
+from . import Dispatcher
Dispatcher.register_modulename(__name__)
+
_MSVC_NOTFOUND_POLICY_DEF = Config.MSVC_NOTFOUND_POLICY_INTERNAL['warning']
def _msvc_notfound_policy_lookup(symbol):
diff --git a/SCons/Tool/MSCommon/MSVC/Registry.py b/SCons/Tool/MSCommon/MSVC/Registry.py
index 848b125..f9a5eb2 100644
--- a/SCons/Tool/MSCommon/MSVC/Registry.py
+++ b/SCons/Tool/MSCommon/MSVC/Registry.py
@@ -39,11 +39,12 @@ from .. common import (
read_reg,
)
-from . import Dispatcher
from . import Util
+from . import Dispatcher
Dispatcher.register_modulename(__name__)
+
def read_value(hkey, subkey_valname):
try:
rval = read_reg(subkey_valname, hkroot=hkey)
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
index 324f8be..1b02396 100644
--- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -37,7 +37,6 @@ from ..common import (
debug,
)
-from . import Dispatcher
from . import Util
from . import Config
from . import WinSDK
@@ -47,8 +46,10 @@ from .Exceptions import (
MSVCArgumentError,
)
+from . import Dispatcher
Dispatcher.register_modulename(__name__)
+
# TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last?
re_sdk_version_100 = re.compile(r'^10[.][0-9][.][0-9]{5}[.][0-9]{1}$')
re_sdk_version_81 = re.compile(r'^8[.]1$')
diff --git a/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py b/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py
index 8a79007..8b9faa9 100644
--- a/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py
+++ b/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py
@@ -44,9 +44,9 @@ from .. common import (
)
from . import Dispatcher
-
Dispatcher.register_modulename(__name__)
+
class _Data:
separator = r';'
diff --git a/SCons/Tool/MSCommon/MSVC/WinSDK.py b/SCons/Tool/MSCommon/MSVC/WinSDK.py
index e95c72e..bd8d9b0 100644
--- a/SCons/Tool/MSCommon/MSVC/WinSDK.py
+++ b/SCons/Tool/MSCommon/MSVC/WinSDK.py
@@ -31,7 +31,6 @@ from ..common import (
debug,
)
-from . import Dispatcher
from . import Util
from . import Config
from . import Registry
@@ -40,8 +39,10 @@ from .Exceptions import (
MSVCInternalError,
)
+from . import Dispatcher
Dispatcher.register_modulename(__name__)
+
def _new_sdk_map():
sdk_map = {
'desktop': [],
diff --git a/SCons/Tool/MSCommon/MSVC/__init__.py b/SCons/Tool/MSCommon/MSVC/__init__.py
index afd993f..1341415 100644
--- a/SCons/Tool/MSCommon/MSVC/__init__.py
+++ b/SCons/Tool/MSCommon/MSVC/__init__.py
@@ -28,8 +28,6 @@ Functions for Microsoft Visual C/C++.
from . import Exceptions
from . import Util
-from . import Dispatcher as _Dispatcher
-
from . import Config
from . import Registry
from . import SetupEnvDefault
@@ -37,10 +35,7 @@ from . import NotFound
from . import WinSDK
from . import ScriptArguments
-from .NotFound import (
- set_msvc_notfound_policy,
- get_msvc_notfound_policy,
-)
+from . import Dispatcher as _Dispatcher
def reset():
_Dispatcher.reset()
diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py
index 9f35e94..7900a87 100644
--- a/SCons/Tool/MSCommon/__init__.py
+++ b/SCons/Tool/MSCommon/__init__.py
@@ -32,18 +32,12 @@ import SCons.Util
from SCons.Tool.MSCommon.sdk import mssdk_exists, mssdk_setup_env
-from SCons.Tool.MSCommon.MSVC import (
- set_msvc_notfound_policy,
- get_msvc_notfound_policy,
-)
-
from SCons.Tool.MSCommon.vc import (
msvc_exists,
msvc_setup_env_tool,
msvc_setup_env_once,
msvc_version_to_maj_min,
msvc_find_vswhere,
- get_msvc_sdk_versions,
)
from SCons.Tool.MSCommon.vs import (
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index 2e5d542..a2e8e42 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -61,10 +61,16 @@ from .common import CONFIG_CACHE, debug
from .sdk import get_installed_sdks
from . import MSVC
+
from .MSVC.Exceptions import (
VisualCException
)
+from .MSVC.NotFound import (
+ set_msvc_notfound_policy,
+ get_msvc_notfound_policy,
+)
+
class UnsupportedVersion(VisualCException):
pass
--
cgit v0.12
From a7c56cebc24fd5aaa2dd05a1751412120c946835 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Mon, 20 Jun 2022 18:36:54 -0400
Subject: Fix msvc notfound policy module path for test
---
test/fixture/no_msvc/no_msvcs_sconstruct_version.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_version.py b/test/fixture/no_msvc/no_msvcs_sconstruct_version.py
index f5cabf7..4c76d44 100644
--- a/test/fixture/no_msvc/no_msvcs_sconstruct_version.py
+++ b/test/fixture/no_msvc/no_msvcs_sconstruct_version.py
@@ -10,7 +10,7 @@ for key in SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR:
SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere
-SCons.Tool.MSCommon.set_msvc_notfound_policy('error')
+SCons.Tool.MSCommon.vc.set_msvc_notfound_policy('error')
env = SCons.Environment.Environment(MSVC_VERSION='14.3')
--
cgit v0.12
From f6a7b846bba8477ca8221edd2b76dc3d1d842439 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Mon, 20 Jun 2022 21:48:57 -0400
Subject: Add global for cache reset (classmethod to module omission) and
remove duplicate import
---
SCons/Tool/MSCommon/MSVC/Registry.py | 2 --
SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 2 ++
SCons/Tool/MSCommon/MSVC/WinSDK.py | 2 ++
3 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/SCons/Tool/MSCommon/MSVC/Registry.py b/SCons/Tool/MSCommon/MSVC/Registry.py
index f9a5eb2..492f3d0 100644
--- a/SCons/Tool/MSCommon/MSVC/Registry.py
+++ b/SCons/Tool/MSCommon/MSVC/Registry.py
@@ -30,8 +30,6 @@ import os
from SCons.Util import (
HKEY_LOCAL_MACHINE,
HKEY_CURRENT_USER,
- HKEY_LOCAL_MACHINE,
- HKEY_CURRENT_USER,
)
from .. common import (
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
index 1b02396..d0fc6ef 100644
--- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -373,6 +373,8 @@ _toolset_version_cache = {}
_toolset_default_cache = {}
def _reset_toolset_cache():
+ global _toolset_version_cache
+ global _toolset_default_cache
debug('reset: toolset cache')
_toolset_version_cache = {}
_toolset_default_cache = {}
diff --git a/SCons/Tool/MSCommon/MSVC/WinSDK.py b/SCons/Tool/MSCommon/MSVC/WinSDK.py
index bd8d9b0..8338c27 100644
--- a/SCons/Tool/MSCommon/MSVC/WinSDK.py
+++ b/SCons/Tool/MSCommon/MSVC/WinSDK.py
@@ -152,6 +152,8 @@ _sdk_map_cache = {}
_sdk_cache = {}
def _reset_sdk_cache():
+ global _sdk_map_cache
+ global _sdk_cache
debug('')
_sdk_map_cache = {}
_sdk_cache = {}
--
cgit v0.12
From dd328cff200935a7f570396f06b93a3da82278d7 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Tue, 21 Jun 2022 08:06:01 -0400
Subject: Suppress sider imported but unused for namespace. Restrict
MSVC_UWP_APP boolean symbols accepted.
---
SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 13 ++++++++-----
SCons/Tool/MSCommon/MSVC/__init__.py | 16 ++++++++--------
SCons/Tool/MSCommon/vc.py | 4 ++--
3 files changed, 18 insertions(+), 15 deletions(-)
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
index d0fc6ef..a64d9f4 100644
--- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -50,6 +50,9 @@ from . import Dispatcher
Dispatcher.register_modulename(__name__)
+# MSVC_UWP_APP argument: boolean True
+_UWP_ARGUMENT_BOOLEAN_TRUE = (True, '1')
+
# TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last?
re_sdk_version_100 = re.compile(r'^10[.][0-9][.][0-9]{5}[.][0-9]{1}$')
re_sdk_version_81 = re.compile(r'^8[.]1$')
@@ -91,10 +94,10 @@ re_vcvars_toolset = re.compile(r'(?:(?(?:[-]{1,2}|[/])vc
re_vcvars_spectre = re.compile(r'(?:(?(?:[-]{1,2}|[/])vcvars_spectre_libs[=](?P\S*))(?:(?!\S)|$)',re.IGNORECASE)
# Force default sdk argument
-MSVC_FORCE_DEFAULT_SDK = False
+_MSVC_FORCE_DEFAULT_SDK = False
# Force default toolset argument
-MSVC_FORCE_DEFAULT_TOOLSET = False
+_MSVC_FORCE_DEFAULT_TOOLSET = False
# MSVC batch file arguments:
#
@@ -148,7 +151,7 @@ def _msvc_script_argument_uwp(env, msvc, arglist):
if not uwp_app:
return None
- if uwp_app not in Config.BOOLEAN_SYMBOLS[True]:
+ if uwp_app not in _UWP_ARGUMENT_BOOLEAN_TRUE:
return None
if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric:
@@ -691,7 +694,7 @@ def msvc_script_arguments(env, version, vc_dir, arg):
else:
user_sdk = None
- if MSVC_FORCE_DEFAULT_SDK:
+ if _MSVC_FORCE_DEFAULT_SDK:
if not sdk_version and not user_sdk:
sdk_version = _msvc_script_default_sdk(env, msvc, platform_type, arglist)
@@ -705,7 +708,7 @@ def msvc_script_arguments(env, version, vc_dir, arg):
else:
user_toolset = None
- if MSVC_FORCE_DEFAULT_TOOLSET:
+ if _MSVC_FORCE_DEFAULT_TOOLSET:
if not toolset_version and not user_toolset:
toolset_version = _msvc_script_default_toolset(env, msvc, vc_dir, arglist)
diff --git a/SCons/Tool/MSCommon/MSVC/__init__.py b/SCons/Tool/MSCommon/MSVC/__init__.py
index 1341415..c07e849 100644
--- a/SCons/Tool/MSCommon/MSVC/__init__.py
+++ b/SCons/Tool/MSCommon/MSVC/__init__.py
@@ -25,15 +25,15 @@
Functions for Microsoft Visual C/C++.
"""
-from . import Exceptions
-from . import Util
+from . import Exceptions # noqa: F401
+from . import Util # noqa: F401
-from . import Config
-from . import Registry
-from . import SetupEnvDefault
-from . import NotFound
-from . import WinSDK
-from . import ScriptArguments
+from . import Config # noqa: F401
+from . import Registry # noqa: F401
+from . import SetupEnvDefault # noqa: F401
+from . import NotFound # noqa: F401
+from . import WinSDK # noqa: F401
+from . import ScriptArguments # noqa: F401
from . import Dispatcher as _Dispatcher
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index a2e8e42..2f1ec11 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -67,8 +67,8 @@ from .MSVC.Exceptions import (
)
from .MSVC.NotFound import (
- set_msvc_notfound_policy,
- get_msvc_notfound_policy,
+ set_msvc_notfound_policy, # noqa: F401
+ get_msvc_notfound_policy, # noqa: F401
)
class UnsupportedVersion(VisualCException):
--
cgit v0.12
From 5dd220bf7d625771acc7f7ca476275795029b560 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Tue, 21 Jun 2022 08:59:21 -0400
Subject: Add internal, undocumented SCONS_CACHE_MSVC_FORCE_DEFAULTS
environment variable to force default SDK and toolset arguments.
---
SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
index a64d9f4..8bef3f5 100644
--- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -34,6 +34,7 @@ from collections import (
)
from ..common import (
+ CONFIG_CACHE,
debug,
)
@@ -50,6 +51,13 @@ from . import Dispatcher
Dispatcher.register_modulename(__name__)
+# Force default SDK and toolset arguments in cache
+_SCONS_CACHE_MSVC_FORCE_DEFAULTS = False
+if CONFIG_CACHE:
+ # SCONS_CACHE_MSVC_FORCE_DEFAULTS is internal and not documented.
+ if os.environ.get('SCONS_CACHE_MSVC_FORCE_DEFAULTS') in Config.BOOLEAN_SYMBOLS[True]:
+ _SCONS_CACHE_MSVC_FORCE_DEFAULTS = True
+
# MSVC_UWP_APP argument: boolean True
_UWP_ARGUMENT_BOOLEAN_TRUE = (True, '1')
@@ -99,6 +107,20 @@ _MSVC_FORCE_DEFAULT_SDK = False
# Force default toolset argument
_MSVC_FORCE_DEFAULT_TOOLSET = False
+def _msvc_force_default_sdk(force=True):
+ global _MSVC_FORCE_DEFAULT_SDK
+ _MSVC_FORCE_DEFAULT_SDK = force
+ debug('_MSVC_FORCE_DEFAULT_SDK=%s', repr(force))
+
+def _msvc_force_default_toolset(force=True):
+ global _MSVC_FORCE_DEFAULT_TOOLSET
+ _MSVC_FORCE_DEFAULT_TOOLSET = force
+ debug('_MSVC_FORCE_DEFAULT_TOOLSET=%s', repr(force))
+
+if _SCONS_CACHE_MSVC_FORCE_DEFAULTS:
+ _msvc_force_default_sdk(True)
+ _msvc_force_default_toolset(True)
+
# MSVC batch file arguments:
#
# VS2022: UWP, SDK, TOOLSET, SPECTRE
--
cgit v0.12
From ac9b54756cedd01c36bfbf1bca7d59f92fd08f15 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Tue, 21 Jun 2022 10:08:53 -0400
Subject: Consider MSVC_TOOLSET_VERSION specification intent to use msvc tools.
Update boolean symbols accepted for uwp and spectre.
---
SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 9 +++++----
SCons/Tool/MSCommon/vc.py | 16 ++++++++++++----
2 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
index 8bef3f5..2a94650 100644
--- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -58,8 +58,9 @@ if CONFIG_CACHE:
if os.environ.get('SCONS_CACHE_MSVC_FORCE_DEFAULTS') in Config.BOOLEAN_SYMBOLS[True]:
_SCONS_CACHE_MSVC_FORCE_DEFAULTS = True
-# MSVC_UWP_APP argument: boolean True
-_UWP_ARGUMENT_BOOLEAN_TRUE = (True, '1')
+# Script argument: boolean True
+_ARGUMENT_BOOLEAN_TRUE_LEGACY = (True, '1') # MSVC_UWP_APP
+_ARGUMENT_BOOLEAN_TRUE = (True,)
# TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last?
re_sdk_version_100 = re.compile(r'^10[.][0-9][.][0-9]{5}[.][0-9]{1}$')
@@ -173,7 +174,7 @@ def _msvc_script_argument_uwp(env, msvc, arglist):
if not uwp_app:
return None
- if uwp_app not in _UWP_ARGUMENT_BOOLEAN_TRUE:
+ if uwp_app not in _ARGUMENT_BOOLEAN_TRUE_LEGACY:
return None
if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric:
@@ -610,7 +611,7 @@ def _msvc_script_argument_spectre(env, msvc, arglist):
if not spectre_libs:
return None
- if spectre_libs not in (True, '1'):
+ if spectre_libs not in _ARGUMENT_BOOLEAN_TRUE:
return None
if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric:
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index 2f1ec11..dc97e2f 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -1300,12 +1300,20 @@ def msvc_setup_env_user(env=None):
if env:
# Intent is to use msvc tools:
- # MSVC_VERSION or MSVS_VERSION: defined and is True
- # MSVC_USE_SCRIPT: defined and (is string or is False)
- # MSVC_USE_SETTINGS: defined and is not None
+ # MSVC_VERSION: defined and evaluates True
+ # MSVS_VERSION: defined and evaluates True
+ # MSVC_TOOLSET_VERSION: defined and evaluates True
+ # MSVC_USE_SCRIPT: defined and (is string or evaluates False)
+ # MSVC_USE_SETTINGS: defined and is not None
+
+ # Arguments possibly present but not considered:
+ # MSVC_SDK_VERSION
+ # MSVC_UWP_APP
+ # MSVC_SPECTRE_LIBS
+ # MSVC_SCRIPT_ARGS
# defined and is True
- for key in ['MSVC_VERSION', 'MSVS_VERSION']:
+ for key in ['MSVC_VERSION', 'MSVS_VERSION', 'MSVC_TOOLSET_VERSION']:
if key in env and env[key]:
rval = True
debug('key=%s, return=%s', repr(key), rval)
--
cgit v0.12
From 377e8152bbd9cc4556318283c845dd66defe2d8c Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Tue, 21 Jun 2022 10:11:29 -0400
Subject: Comment out BatchFileExecutionWarning definition and invocation.
---
SCons/Tool/MSCommon/vc.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index dc97e2f..7b6034e 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -95,8 +95,8 @@ class MSVCScriptNotFound(VisualCException):
class MSVCUseSettingsError(VisualCException):
pass
-class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault):
- pass
+#class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault):
+# pass
# Dict to 'canonalize' the arch
@@ -1055,7 +1055,7 @@ def script_env(script, args=None):
# detected errors, cl.exe on path
debug('script=%s args=%s errors=%s', repr(script), repr(args), script_errmsg)
# This may be a bad idea (scons environment != vs cmdline environment)
- SCons.Warnings.warn(BatchFileExecutionWarning, script_errmsg)
+ #SCons.Warnings.warn(BatchFileExecutionWarning, script_errmsg)
# TODO: errlog/errstr should be added to cache and warning moved to call site
# once we updated cache, give a chance to write out if user wanted
--
cgit v0.12
From 016e7f7bdc6b901916da08a361b5cca8c24ee600 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Tue, 21 Jun 2022 10:26:36 -0400
Subject: Update flake8 F401 placement
---
SCons/Tool/MSCommon/vc.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index 7b6034e..f34e9e0 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -67,9 +67,9 @@ from .MSVC.Exceptions import (
)
from .MSVC.NotFound import (
- set_msvc_notfound_policy, # noqa: F401
- get_msvc_notfound_policy, # noqa: F401
-)
+ set_msvc_notfound_policy,
+ get_msvc_notfound_policy,
+) # noqa: F401
class UnsupportedVersion(VisualCException):
pass
--
cgit v0.12
From 56bd50d3a15cbe3c82e84bf703a3f4bf1e5615d5 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Tue, 21 Jun 2022 10:32:31 -0400
Subject: Add comment and import one-by-one for msvc notfound policy and flake8
F401
---
SCons/Tool/MSCommon/vc.py | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index f34e9e0..56f21e8 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -66,10 +66,9 @@ from .MSVC.Exceptions import (
VisualCException
)
-from .MSVC.NotFound import (
- set_msvc_notfound_policy,
- get_msvc_notfound_policy,
-) # noqa: F401
+# msvc test(s) expect avaiable via vc
+from .MSVC.NotFound import set_msvc_notfound_policy # noqa: F401
+from .MSVC.NotFound import get_msvc_notfound_policy # noqa: F401
class UnsupportedVersion(VisualCException):
pass
--
cgit v0.12
From ad7a59d1621d4154aeb51ba402f77aa18d65e21d Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Tue, 21 Jun 2022 10:38:05 -0400
Subject: Fix sider issue
---
SCons/Tool/MSCommon/vc.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index 56f21e8..5130727 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -66,7 +66,7 @@ from .MSVC.Exceptions import (
VisualCException
)
-# msvc test(s) expect avaiable via vc
+# msvc test(s) expect notfound policy available via vc
from .MSVC.NotFound import set_msvc_notfound_policy # noqa: F401
from .MSVC.NotFound import get_msvc_notfound_policy # noqa: F401
--
cgit v0.12
From a5938cce33f77237e45ad86a3f43e58f65ba74d3 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Tue, 21 Jun 2022 11:22:29 -0400
Subject: Move SCONS_CACHE_MSVC_FORCE_DEFAULTS environment variable query to
MSCommon and set boolean if active.
---
SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 18 +++++++-----------
SCons/Tool/MSCommon/common.py | 5 +++++
2 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
index 2a94650..aa96946 100644
--- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -34,7 +34,7 @@ from collections import (
)
from ..common import (
- CONFIG_CACHE,
+ CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS,
debug,
)
@@ -51,13 +51,6 @@ from . import Dispatcher
Dispatcher.register_modulename(__name__)
-# Force default SDK and toolset arguments in cache
-_SCONS_CACHE_MSVC_FORCE_DEFAULTS = False
-if CONFIG_CACHE:
- # SCONS_CACHE_MSVC_FORCE_DEFAULTS is internal and not documented.
- if os.environ.get('SCONS_CACHE_MSVC_FORCE_DEFAULTS') in Config.BOOLEAN_SYMBOLS[True]:
- _SCONS_CACHE_MSVC_FORCE_DEFAULTS = True
-
# Script argument: boolean True
_ARGUMENT_BOOLEAN_TRUE_LEGACY = (True, '1') # MSVC_UWP_APP
_ARGUMENT_BOOLEAN_TRUE = (True,)
@@ -118,9 +111,12 @@ def _msvc_force_default_toolset(force=True):
_MSVC_FORCE_DEFAULT_TOOLSET = force
debug('_MSVC_FORCE_DEFAULT_TOOLSET=%s', repr(force))
-if _SCONS_CACHE_MSVC_FORCE_DEFAULTS:
- _msvc_force_default_sdk(True)
- _msvc_force_default_toolset(True)
+def msvc_force_default_arguments(force=True):
+ _msvc_force_default_sdk(force)
+ _msvc_force_default_toolset(force)
+
+if CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS:
+ msvc_force_default_arguments(force=True)
# MSVC batch file arguments:
#
diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py
index c9f07f5..da8fd55 100644
--- a/SCons/Tool/MSCommon/common.py
+++ b/SCons/Tool/MSCommon/common.py
@@ -102,6 +102,11 @@ CONFIG_CACHE = os.environ.get('SCONS_CACHE_MSVC_CONFIG')
if CONFIG_CACHE in ('1', 'true', 'True'):
CONFIG_CACHE = os.path.join(os.path.expanduser('~'), 'scons_msvc_cache.json')
+# SCONS_CACHE_MSVC_FORCE_DEFAULTS is internal-use so undocumented.
+CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS = False
+if CONFIG_CACHE:
+ if os.environ.get('SCONS_CACHE_MSVC_FORCE_DEFAULTS') in ('1', 'true', 'True'):
+ CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS = True
def read_script_env_cache():
""" fetch cached msvc env vars if requested, else return empty dict """
--
cgit v0.12
From 52e349c07058d6b7b2b5eb46ae6371427bee9849 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Tue, 21 Jun 2022 12:37:03 -0400
Subject: Remove debug messages that by default are noisy.
---
SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py | 2 --
SCons/Tool/MSCommon/vc.py | 7 ++-----
2 files changed, 2 insertions(+), 7 deletions(-)
diff --git a/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py b/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py
index 8b9faa9..e1c05bc 100644
--- a/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py
+++ b/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py
@@ -73,7 +73,6 @@ def _initialize(env, msvc_exists_func):
debug('msvc default:msvc_installed=%s', _Data.msvc_installed)
def register_tool(env, tool, msvc_exists_func):
- debug('msvc default:tool=%s', tool)
if _Data.need_init:
_initialize(env, msvc_exists_func)
if _Data.msvc_installed:
@@ -90,7 +89,6 @@ def register_tool(env, tool, msvc_exists_func):
debug('msvc default:tool=%s, msvc_tools=%s', tool, _Data.msvc_tools)
def register_setup(env, msvc_exists_func):
- debug('msvc default')
if _Data.need_init:
_initialize(env, msvc_exists_func)
_Data.n_setup += 1
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index 5130727..7ea3583 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -1102,7 +1102,6 @@ def msvc_setup_env_once(env, tool=None):
has_run = False
if not has_run:
- debug('tool=%s', repr(tool))
MSVC.SetupEnvDefault.register_setup(env, msvc_exists)
msvc_setup_env(env)
env["MSVC_SETUP_RUN"] = True
@@ -1285,13 +1284,13 @@ def msvc_setup_env(env):
SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
def msvc_exists(env=None, version=None):
- debug('version=%s', repr(version))
vcs = get_installed_vcs(env)
if version is None:
rval = len(vcs) > 0
else:
rval = version in vcs
- debug('version=%s, return=%s', repr(version), rval)
+ if not rval:
+ debug('version=%s, return=%s', repr(version), rval)
return rval
def msvc_setup_env_user(env=None):
@@ -1336,14 +1335,12 @@ def msvc_setup_env_user(env=None):
return rval
def msvc_setup_env_tool(env=None, version=None, tool=None):
- debug('tool=%s, version=%s', repr(tool), repr(version))
MSVC.SetupEnvDefault.register_tool(env, tool, msvc_exists)
rval = False
if not rval and msvc_exists(env, version):
rval = True
if not rval and msvc_setup_env_user(env):
rval = True
- debug('tool=%s, version=%s, return=%s', repr(tool), repr(version), rval)
return rval
def get_msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False):
--
cgit v0.12
From 7683a2f958287ceee257b0449eed767b0dd275e5 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Tue, 21 Jun 2022 12:46:14 -0400
Subject: Remove MSVC_TOOLSET_VERSION from intent to use msvc tools (attached
to default version or specific version).
---
SCons/Tool/MSCommon/vc.py | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index 7ea3583..675b8d0 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -1304,14 +1304,8 @@ def msvc_setup_env_user(env=None):
# MSVC_USE_SCRIPT: defined and (is string or evaluates False)
# MSVC_USE_SETTINGS: defined and is not None
- # Arguments possibly present but not considered:
- # MSVC_SDK_VERSION
- # MSVC_UWP_APP
- # MSVC_SPECTRE_LIBS
- # MSVC_SCRIPT_ARGS
-
# defined and is True
- for key in ['MSVC_VERSION', 'MSVS_VERSION', 'MSVC_TOOLSET_VERSION']:
+ for key in ['MSVC_VERSION', 'MSVS_VERSION']:
if key in env and env[key]:
rval = True
debug('key=%s, return=%s', repr(key), rval)
--
cgit v0.12
From 98cf01fd1434463a7af5b3fe5375a9772882dcd2 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Wed, 22 Jun 2022 16:53:53 -0400
Subject: Reorder function declarations
---
SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 46 ++++++++++++++---------------
1 file changed, 23 insertions(+), 23 deletions(-)
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
index aa96946..e56dd4a 100644
--- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -629,6 +629,29 @@ def _msvc_script_argument_spectre(env, msvc, arglist):
return spectre_arg
+def _user_script_argument_spectre(env, spectre, user_argstr):
+
+ matches = [m for m in re_vcvars_spectre.finditer(user_argstr)]
+ if not matches:
+ return None
+
+ if len(matches) > 1:
+ debug('multiple spectre declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
+ err_msg = "multiple spectre declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
+ raise MSVCArgumentError(err_msg)
+
+ if not spectre:
+ return None
+
+ env_argstr = env.get('MSVC_SPECTRE_LIBS','')
+ debug('multiple spectre declarations: MSVC_SPECTRE_LIBS=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
+
+ err_msg = "multiple spectre declarations: MSVC_SPECTRE_LIBS={} and MSVC_SCRIPT_ARGS={}".format(
+ repr(env_argstr), repr(user_argstr)
+ )
+
+ raise MSVCArgumentError(err_msg)
+
def _msvc_script_argument_user(env, msvc, arglist):
# subst None -> empty string
@@ -655,29 +678,6 @@ def _msvc_script_argument_user(env, msvc, arglist):
return script_args
-def _user_script_argument_spectre(env, spectre, user_argstr):
-
- matches = [m for m in re_vcvars_spectre.finditer(user_argstr)]
- if not matches:
- return None
-
- if len(matches) > 1:
- debug('multiple spectre declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
- err_msg = "multiple spectre declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
- raise MSVCArgumentError(err_msg)
-
- if not spectre:
- return None
-
- env_argstr = env.get('MSVC_SPECTRE_LIBS','')
- debug('multiple spectre declarations: MSVC_SPECTRE_LIBS=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
-
- err_msg = "multiple spectre declarations: MSVC_SPECTRE_LIBS={} and MSVC_SCRIPT_ARGS={}".format(
- repr(env_argstr), repr(user_argstr)
- )
-
- raise MSVCArgumentError(err_msg)
-
def msvc_script_arguments(env, version, vc_dir, arg):
arglist = []
--
cgit v0.12
From 1de714ae3085d9fd07cb8837f523037885271aa8 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Thu, 23 Jun 2022 18:58:38 -0400
Subject: Construction variable documentation additions and modifications.
---
SCons/Tool/msvc.xml | 553 ++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 542 insertions(+), 11 deletions(-)
diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml
index e8df128..6663aab 100644
--- a/SCons/Tool/msvc.xml
+++ b/SCons/Tool/msvc.xml
@@ -481,9 +481,24 @@ env = Environment(MSVC_VERSION='8.0', MSVC_USE_SETTINGS=msvc_use_settings)
-Note: the dictionary content requirements are based on the internal msvc implementation and
-therefore may change at any time. The burden is on the user to ensure the dictionary contents
-are minimally sufficient to ensure successful builds.
+Important usage details:
+
+
+
+MSVC_USE_SETTINGS must be passed as an argument to the Environment() constructor;
+setting it later has no effect.
+
+
+
+
+The dictionary content requirements are based on the internal msvc implementation and
+therefore may change at any time.
+
+The burden is on the user to ensure the dictionary contents are minimally sufficient to
+ensure successful builds.
+
+
+
@@ -510,21 +525,65 @@ are minimally sufficient to ensure successful builds.
-Build libraries for a Universal Windows Platform (UWP) Application.
+Build with the Universal Windows Platform (UWP) application Visual C++ libraries.
-If &cv-MSVC_UWP_APP; is set, the Visual C++ environment will be set up to point
+The valid values for MSVC_UWP_APP are True,
+'1', False, '0',
+or None.
+
+
+
+When MSVC_UWP_APP is enabled (i.e., True or
+'1'), the Visual C++ environment will be set up to point
to the Windows Store compatible libraries and Visual C++ runtimes. In doing so,
any libraries that are built will be able to be used in a UWP App and published
to the Windows Store.
-This flag will only have an effect with Visual Studio 2015 or later.
-This variable must be passed as an argument to the Environment()
-constructor; setting it later has no effect.
+
+
+
+
+
+An exception is raised when any of the following conditions are satisfied:
+
+
+MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier.
+
+
+MSVC_UWP_APP is enabled and a UWP argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple UWP declarations via MSVC_UWP_APP
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+
+
-Valid values are '1' or '0'
+Example - A Visual Studio 2022 build for the Universal Windows Platform:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_UWP_APP=True)
+
+
+
+
+Important usage details:
+
+
+
+MSVC_UWP_APP must be passed as an argument to the Environment() constructor;
+setting it later has no effect.
+
+
+
+
+The existence of the UWP libraries is not verified when MSVC_UWP_APP is enabled
+which could result in build failures.
+
+The burden is on the user to ensure the requisite UWP libraries are installed.
+
+
+
@@ -582,7 +641,7 @@ and also before &f-link-env-Tool; is called to ininitialize any of those tools:
-Specify the scons behavior when the Microsoft Visual C/C++ compiler is not detected.
+Specify the &scons; behavior when the Microsoft Visual C/C++ compiler is not detected.
@@ -590,7 +649,7 @@ Specify the scons behavior when the Microsoft Visual C/C++ compiler is not detec
when the requested msvc version is not detected.
-
+
The valid values for MSVC_NOTFOUND_POLICY and the corresponding &scons; behavior are:
@@ -664,13 +723,485 @@ A non-default tool list is specified that does not contain any of the msvc tools
+Important usage details:
+
+
+
+MSVC_NOTFOUND_POLICY must be passed as an argument to the Environment() constructor;
+setting it later has no effect.
+
+
+
+
+
+
When MSVC_NOTFOUND_POLICY is not specified, the default &scons; behavior is to issue a warning and continue
subject to the conditions listed above. The default &scons; behavior may change in the future.
+
+
+
+
+
+
+Pass user-defined arguments to the Visual C++ batch file determined via autodetection.
+
+
+
+MSVC_SCRIPT_ARGS is available for msvc batch file arguments that do not have first-class support
+via construction variables or when there is an issue with the appropriate construction variable validation.
+When available, it is recommended to use the appropriate construction variables (e.g., &cv-link-MSVC_TOOLSET_VERSION;)
+rather than MSVC_SCRIPT_ARGS arguments.
+
+
+
+The valid values for MSVC_SCRIPT_ARGS are: None, a string,
+or a list of strings.
+
+
+
+The MSVC_SCRIPT_ARGS value is converted to a scalar string (i.e., "flattened").
+The resulting scalar string, if not empty, is passed as an argument to the msvc batch file determined
+via autodetection subject to the validation conditions listed below.
+
+
+
+MSVC_SCRIPT_ARGS is ignored when the value is None and when the
+result from argument conversion is an empty string. The validation conditions below do not apply.
+
+
+
+An exception is raised when any of the following conditions are satisfied:
+
+
+
+MSVC_SCRIPT_ARGS is specified for Visual Studio 2013 and earlier.
+
+
+
+Multiple SDK version arguments (e.g., '10.0.20348.0') are specified
+in MSVC_SCRIPT_ARGS.
+
+
+
+&cv-link-MSVC_SDK_VERSION; is specified and an SDK version argument
+(e.g., '10.0.20348.0') is specified in MSVC_SCRIPT_ARGS.
+Multiple SDK version declarations via &cv-link-MSVC_SDK_VERSION; and MSVC_SCRIPT_ARGS
+are not allowed.
+
+
+
+Multiple toolset version arguments (e.g., '-vcvars_ver=14.29')
+are specified in MSVC_SCRIPT_ARGS.
+
+
+
+&cv-link-MSVC_TOOLSET_VERSION; is specified and a toolset version argument
+(e.g., '-vcvars_ver=14.29') is specified in MSVC_SCRIPT_ARGS.
+Multiple toolset version declarations via &cv-link-MSVC_TOOLSET_VERSION; and
+MSVC_SCRIPT_ARGS are not allowed.
+
+
+
+Multiple spectre library arguments (e.g., '-vcvars_spectre_libs=spectre')
+are specified in MSVC_SCRIPT_ARGS.
+
+
+
+&cv-link-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument
+(e.g., '-vcvars_spectre_libs=spectre') is specified in
+MSVC_SCRIPT_ARGS. Multiple spectre library declarations via &cv-link-MSVC_SPECTRE_LIBS;
+and MSVC_SCRIPT_ARGS are not allowed.
+
+
+
+Multiple UWP arguments (e.g., uwp or store) are specified
+in MSVC_SCRIPT_ARGS.
+
+
+
+&cv-link-MSVC_UWP_APP; is enabled and a UWP argument (e.g., uwp or
+store) is specified in MSVC_SCRIPT_ARGS. Multiple UWP declarations
+via &cv-link-MSVC_UWP_APP; and MSVC_SCRIPT_ARGS are not allowed.
+
+
+
+
+
+
+Example 1 - A Visual Studio 2022 build with an SDK version and a toolset version
+specified with a string argument:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS='10.0.20348.0 -vcvars_ver=14.29.30133')
+
+
+
+
+Example 2 - A Visual Studio 2022 build with an SDK version and a toolset version
+specified with a list argument:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS=['10.0.20348.0', '-vcvars_ver=14.29.30133'])
+
+
+
+
+Important usage details:
+
+
+
+MSVC_SCRIPT_ARGS must be passed as an argument to the Environment() constructor;
+setting it later has no effect.
+
+
+
+Other than checking for multiple declarations as described above, MSVC_SCRIPT_ARGS arguments
+are not validated.
+
+
+
+
+Erroneous, inconsistent, and/or version incompatible MSVC_SCRIPT_ARGS arguments are likely
+to result in build failures for reasons that are not readily apparent and may be difficult to diagnose.
+
+The burden is on the user to ensure that the arguments provided to the msvc batch file are valid, consistent
+and compatible with the version of msvc selected.
+
+
+
+
+
+
+
+Build with a specific version of the Microsoft Software Development Kit (SDK).
+
+
+
+The valid values for MSVC_SDK_VERSION are: None
+or a string containing the requested SDK version (e.g., '10.0.20348.0').
+
+
+
+MSVC_SDK_VERSION is ignored when the value is None and when
+the value is an empty string. The validation conditions below do not apply.
+
+
+
+An exception is raised when any of the following conditions are satisfied:
+
+
+
+MSVC_SDK_VERSION is specified for Visual Studio 2013 and earlier.
+
+
+
+MSVC_SDK_VERSION is specified and an SDK version argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple SDK version declarations via MSVC_SDK_VERSION
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+
+
+
+The MSVC_SDK_VERSION specified does not match any of the supported formats:
+
+
+'10.0.XXXXX.Y' [SDK 10.0]
+
+
+'8.1' [SDK 8.1]
+
+
+
+
+
+The system folder for the corresponding MSVC_SDK_VERSION version is not found.
+The requested SDK version does not appear to be installed.
+
+
+
+The MSVC_SDK_VERSION version does not appear to support the requested platform
+type (i.e., UWP or Desktop). The requested SDK version
+platform type components do not appear to be installed.
+
+
+
+
+
+
+Example 1 - A Visual Studio 2022 build with a specific Windows SDK version:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_SDK_VERSION='10.0.20348.0')
+
+
+
+
+Example 2 - A Visual Studio 2022 build with a specific SDK version for the Universal Windows Platform:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_SDK_VERSION='10.0.20348.0', MSVC_UWP_APP=True)
+
+
+
+
+Important usage details:
+
+
+
+MSVC_SDK_VERSION must be passed as an argument to the Environment() constructor;
+setting it later has no effect.
+
+
+
+
+Should a SDK 10.0 version be installed that does not follow the naming scheme above, the
+SDK version will need to be specified via &cv-link-MSVC_SCRIPT_ARGS; until the version number
+validation format can be extended.
+
+
+
+
+Should an exception be raised indicating that the SDK version is not found, verify that
+the requested SDK version is installed with the necessary platform type components.
+
+
+
+There is a known issue with the Microsoft libraries for SDK version '10.0.22000.0'
+when using the v141 build tools and the target architecture is ARM64.
+Should build failures arise with this combination of settings, MSVC_SDK_VERSION may be
+employed to specify a different SDK version for the build.
+
+
+
+
+
+
+
+
+
+
+
+Build with a specific Visual C++ toolset version.
+
+
+
+Specifying MSVC_TOOLSET_VERSION does not affect the autodetection and selection
+of msvc instances. The MSVC_TOOLSET_VERSION is applied after
+an msvc instance is selected. This could be the default version of msvc if &cv-link-MSVC_VERSION;
+is not specified.
+
+
+
+The valid values for MSVC_TOOLSET_VERSION are: None
+or a string containing the requested toolset version (e.g., '14.29').
+
+
+
+MSVC_TOOLSET_VERSION is ignored when the value is None and when
+the value is an empty string. The validation conditions below do not apply.
+
+
+
+An exception is raised when any of the following conditions are satisfied:
+
+
+
+MSVC_TOOLSET_VERSION is specified for Visual Studio 2015 and earlier.
+
+
+
+MSVC_TOOLSET_VERSION is specified and a toolset version argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via MSVC_TOOLSET_VERSION
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+
+
+
+
+The MSVC_TOOLSET_VERSION specified does not match any of the supported formats:
+
+
+
+
+
+'XX.Y'
+
+
+
+'XX.YY'
+
+
+
+'XX.YY.ZZZZZ'
+
+
+
+'XX.YY.Z' to 'XX.YY.ZZZZ'
+
+[&scons; extension not directly supported by the msvc batch files and may be removed in the future]
+
+
+
+
+'XX.YY.ZZ.N' [SxS format]
+
+
+
+'XX.YY.ZZ.NN' [SxS format]
+
+
+
+
+
+
+
+The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified
+is for Visual Studio 2013 and earlier (e.g., '12.0').
+
+
+
+The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified
+is greater than the msvc version selected (e.g., '99.0').
+
+
+
+A system folder for the corresponding MSVC_TOOLSET_VERSION version is not found.
+The requested toolset version does not appear to be installed.
+
+
+
+
+
+
+Toolset selection details:
+
+
+
+SxS version numbers are not always in three dot format (e.g., 'XX.YY.ZZ.NN') as shown above.
+
+In Visual Studio 2022 for example, 14.16 is an SxS toolset version that is directly
+mapped to toolset version 14.16.27023.
+
+
+
+When MSVC_TOOLSET_VERSION is not an SxS version number or a full toolset version number:
+the first toolset version, ranked in descending order, that matches the MSVC_TOOLSET_VERSION
+prefix is selected.
+
+
+
+When MSVC_TOOLSET_VERSION is specified using the major msvc version prefix
+(i.e., 'XX.Y') and the major msvc version is that of the latest release of
+Visual Studio, the selected toolset version may not be the same as the default Visual C++ toolset version.
+
+In the latest release of Visual Studio, the default Visual C++ toolset version is not necessarily the
+toolset with the largest version number.
+
+
+
+
+
+
+Example environment constructor invocations with toolset version specifications:
+
+Environment(MSVC_TOOLSET_VERSION='14.2')
+Environment(MSVC_TOOLSET_VERSION='14.29')
+Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.30133')
+Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.16.11')
+
+
+
+
+Important usage details:
+
+
+
+MSVC_TOOLSET_VERSION must be passed as an argument to the Environment() constructor;
+setting it later has no effect.
+
+
+
+
+The existence of the toolset host architecture and target architecture folders are not verified
+when MSVC_TOOLSET_VERSION is specified which could result in build failures.
+
+The burden is on the user to ensure the requisite toolset target architecture build tools are installed.
+
+
+
+
+
+
+
+
+
+
+
+Build with the spectre-mitigated Visual C++ libraries.
+
+
+
+The valid values for MSVC_SPECTRE_LIBS are: True,
+False, or None.
+
+
+
+When MSVC_SPECTRE_LIBS is enabled (i.e., True),
+the Visual C++ environment will include the paths to the spectre-mitigated implementations
+of the Microsoft Visual C++ libraries.
+
+
+
+An exception is raised when any of the following conditions are satisfied:
+
+
+
+MSVC_SPECTRE_LIBS is enabled for Visual Studio 2015 and earlier.
+
+
+
+MSVC_SPECTRE_LIBS is enabled and a spectre library argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via MSVC_SPECTRE_LIBS
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+
+
+
+
+
+
+Example - A Visual Studio 2022 build with spectre mitigated Visual C++ libraries:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_SPECTRE_LIBS=True)
+
+
+
+
+Important usage details:
+
+
+
+MSVC_SPECTRE_LIBS must be passed as an argument to the Environment() constructor;
+setting it later has no effect.
+
+
+
+Additional compiler switches (e.g., /Qspectre) are necessary for including
+spectre mitigations when building user artifacts. Refer to the Visual Studio documentation for
+details.
+
+
+
+
+The existence of the spectre mitigations libraries is not verified when MSVC_SPECTRE_LIBS
+is enabled which could result in build failures.
+
+The burden is on the user to ensure the requisite libraries with spectre mitigations are installed.
+
+
+
+
+
+
+
--
cgit v0.12
From f99f7cab7bc3af5ef18e3189f531572df4558fe4 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Thu, 23 Jun 2022 18:59:01 -0400
Subject: Construction variable documentation additions and modifications.
---
doc/generated/variables.gen | 558 +++++++++++++++++++++++++++++++++++++++++++-
doc/generated/variables.mod | 8 +
2 files changed, 555 insertions(+), 11 deletions(-)
diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen
index 996e35a..f1b3fff 100644
--- a/doc/generated/variables.gen
+++ b/doc/generated/variables.gen
@@ -4686,7 +4686,7 @@ will be compiled separately.
MSVC_NOTFOUND_POLICY
-Specify the scons behavior when the Microsoft Visual C/C++ compiler is not detected.
+Specify the &scons; behavior when the Microsoft Visual C/C++ compiler is not detected.
@@ -4694,7 +4694,7 @@ Specify the scons behavior when the Microsoft Visual C/C++ compiler is not detec
when the requested msvc version is not detected.
-
+
The valid values for MSVC_NOTFOUND_POLICY and the corresponding &scons; behavior are:
@@ -4768,10 +4768,487 @@ A non-default tool list is specified that does not contain any of the msvc tools
+Important usage details:
+
+
+
+MSVC_NOTFOUND_POLICY must be passed as an argument to the Environment() constructor;
+setting it later has no effect.
+
+
+
+
+
+
When MSVC_NOTFOUND_POLICY is not specified, the default &scons; behavior is to issue a warning and continue
subject to the conditions listed above. The default &scons; behavior may change in the future.
+
+
+
+
+ MSVC_SCRIPT_ARGS
+
+
+Pass user-defined arguments to the Visual C++ batch file determined via autodetection.
+
+
+
+MSVC_SCRIPT_ARGS is available for msvc batch file arguments that do not have first-class support
+via construction variables or when there is an issue with the appropriate construction variable validation.
+When available, it is recommended to use the appropriate construction variables (e.g., &cv-link-MSVC_TOOLSET_VERSION;)
+rather than MSVC_SCRIPT_ARGS arguments.
+
+
+
+The valid values for MSVC_SCRIPT_ARGS are: None, a string,
+or a list of strings.
+
+
+
+The MSVC_SCRIPT_ARGS value is converted to a scalar string (i.e., "flattened").
+The resulting scalar string, if not empty, is passed as an argument to the msvc batch file determined
+via autodetection subject to the validation conditions listed below.
+
+
+
+MSVC_SCRIPT_ARGS is ignored when the value is None and when the
+result from argument conversion is an empty string. The validation conditions below do not apply.
+
+
+
+An exception is raised when any of the following conditions are satisfied:
+
+
+
+MSVC_SCRIPT_ARGS is specified for Visual Studio 2013 and earlier.
+
+
+
+Multiple SDK version arguments (e.g., '10.0.20348.0') are specified
+in MSVC_SCRIPT_ARGS.
+
+
+
+&cv-link-MSVC_SDK_VERSION; is specified and an SDK version argument
+(e.g., '10.0.20348.0') is specified in MSVC_SCRIPT_ARGS.
+Multiple SDK version declarations via &cv-link-MSVC_SDK_VERSION; and MSVC_SCRIPT_ARGS
+are not allowed.
+
+
+
+Multiple toolset version arguments (e.g., '-vcvars_ver=14.29')
+are specified in MSVC_SCRIPT_ARGS.
+
+
+
+&cv-link-MSVC_TOOLSET_VERSION; is specified and a toolset version argument
+(e.g., '-vcvars_ver=14.29') is specified in MSVC_SCRIPT_ARGS.
+Multiple toolset version declarations via &cv-link-MSVC_TOOLSET_VERSION; and
+MSVC_SCRIPT_ARGS are not allowed.
+
+
+
+Multiple spectre library arguments (e.g., '-vcvars_spectre_libs=spectre')
+are specified in MSVC_SCRIPT_ARGS.
+
+
+
+&cv-link-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument
+(e.g., '-vcvars_spectre_libs=spectre') is specified in
+MSVC_SCRIPT_ARGS. Multiple spectre library declarations via &cv-link-MSVC_SPECTRE_LIBS;
+and MSVC_SCRIPT_ARGS are not allowed.
+
+
+
+Multiple UWP arguments (e.g., uwp or store) are specified
+in MSVC_SCRIPT_ARGS.
+
+
+
+&cv-link-MSVC_UWP_APP; is enabled and a UWP argument (e.g., uwp or
+store) is specified in MSVC_SCRIPT_ARGS. Multiple UWP declarations
+via &cv-link-MSVC_UWP_APP; and MSVC_SCRIPT_ARGS are not allowed.
+
+
+
+
+
+
+Example 1 - A Visual Studio 2022 build with an SDK version and a toolset version
+specified with a string argument:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS='10.0.20348.0 -vcvars_ver=14.29.30133')
+
+
+
+
+Example 2 - A Visual Studio 2022 build with an SDK version and a toolset version
+specified with a list argument:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS=['10.0.20348.0', '-vcvars_ver=14.29.30133'])
+
+
+
+
+Important usage details:
+
+
+
+MSVC_SCRIPT_ARGS must be passed as an argument to the Environment() constructor;
+setting it later has no effect.
+
+
+
+Other than checking for multiple declarations as described above, MSVC_SCRIPT_ARGS arguments
+are not validated.
+
+
+
+
+Erroneous, inconsistent, and/or version incompatible MSVC_SCRIPT_ARGS arguments are likely
+to result in build failures for reasons that are not readily apparent and may be difficult to diagnose.
+
+The burden is on the user to ensure that the arguments provided to the msvc batch file are valid, consistent
+and compatible with the version of msvc selected.
+
+
+
+
+
+
+
+
+
+ MSVC_SDK_VERSION
+
+
+Build with a specific version of the Microsoft Software Development Kit (SDK).
+
+
+
+The valid values for MSVC_SDK_VERSION are: None
+or a string containing the requested SDK version (e.g., '10.0.20348.0').
+
+
+
+MSVC_SDK_VERSION is ignored when the value is None and when
+the value is an empty string. The validation conditions below do not apply.
+
+
+
+An exception is raised when any of the following conditions are satisfied:
+
+
+
+MSVC_SDK_VERSION is specified for Visual Studio 2013 and earlier.
+
+
+
+MSVC_SDK_VERSION is specified and an SDK version argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple SDK version declarations via MSVC_SDK_VERSION
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+
+
+
+The MSVC_SDK_VERSION specified does not match any of the supported formats:
+
+
+'10.0.XXXXX.Y' [SDK 10.0]
+
+
+'8.1' [SDK 8.1]
+
+
+
+
+
+The system folder for the corresponding MSVC_SDK_VERSION version is not found.
+The requested SDK version does not appear to be installed.
+
+
+
+The MSVC_SDK_VERSION version does not appear to support the requested platform
+type (i.e., UWP or Desktop). The requested SDK version
+platform type components do not appear to be installed.
+
+
+
+
+
+
+Example 1 - A Visual Studio 2022 build with a specific Windows SDK version:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_SDK_VERSION='10.0.20348.0')
+
+
+
+
+Example 2 - A Visual Studio 2022 build with a specific SDK version for the Universal Windows Platform:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_SDK_VERSION='10.0.20348.0', MSVC_UWP_APP=True)
+
+
+
+
+Important usage details:
+
+
+
+MSVC_SDK_VERSION must be passed as an argument to the Environment() constructor;
+setting it later has no effect.
+
+
+
+
+Should a SDK 10.0 version be installed that does not follow the naming scheme above, the
+SDK version will need to be specified via &cv-link-MSVC_SCRIPT_ARGS; until the version number
+validation format can be extended.
+
+
+
+
+Should an exception be raised indicating that the SDK version is not found, verify that
+the requested SDK version is installed with the necessary platform type components.
+
+
+
+There is a known issue with the Microsoft libraries for SDK version '10.0.22000.0'
+when using the v141 build tools and the target architecture is ARM64.
+Should build failures arise with this combination of settings, MSVC_SDK_VERSION may be
+employed to specify a different SDK version for the build.
+
+
+
+
+
+
+
+
+
+ MSVC_SPECTRE_LIBS
+
+
+Build with the spectre-mitigated Visual C++ libraries.
+
+
+
+The valid values for MSVC_SPECTRE_LIBS are: True,
+False, or None.
+
+
+
+When MSVC_SPECTRE_LIBS is enabled (i.e., True),
+the Visual C++ environment will include the paths to the spectre-mitigated implementations
+of the Microsoft Visual C++ libraries.
+
+
+
+An exception is raised when any of the following conditions are satisfied:
+
+
+
+MSVC_SPECTRE_LIBS is enabled for Visual Studio 2015 and earlier.
+
+
+
+MSVC_SPECTRE_LIBS is enabled and a spectre library argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via MSVC_SPECTRE_LIBS
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+
+
+
+
+
+
+Example - A Visual Studio 2022 build with spectre mitigated Visual C++ libraries:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_SPECTRE_LIBS=True)
+
+
+
+
+Important usage details:
+
+
+
+MSVC_SPECTRE_LIBS must be passed as an argument to the Environment() constructor;
+setting it later has no effect.
+
+
+
+Additional compiler switches (e.g., /Qspectre) are necessary for including
+spectre mitigations when building user artifacts. Refer to the Visual Studio documentation for
+details.
+
+
+
+
+The existence of the spectre mitigations libraries is not verified when MSVC_SPECTRE_LIBS
+is enabled which could result in build failures.
+
+The burden is on the user to ensure the requisite libraries with spectre mitigations are installed.
+
+
+
+
+
+
+
+
+
+ MSVC_TOOLSET_VERSION
+
+
+Build with a specific Visual C++ toolset version.
+
+
+
+Specifying MSVC_TOOLSET_VERSION does not affect the autodetection and selection
+of msvc instances. The MSVC_TOOLSET_VERSION is applied after
+an msvc instance is selected. This could be the default version of msvc if &cv-link-MSVC_VERSION;
+is not specified.
+
+
+
+The valid values for MSVC_TOOLSET_VERSION are: None
+or a string containing the requested toolset version (e.g., '14.29').
+
+
+
+MSVC_TOOLSET_VERSION is ignored when the value is None and when
+the value is an empty string. The validation conditions below do not apply.
+
+
+
+An exception is raised when any of the following conditions are satisfied:
+
+
+
+MSVC_TOOLSET_VERSION is specified for Visual Studio 2015 and earlier.
+
+
+
+MSVC_TOOLSET_VERSION is specified and a toolset version argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via MSVC_TOOLSET_VERSION
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+
+
+
+
+The MSVC_TOOLSET_VERSION specified does not match any of the supported formats:
+
+
+
+
+
+'XX.Y'
+
+
+
+'XX.YY'
+
+
+
+'XX.YY.ZZZZZ'
+
+
+
+'XX.YY.Z' to 'XX.YY.ZZZZ'
+
+[&scons; extension not directly supported by the msvc batch files and may be removed in the future]
+
+
+
+
+'XX.YY.ZZ.N' [SxS format]
+
+
+
+'XX.YY.ZZ.NN' [SxS format]
+
+
+
+
+
+
+
+The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified
+is for Visual Studio 2013 and earlier (e.g., '12.0').
+
+
+
+The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified
+is greater than the msvc version selected (e.g., '99.0').
+
+
+
+A system folder for the corresponding MSVC_TOOLSET_VERSION version is not found.
+The requested toolset version does not appear to be installed.
+
+
+
+
+
+
+Toolset selection details:
+
+
+
+SxS version numbers are not always in three dot format (e.g., 'XX.YY.ZZ.NN') as shown above.
+
+In Visual Studio 2022 for example, 14.16 is an SxS toolset version that is directly
+mapped to toolset version 14.16.27023.
+
+
+
+When MSVC_TOOLSET_VERSION is not an SxS version number or a full toolset version number:
+the first toolset version, ranked in descending order, that matches the MSVC_TOOLSET_VERSION
+prefix is selected.
+
+
+
+When MSVC_TOOLSET_VERSION is specified using the major msvc version prefix
+(i.e., 'XX.Y') and the major msvc version is that of the latest release of
+Visual Studio, the selected toolset version may not be the same as the default Visual C++ toolset version.
+
+In the latest release of Visual Studio, the default Visual C++ toolset version is not necessarily the
+toolset with the largest version number.
+
+
+
+
+
+
+Example environment constructor invocations with toolset version specifications:
+
+Environment(MSVC_TOOLSET_VERSION='14.2')
+Environment(MSVC_TOOLSET_VERSION='14.29')
+Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.30133')
+Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.16.11')
+
+
+
+
+Important usage details:
+
+
+
+MSVC_TOOLSET_VERSION must be passed as an argument to the Environment() constructor;
+setting it later has no effect.
+
+
+
+
+The existence of the toolset host architecture and target architecture folders are not verified
+when MSVC_TOOLSET_VERSION is specified which could result in build failures.
+
+The burden is on the user to ensure the requisite toolset target architecture build tools are installed.
+
+
+
+
@@ -4879,9 +5356,24 @@ env = Environment(MSVC_VERSION='8.0', MSVC_USE_SETTINGS=msvc_use_settings)
-Note: the dictionary content requirements are based on the internal msvc implementation and
-therefore may change at any time. The burden is on the user to ensure the dictionary contents
-are minimally sufficient to ensure successful builds.
+Important usage details:
+
+
+
+MSVC_USE_SETTINGS must be passed as an argument to the Environment() constructor;
+setting it later has no effect.
+
+
+
+
+The dictionary content requirements are based on the internal msvc implementation and
+therefore may change at any time.
+
+The burden is on the user to ensure the dictionary contents are minimally sufficient to
+ensure successful builds.
+
+
+
@@ -4891,21 +5383,65 @@ are minimally sufficient to ensure successful builds.
MSVC_UWP_APP
-Build libraries for a Universal Windows Platform (UWP) Application.
+Build with the Universal Windows Platform (UWP) application Visual C++ libraries.
+
+
+
+The valid values for MSVC_UWP_APP are True,
+'1', False, '0',
+or None.
-If &cv-MSVC_UWP_APP; is set, the Visual C++ environment will be set up to point
+When MSVC_UWP_APP is enabled (i.e., True or
+'1'), the Visual C++ environment will be set up to point
to the Windows Store compatible libraries and Visual C++ runtimes. In doing so,
any libraries that are built will be able to be used in a UWP App and published
to the Windows Store.
-This flag will only have an effect with Visual Studio 2015 or later.
-This variable must be passed as an argument to the Environment()
-constructor; setting it later has no effect.
+
+
-Valid values are '1' or '0'
+An exception is raised when any of the following conditions are satisfied:
+
+
+MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier.
+
+
+MSVC_UWP_APP is enabled and a UWP argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple UWP declarations via MSVC_UWP_APP
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+
+
+
+
+
+Example - A Visual Studio 2022 build for the Universal Windows Platform:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_UWP_APP=True)
+
+
+
+
+Important usage details:
+
+
+
+MSVC_UWP_APP must be passed as an argument to the Environment() constructor;
+setting it later has no effect.
+
+
+
+
+The existence of the UWP libraries is not verified when MSVC_UWP_APP is enabled
+which could result in build failures.
+
+The burden is on the user to ensure the requisite UWP libraries are installed.
+
+
+
diff --git a/doc/generated/variables.mod b/doc/generated/variables.mod
index c5cd0cd..d3d29f2 100644
--- a/doc/generated/variables.mod
+++ b/doc/generated/variables.mod
@@ -321,6 +321,10 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
$MSSDK_VERSION">
$MSVC_BATCH">
$MSVC_NOTFOUND_POLICY">
+$MSVC_SCRIPT_ARGS">
+$MSVC_SDK_VERSION">
+$MSVC_SPECTRE_LIBS">
+$MSVC_TOOLSET_VERSION">
$MSVC_USE_SCRIPT">
$MSVC_USE_SCRIPT_ARGS">
$MSVC_USE_SETTINGS">
@@ -985,6 +989,10 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
$MSSDK_VERSION">
$MSVC_BATCH">
$MSVC_NOTFOUND_POLICY">
+$MSVC_SCRIPT_ARGS">
+$MSVC_SDK_VERSION">
+$MSVC_SPECTRE_LIBS">
+$MSVC_TOOLSET_VERSION">
$MSVC_USE_SCRIPT">
$MSVC_USE_SCRIPT_ARGS">
$MSVC_USE_SETTINGS">
--
cgit v0.12
From 623cdba27514874cd1af6e09911153885574113d Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Fri, 24 Jun 2022 19:21:22 -0400
Subject: Documentation updates [ci skip]
---
SCons/Tool/msvc.xml | 199 ++++++++++++++++++++++++++------------------
doc/generated/variables.gen | 199 ++++++++++++++++++++++++++------------------
2 files changed, 234 insertions(+), 164 deletions(-)
diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml
index 6663aab..de82db0 100644
--- a/SCons/Tool/msvc.xml
+++ b/SCons/Tool/msvc.xml
@@ -349,8 +349,15 @@ Sets the preferred version of Microsoft Visual C/C++ to use.
If &cv-MSVC_VERSION; is not set, SCons will (by default) select the
latest version of Visual C/C++ installed on your system. If the
specified version isn't installed, tool initialization will fail.
-This variable must be passed as an argument to the &f-link-Environment;
-constructor; setting it later has no effect.
+
+
+
+&cv-MSVC_VERSION; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_VERSION; must be set before the first msvc tool is
+loaded into the environment.
@@ -410,7 +417,7 @@ is, if you are sure everything is set correctly already and
you don't want &SCons; to change anything.
-&cv-MSVC_USE_SCRIPT; overrides &cv-link-MSVC_VERSION; and &cv-link-TARGET_ARCH;.
+&cv-MSVC_USE_SCRIPT; ignores &cv-link-MSVC_VERSION; and &cv-link-TARGET_ARCH;.
@@ -485,8 +492,12 @@ Important usage details:
-MSVC_USE_SETTINGS must be passed as an argument to the Environment() constructor;
-setting it later has no effect.
+&cv-MSVC_USE_SETTINGS; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_USE_SETTINGS; must be set before the first msvc tool is
+loaded into the environment.
@@ -529,13 +540,13 @@ Build with the Universal Windows Platform (UWP) application Visual C++ libraries
-The valid values for MSVC_UWP_APP are True,
+The valid values for &cv-MSVC_UWP_APP; are: True,
'1', False, '0',
or None.
-When MSVC_UWP_APP is enabled (i.e., True or
+When &cv-MSVC_UWP_APP; is enabled (i.e., True or
'1'), the Visual C++ environment will be set up to point
to the Windows Store compatible libraries and Visual C++ runtimes. In doing so,
any libraries that are built will be able to be used in a UWP App and published
@@ -549,11 +560,11 @@ constructor; setting it later has no effect. -->
An exception is raised when any of the following conditions are satisfied:
-MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier.
+&cv-MSVC_UWP_APP; is enabled for Visual Studio 2013 and earlier.
-MSVC_UWP_APP is enabled and a UWP argument is specified in
-&cv-link-MSVC_SCRIPT_ARGS;. Multiple UWP declarations via MSVC_UWP_APP
+&cv-MSVC_UWP_APP; is enabled and a UWP argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple UWP declarations via &cv-MSVC_UWP_APP;
and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
@@ -571,13 +582,17 @@ Important usage details:
-MSVC_UWP_APP must be passed as an argument to the Environment() constructor;
-setting it later has no effect.
+&cv-MSVC_UWP_APP; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_UWP_APP; must be set before the first msvc tool is
+loaded into the environment.
-The existence of the UWP libraries is not verified when MSVC_UWP_APP is enabled
+The existence of the UWP libraries is not verified when &cv-MSVC_UWP_APP; is enabled
which could result in build failures.
The burden is on the user to ensure the requisite UWP libraries are installed.
@@ -645,12 +660,12 @@ Specify the &scons; behavior when the Microsoft Visual C/C++ compiler is not det
- The MSVC_NOTFOUND_POLICY specifies the &scons; behavior when no msvc versions are detected or
+ The &cv-MSVC_NOTFOUND_POLICY; specifies the &scons; behavior when no msvc versions are detected or
when the requested msvc version is not detected.
-The valid values for MSVC_NOTFOUND_POLICY and the corresponding &scons; behavior are:
+The valid values for &cv-MSVC_NOTFOUND_POLICY; and the corresponding &scons; behavior are:
@@ -691,7 +706,7 @@ Note: in addition to the camel case values shown above, lower case and upper cas
-The MSVC_NOTFOUND_POLICY is applied when any of the following conditions are satisfied:
+The &cv-MSVC_NOTFOUND_POLICY; is applied when any of the following conditions are satisfied:
&cv-MSVC_VERSION; is specified, the default tools list is implicitly defined (i.e., the tools list is not specified),
@@ -708,7 +723,7 @@ A non-default tools list is specified that contains one or more of the msvc tool
-The MSVC_NOTFOUND_POLICY is ignored when any of the following conditions are satisfied:
+The &cv-MSVC_NOTFOUND_POLICY; is ignored when any of the following conditions are satisfied:
&cv-MSVC_VERSION; is not specified and the default tools list is implicitly defined (i.e., the tools list is not specified).
@@ -727,15 +742,19 @@ Important usage details:
-MSVC_NOTFOUND_POLICY must be passed as an argument to the Environment() constructor;
-setting it later has no effect.
+&cv-MSVC_NOTFOUND_POLICY; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_NOTFOUND_POLICY; must be set before the first msvc tool is
+loaded into the environment.
-When MSVC_NOTFOUND_POLICY is not specified, the default &scons; behavior is to issue a warning and continue
+When &cv-MSVC_NOTFOUND_POLICY; is not specified, the default &scons; behavior is to issue a warning and continue
subject to the conditions listed above. The default &scons; behavior may change in the future.
@@ -749,25 +768,25 @@ Pass user-defined arguments to the Visual C++ batch file determined via autodete
-MSVC_SCRIPT_ARGS is available for msvc batch file arguments that do not have first-class support
+&cv-MSVC_SCRIPT_ARGS; is available for msvc batch file arguments that do not have first-class support
via construction variables or when there is an issue with the appropriate construction variable validation.
When available, it is recommended to use the appropriate construction variables (e.g., &cv-link-MSVC_TOOLSET_VERSION;)
-rather than MSVC_SCRIPT_ARGS arguments.
+rather than &cv-MSVC_SCRIPT_ARGS; arguments.
-The valid values for MSVC_SCRIPT_ARGS are: None, a string,
+The valid values for &cv-MSVC_SCRIPT_ARGS; are: None, a string,
or a list of strings.
-The MSVC_SCRIPT_ARGS value is converted to a scalar string (i.e., "flattened").
+The &cv-MSVC_SCRIPT_ARGS; value is converted to a scalar string (i.e., "flattened").
The resulting scalar string, if not empty, is passed as an argument to the msvc batch file determined
via autodetection subject to the validation conditions listed below.
-MSVC_SCRIPT_ARGS is ignored when the value is None and when the
+&cv-MSVC_SCRIPT_ARGS; is ignored when the value is None and when the
result from argument conversion is an empty string. The validation conditions below do not apply.
@@ -776,54 +795,54 @@ An exception is raised when any of the following conditions are satisfied:
-MSVC_SCRIPT_ARGS is specified for Visual Studio 2013 and earlier.
+&cv-MSVC_SCRIPT_ARGS; is specified for Visual Studio 2013 and earlier.
Multiple SDK version arguments (e.g., '10.0.20348.0') are specified
-in MSVC_SCRIPT_ARGS.
+in &cv-MSVC_SCRIPT_ARGS;.
&cv-link-MSVC_SDK_VERSION; is specified and an SDK version argument
-(e.g., '10.0.20348.0') is specified in MSVC_SCRIPT_ARGS.
-Multiple SDK version declarations via &cv-link-MSVC_SDK_VERSION; and MSVC_SCRIPT_ARGS
+(e.g., '10.0.20348.0') is specified in &cv-MSVC_SCRIPT_ARGS;.
+Multiple SDK version declarations via &cv-link-MSVC_SDK_VERSION; and &cv-MSVC_SCRIPT_ARGS;
are not allowed.
Multiple toolset version arguments (e.g., '-vcvars_ver=14.29')
-are specified in MSVC_SCRIPT_ARGS.
+are specified in &cv-MSVC_SCRIPT_ARGS;.
&cv-link-MSVC_TOOLSET_VERSION; is specified and a toolset version argument
-(e.g., '-vcvars_ver=14.29') is specified in MSVC_SCRIPT_ARGS.
+(e.g., '-vcvars_ver=14.29') is specified in &cv-MSVC_SCRIPT_ARGS;.
Multiple toolset version declarations via &cv-link-MSVC_TOOLSET_VERSION; and
-MSVC_SCRIPT_ARGS are not allowed.
+&cv-MSVC_SCRIPT_ARGS; are not allowed.
Multiple spectre library arguments (e.g., '-vcvars_spectre_libs=spectre')
-are specified in MSVC_SCRIPT_ARGS.
+are specified in &cv-MSVC_SCRIPT_ARGS;.
&cv-link-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument
(e.g., '-vcvars_spectre_libs=spectre') is specified in
-MSVC_SCRIPT_ARGS. Multiple spectre library declarations via &cv-link-MSVC_SPECTRE_LIBS;
-and MSVC_SCRIPT_ARGS are not allowed.
+&cv-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via &cv-link-MSVC_SPECTRE_LIBS;
+and &cv-MSVC_SCRIPT_ARGS; are not allowed.
Multiple UWP arguments (e.g., uwp or store) are specified
-in MSVC_SCRIPT_ARGS.
+in &cv-MSVC_SCRIPT_ARGS;.
&cv-link-MSVC_UWP_APP; is enabled and a UWP argument (e.g., uwp or
-store) is specified in MSVC_SCRIPT_ARGS. Multiple UWP declarations
-via &cv-link-MSVC_UWP_APP; and MSVC_SCRIPT_ARGS are not allowed.
+store) is specified in &cv-MSVC_SCRIPT_ARGS;. Multiple UWP declarations
+via &cv-link-MSVC_UWP_APP; and &cv-MSVC_SCRIPT_ARGS; are not allowed.
@@ -850,18 +869,22 @@ Important usage details:
-MSVC_SCRIPT_ARGS must be passed as an argument to the Environment() constructor;
-setting it later has no effect.
+&cv-MSVC_SCRIPT_ARGS; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_SCRIPT_ARGS; must be set before the first msvc tool is
+loaded into the environment.
-Other than checking for multiple declarations as described above, MSVC_SCRIPT_ARGS arguments
+Other than checking for multiple declarations as described above, &cv-MSVC_SCRIPT_ARGS; arguments
are not validated.
-Erroneous, inconsistent, and/or version incompatible MSVC_SCRIPT_ARGS arguments are likely
+Erroneous, inconsistent, and/or version incompatible &cv-MSVC_SCRIPT_ARGS; arguments are likely
to result in build failures for reasons that are not readily apparent and may be difficult to diagnose.
The burden is on the user to ensure that the arguments provided to the msvc batch file are valid, consistent
@@ -881,12 +904,12 @@ Build with a specific version of the Microsoft Software Development Kit (SDK).
-The valid values for MSVC_SDK_VERSION are: None
-or a string containing the requested SDK version (e.g., '10.0.20348.0').
+The valid values for &cv-MSVC_SDK_VERSION; are: None
+or a string containing the requested SDK version (e.g., '10.0.20348.0').
-MSVC_SDK_VERSION is ignored when the value is None and when
+&cv-MSVC_SDK_VERSION; is ignored when the value is None and when
the value is an empty string. The validation conditions below do not apply.
@@ -895,17 +918,17 @@ An exception is raised when any of the following conditions are satisfied:
-MSVC_SDK_VERSION is specified for Visual Studio 2013 and earlier.
+&cv-MSVC_SDK_VERSION; is specified for Visual Studio 2013 and earlier.
-MSVC_SDK_VERSION is specified and an SDK version argument is specified in
-&cv-link-MSVC_SCRIPT_ARGS;. Multiple SDK version declarations via MSVC_SDK_VERSION
+&cv-MSVC_SDK_VERSION; is specified and an SDK version argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple SDK version declarations via &cv-MSVC_SDK_VERSION;
and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
-The MSVC_SDK_VERSION specified does not match any of the supported formats:
+The &cv-MSVC_SDK_VERSION; specified does not match any of the supported formats:
'10.0.XXXXX.Y' [SDK 10.0]
@@ -917,12 +940,12 @@ The MSVC_SDK_VERSION specified does not match any of the supporte
-The system folder for the corresponding MSVC_SDK_VERSION version is not found.
+The system folder for the corresponding &cv-MSVC_SDK_VERSION; version is not found.
The requested SDK version does not appear to be installed.
-The MSVC_SDK_VERSION version does not appear to support the requested platform
+The &cv-MSVC_SDK_VERSION; version does not appear to support the requested platform
type (i.e., UWP or Desktop). The requested SDK version
platform type components do not appear to be installed.
@@ -949,8 +972,12 @@ Important usage details:
-MSVC_SDK_VERSION must be passed as an argument to the Environment() constructor;
-setting it later has no effect.
+&cv-MSVC_SDK_VERSION; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_SDK_VERSION; must be set before the first msvc tool is
+loaded into the environment.
@@ -969,7 +996,7 @@ the requested SDK version is installed with the necessary platform type componen
There is a known issue with the Microsoft libraries for SDK version '10.0.22000.0'
when using the v141 build tools and the target architecture is ARM64.
-Should build failures arise with this combination of settings, MSVC_SDK_VERSION may be
+Should build failures arise with this combination of settings, &cv-MSVC_SDK_VERSION; may be
employed to specify a different SDK version for the build.
@@ -986,19 +1013,19 @@ Build with a specific Visual C++ toolset version.
-Specifying MSVC_TOOLSET_VERSION does not affect the autodetection and selection
-of msvc instances. The MSVC_TOOLSET_VERSION is applied after
+Specifying &cv-MSVC_TOOLSET_VERSION; does not affect the autodetection and selection
+of msvc instances. The &cv-MSVC_TOOLSET_VERSION; is applied after
an msvc instance is selected. This could be the default version of msvc if &cv-link-MSVC_VERSION;
is not specified.
-The valid values for MSVC_TOOLSET_VERSION are: None
-or a string containing the requested toolset version (e.g., '14.29').
+The valid values for &cv-MSVC_TOOLSET_VERSION; are: None
+or a string containing the requested toolset version (e.g., '14.29').
-MSVC_TOOLSET_VERSION is ignored when the value is None and when
+&cv-MSVC_TOOLSET_VERSION; is ignored when the value is None and when
the value is an empty string. The validation conditions below do not apply.
@@ -1007,18 +1034,18 @@ An exception is raised when any of the following conditions are satisfied:
-MSVC_TOOLSET_VERSION is specified for Visual Studio 2015 and earlier.
+&cv-MSVC_TOOLSET_VERSION; is specified for Visual Studio 2015 and earlier.
-MSVC_TOOLSET_VERSION is specified and a toolset version argument is specified in
-&cv-link-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via MSVC_TOOLSET_VERSION
+&cv-MSVC_TOOLSET_VERSION; is specified and a toolset version argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via &cv-MSVC_TOOLSET_VERSION;
and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
-The MSVC_TOOLSET_VERSION specified does not match any of the supported formats:
+The &cv-MSVC_TOOLSET_VERSION; specified does not match any of the supported formats:
@@ -1055,17 +1082,17 @@ The MSVC_TOOLSET_VERSION specified does not match any of the supp
-The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified
-is for Visual Studio 2013 and earlier (e.g., '12.0').
+The major msvc version prefix (i.e., 'XX.Y') of the &cv-MSVC_TOOLSET_VERSION; specified
+is for Visual Studio 2013 and earlier (e.g., '12.0').
-The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified
-is greater than the msvc version selected (e.g., '99.0').
+The major msvc version prefix (i.e., 'XX.Y') of the &cv-MSVC_TOOLSET_VERSION; specified
+is greater than the msvc version selected (e.g., '99.0').
-A system folder for the corresponding MSVC_TOOLSET_VERSION version is not found.
+A system folder for the corresponding &cv-MSVC_TOOLSET_VERSION; version is not found.
The requested toolset version does not appear to be installed.
@@ -1084,13 +1111,13 @@ mapped to toolset version 14.16.27023.
-When MSVC_TOOLSET_VERSION is not an SxS version number or a full toolset version number:
-the first toolset version, ranked in descending order, that matches the MSVC_TOOLSET_VERSION
+When &cv-MSVC_TOOLSET_VERSION; is not an SxS version number or a full toolset version number:
+the first toolset version, ranked in descending order, that matches the &cv-MSVC_TOOLSET_VERSION;
prefix is selected.
-When MSVC_TOOLSET_VERSION is specified using the major msvc version prefix
+When &cv-MSVC_TOOLSET_VERSION; is specified using the major msvc version prefix
(i.e., 'XX.Y') and the major msvc version is that of the latest release of
Visual Studio, the selected toolset version may not be the same as the default Visual C++ toolset version.
@@ -1116,14 +1143,18 @@ Important usage details:
-MSVC_TOOLSET_VERSION must be passed as an argument to the Environment() constructor;
-setting it later has no effect.
+&cv-MSVC_TOOLSET_VERSION; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_TOOLSET_VERSION; must be set before the first msvc tool is
+loaded into the environment.
The existence of the toolset host architecture and target architecture folders are not verified
-when MSVC_TOOLSET_VERSION is specified which could result in build failures.
+when &cv-MSVC_TOOLSET_VERSION; is specified which could result in build failures.
The burden is on the user to ensure the requisite toolset target architecture build tools are installed.
@@ -1141,12 +1172,12 @@ Build with the spectre-mitigated Visual C++ libraries.
-The valid values for MSVC_SPECTRE_LIBS are: True,
+The valid values for &cv-MSVC_SPECTRE_LIBS; are: True,
False, or None.
-When MSVC_SPECTRE_LIBS is enabled (i.e., True),
+When &cv-MSVC_SPECTRE_LIBS; is enabled (i.e., True),
the Visual C++ environment will include the paths to the spectre-mitigated implementations
of the Microsoft Visual C++ libraries.
@@ -1156,12 +1187,12 @@ An exception is raised when any of the following conditions are satisfied:
-MSVC_SPECTRE_LIBS is enabled for Visual Studio 2015 and earlier.
+&cv-MSVC_SPECTRE_LIBS; is enabled for Visual Studio 2015 and earlier.
-MSVC_SPECTRE_LIBS is enabled and a spectre library argument is specified in
-&cv-link-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via MSVC_SPECTRE_LIBS
+&cv-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via &cv-MSVC_SPECTRE_LIBS;
and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
@@ -1180,8 +1211,12 @@ Important usage details:
-MSVC_SPECTRE_LIBS must be passed as an argument to the Environment() constructor;
-setting it later has no effect.
+&cv-MSVC_SPECTRE_LIBS; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_SPECTRE_LIBS; must be set before the first msvc tool is
+loaded into the environment.
@@ -1192,7 +1227,7 @@ details.
-The existence of the spectre mitigations libraries is not verified when MSVC_SPECTRE_LIBS
+The existence of the spectre mitigations libraries is not verified when &cv-MSVC_SPECTRE_LIBS;
is enabled which could result in build failures.
The burden is on the user to ensure the requisite libraries with spectre mitigations are installed.
diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen
index f1b3fff..a0c2dc4 100644
--- a/doc/generated/variables.gen
+++ b/doc/generated/variables.gen
@@ -4690,12 +4690,12 @@ Specify the &scons; behavior when the Microsoft Visual C/C++ compiler is not det
- The MSVC_NOTFOUND_POLICY specifies the &scons; behavior when no msvc versions are detected or
+ The &cv-MSVC_NOTFOUND_POLICY; specifies the &scons; behavior when no msvc versions are detected or
when the requested msvc version is not detected.
-The valid values for MSVC_NOTFOUND_POLICY and the corresponding &scons; behavior are:
+The valid values for &cv-MSVC_NOTFOUND_POLICY; and the corresponding &scons; behavior are:
@@ -4736,7 +4736,7 @@ Note: in addition to the camel case values shown above, lower case and upper cas
-The MSVC_NOTFOUND_POLICY is applied when any of the following conditions are satisfied:
+The &cv-MSVC_NOTFOUND_POLICY; is applied when any of the following conditions are satisfied:
&cv-MSVC_VERSION; is specified, the default tools list is implicitly defined (i.e., the tools list is not specified),
@@ -4753,7 +4753,7 @@ A non-default tools list is specified that contains one or more of the msvc tool
-The MSVC_NOTFOUND_POLICY is ignored when any of the following conditions are satisfied:
+The &cv-MSVC_NOTFOUND_POLICY; is ignored when any of the following conditions are satisfied:
&cv-MSVC_VERSION; is not specified and the default tools list is implicitly defined (i.e., the tools list is not specified).
@@ -4772,15 +4772,19 @@ Important usage details:
-MSVC_NOTFOUND_POLICY must be passed as an argument to the Environment() constructor;
-setting it later has no effect.
+&cv-MSVC_NOTFOUND_POLICY; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_NOTFOUND_POLICY; must be set before the first msvc tool is
+loaded into the environment.
-When MSVC_NOTFOUND_POLICY is not specified, the default &scons; behavior is to issue a warning and continue
+When &cv-MSVC_NOTFOUND_POLICY; is not specified, the default &scons; behavior is to issue a warning and continue
subject to the conditions listed above. The default &scons; behavior may change in the future.
@@ -4795,25 +4799,25 @@ Pass user-defined arguments to the Visual C++ batch file determined via autodete
-MSVC_SCRIPT_ARGS is available for msvc batch file arguments that do not have first-class support
+&cv-MSVC_SCRIPT_ARGS; is available for msvc batch file arguments that do not have first-class support
via construction variables or when there is an issue with the appropriate construction variable validation.
When available, it is recommended to use the appropriate construction variables (e.g., &cv-link-MSVC_TOOLSET_VERSION;)
-rather than MSVC_SCRIPT_ARGS arguments.
+rather than &cv-MSVC_SCRIPT_ARGS; arguments.
-The valid values for MSVC_SCRIPT_ARGS are: None, a string,
+The valid values for &cv-MSVC_SCRIPT_ARGS; are: None, a string,
or a list of strings.
-The MSVC_SCRIPT_ARGS value is converted to a scalar string (i.e., "flattened").
+The &cv-MSVC_SCRIPT_ARGS; value is converted to a scalar string (i.e., "flattened").
The resulting scalar string, if not empty, is passed as an argument to the msvc batch file determined
via autodetection subject to the validation conditions listed below.
-MSVC_SCRIPT_ARGS is ignored when the value is None and when the
+&cv-MSVC_SCRIPT_ARGS; is ignored when the value is None and when the
result from argument conversion is an empty string. The validation conditions below do not apply.
@@ -4822,54 +4826,54 @@ An exception is raised when any of the following conditions are satisfied:
-MSVC_SCRIPT_ARGS is specified for Visual Studio 2013 and earlier.
+&cv-MSVC_SCRIPT_ARGS; is specified for Visual Studio 2013 and earlier.
Multiple SDK version arguments (e.g., '10.0.20348.0') are specified
-in MSVC_SCRIPT_ARGS.
+in &cv-MSVC_SCRIPT_ARGS;.
&cv-link-MSVC_SDK_VERSION; is specified and an SDK version argument
-(e.g., '10.0.20348.0') is specified in MSVC_SCRIPT_ARGS.
-Multiple SDK version declarations via &cv-link-MSVC_SDK_VERSION; and MSVC_SCRIPT_ARGS
+(e.g., '10.0.20348.0') is specified in &cv-MSVC_SCRIPT_ARGS;.
+Multiple SDK version declarations via &cv-link-MSVC_SDK_VERSION; and &cv-MSVC_SCRIPT_ARGS;
are not allowed.
Multiple toolset version arguments (e.g., '-vcvars_ver=14.29')
-are specified in MSVC_SCRIPT_ARGS.
+are specified in &cv-MSVC_SCRIPT_ARGS;.
&cv-link-MSVC_TOOLSET_VERSION; is specified and a toolset version argument
-(e.g., '-vcvars_ver=14.29') is specified in MSVC_SCRIPT_ARGS.
+(e.g., '-vcvars_ver=14.29') is specified in &cv-MSVC_SCRIPT_ARGS;.
Multiple toolset version declarations via &cv-link-MSVC_TOOLSET_VERSION; and
-MSVC_SCRIPT_ARGS are not allowed.
+&cv-MSVC_SCRIPT_ARGS; are not allowed.
Multiple spectre library arguments (e.g., '-vcvars_spectre_libs=spectre')
-are specified in MSVC_SCRIPT_ARGS.
+are specified in &cv-MSVC_SCRIPT_ARGS;.
&cv-link-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument
(e.g., '-vcvars_spectre_libs=spectre') is specified in
-MSVC_SCRIPT_ARGS. Multiple spectre library declarations via &cv-link-MSVC_SPECTRE_LIBS;
-and MSVC_SCRIPT_ARGS are not allowed.
+&cv-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via &cv-link-MSVC_SPECTRE_LIBS;
+and &cv-MSVC_SCRIPT_ARGS; are not allowed.
Multiple UWP arguments (e.g., uwp or store) are specified
-in MSVC_SCRIPT_ARGS.
+in &cv-MSVC_SCRIPT_ARGS;.
&cv-link-MSVC_UWP_APP; is enabled and a UWP argument (e.g., uwp or
-store) is specified in MSVC_SCRIPT_ARGS. Multiple UWP declarations
-via &cv-link-MSVC_UWP_APP; and MSVC_SCRIPT_ARGS are not allowed.
+store) is specified in &cv-MSVC_SCRIPT_ARGS;. Multiple UWP declarations
+via &cv-link-MSVC_UWP_APP; and &cv-MSVC_SCRIPT_ARGS; are not allowed.
@@ -4896,18 +4900,22 @@ Important usage details:
-MSVC_SCRIPT_ARGS must be passed as an argument to the Environment() constructor;
-setting it later has no effect.
+&cv-MSVC_SCRIPT_ARGS; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_SCRIPT_ARGS; must be set before the first msvc tool is
+loaded into the environment.
-Other than checking for multiple declarations as described above, MSVC_SCRIPT_ARGS arguments
+Other than checking for multiple declarations as described above, &cv-MSVC_SCRIPT_ARGS; arguments
are not validated.
-Erroneous, inconsistent, and/or version incompatible MSVC_SCRIPT_ARGS arguments are likely
+Erroneous, inconsistent, and/or version incompatible &cv-MSVC_SCRIPT_ARGS; arguments are likely
to result in build failures for reasons that are not readily apparent and may be difficult to diagnose.
The burden is on the user to ensure that the arguments provided to the msvc batch file are valid, consistent
@@ -4928,12 +4936,12 @@ Build with a specific version of the Microsoft Software Development Kit (SDK).
-The valid values for MSVC_SDK_VERSION are: None
-or a string containing the requested SDK version (e.g., '10.0.20348.0').
+The valid values for &cv-MSVC_SDK_VERSION; are: None
+or a string containing the requested SDK version (e.g., '10.0.20348.0').
-MSVC_SDK_VERSION is ignored when the value is None and when
+&cv-MSVC_SDK_VERSION; is ignored when the value is None and when
the value is an empty string. The validation conditions below do not apply.
@@ -4942,17 +4950,17 @@ An exception is raised when any of the following conditions are satisfied:
-MSVC_SDK_VERSION is specified for Visual Studio 2013 and earlier.
+&cv-MSVC_SDK_VERSION; is specified for Visual Studio 2013 and earlier.
-MSVC_SDK_VERSION is specified and an SDK version argument is specified in
-&cv-link-MSVC_SCRIPT_ARGS;. Multiple SDK version declarations via MSVC_SDK_VERSION
+&cv-MSVC_SDK_VERSION; is specified and an SDK version argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple SDK version declarations via &cv-MSVC_SDK_VERSION;
and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
-The MSVC_SDK_VERSION specified does not match any of the supported formats:
+The &cv-MSVC_SDK_VERSION; specified does not match any of the supported formats:
'10.0.XXXXX.Y' [SDK 10.0]
@@ -4964,12 +4972,12 @@ The MSVC_SDK_VERSION specified does not match any of the supporte
-The system folder for the corresponding MSVC_SDK_VERSION version is not found.
+The system folder for the corresponding &cv-MSVC_SDK_VERSION; version is not found.
The requested SDK version does not appear to be installed.
-The MSVC_SDK_VERSION version does not appear to support the requested platform
+The &cv-MSVC_SDK_VERSION; version does not appear to support the requested platform
type (i.e., UWP or Desktop). The requested SDK version
platform type components do not appear to be installed.
@@ -4996,8 +5004,12 @@ Important usage details:
-MSVC_SDK_VERSION must be passed as an argument to the Environment() constructor;
-setting it later has no effect.
+&cv-MSVC_SDK_VERSION; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_SDK_VERSION; must be set before the first msvc tool is
+loaded into the environment.
@@ -5016,7 +5028,7 @@ the requested SDK version is installed with the necessary platform type componen
There is a known issue with the Microsoft libraries for SDK version '10.0.22000.0'
when using the v141 build tools and the target architecture is ARM64.
-Should build failures arise with this combination of settings, MSVC_SDK_VERSION may be
+Should build failures arise with this combination of settings, &cv-MSVC_SDK_VERSION; may be
employed to specify a different SDK version for the build.
@@ -5034,12 +5046,12 @@ Build with the spectre-mitigated Visual C++ libraries.
-The valid values for MSVC_SPECTRE_LIBS are: True,
+The valid values for &cv-MSVC_SPECTRE_LIBS; are: True,
False, or None.
-When MSVC_SPECTRE_LIBS is enabled (i.e., True),
+When &cv-MSVC_SPECTRE_LIBS; is enabled (i.e., True),
the Visual C++ environment will include the paths to the spectre-mitigated implementations
of the Microsoft Visual C++ libraries.
@@ -5049,12 +5061,12 @@ An exception is raised when any of the following conditions are satisfied:
-MSVC_SPECTRE_LIBS is enabled for Visual Studio 2015 and earlier.
+&cv-MSVC_SPECTRE_LIBS; is enabled for Visual Studio 2015 and earlier.
-MSVC_SPECTRE_LIBS is enabled and a spectre library argument is specified in
-&cv-link-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via MSVC_SPECTRE_LIBS
+&cv-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via &cv-MSVC_SPECTRE_LIBS;
and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
@@ -5073,8 +5085,12 @@ Important usage details:
-MSVC_SPECTRE_LIBS must be passed as an argument to the Environment() constructor;
-setting it later has no effect.
+&cv-MSVC_SPECTRE_LIBS; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_SPECTRE_LIBS; must be set before the first msvc tool is
+loaded into the environment.
@@ -5085,7 +5101,7 @@ details.
-The existence of the spectre mitigations libraries is not verified when MSVC_SPECTRE_LIBS
+The existence of the spectre mitigations libraries is not verified when &cv-MSVC_SPECTRE_LIBS;
is enabled which could result in build failures.
The burden is on the user to ensure the requisite libraries with spectre mitigations are installed.
@@ -5105,19 +5121,19 @@ Build with a specific Visual C++ toolset version.
-Specifying MSVC_TOOLSET_VERSION does not affect the autodetection and selection
-of msvc instances. The MSVC_TOOLSET_VERSION is applied after
+Specifying &cv-MSVC_TOOLSET_VERSION; does not affect the autodetection and selection
+of msvc instances. The &cv-MSVC_TOOLSET_VERSION; is applied after
an msvc instance is selected. This could be the default version of msvc if &cv-link-MSVC_VERSION;
is not specified.
-The valid values for MSVC_TOOLSET_VERSION are: None
-or a string containing the requested toolset version (e.g., '14.29').
+The valid values for &cv-MSVC_TOOLSET_VERSION; are: None
+or a string containing the requested toolset version (e.g., '14.29').
-MSVC_TOOLSET_VERSION is ignored when the value is None and when
+&cv-MSVC_TOOLSET_VERSION; is ignored when the value is None and when
the value is an empty string. The validation conditions below do not apply.
@@ -5126,18 +5142,18 @@ An exception is raised when any of the following conditions are satisfied:
-MSVC_TOOLSET_VERSION is specified for Visual Studio 2015 and earlier.
+&cv-MSVC_TOOLSET_VERSION; is specified for Visual Studio 2015 and earlier.
-MSVC_TOOLSET_VERSION is specified and a toolset version argument is specified in
-&cv-link-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via MSVC_TOOLSET_VERSION
+&cv-MSVC_TOOLSET_VERSION; is specified and a toolset version argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via &cv-MSVC_TOOLSET_VERSION;
and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
-The MSVC_TOOLSET_VERSION specified does not match any of the supported formats:
+The &cv-MSVC_TOOLSET_VERSION; specified does not match any of the supported formats:
@@ -5174,17 +5190,17 @@ The MSVC_TOOLSET_VERSION specified does not match any of the supp
-The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified
-is for Visual Studio 2013 and earlier (e.g., '12.0').
+The major msvc version prefix (i.e., 'XX.Y') of the &cv-MSVC_TOOLSET_VERSION; specified
+is for Visual Studio 2013 and earlier (e.g., '12.0').
-The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified
-is greater than the msvc version selected (e.g., '99.0').
+The major msvc version prefix (i.e., 'XX.Y') of the &cv-MSVC_TOOLSET_VERSION; specified
+is greater than the msvc version selected (e.g., '99.0').
-A system folder for the corresponding MSVC_TOOLSET_VERSION version is not found.
+A system folder for the corresponding &cv-MSVC_TOOLSET_VERSION; version is not found.
The requested toolset version does not appear to be installed.
@@ -5203,13 +5219,13 @@ mapped to toolset version 14.16.27023.
-When MSVC_TOOLSET_VERSION is not an SxS version number or a full toolset version number:
-the first toolset version, ranked in descending order, that matches the MSVC_TOOLSET_VERSION
+When &cv-MSVC_TOOLSET_VERSION; is not an SxS version number or a full toolset version number:
+the first toolset version, ranked in descending order, that matches the &cv-MSVC_TOOLSET_VERSION;
prefix is selected.
-When MSVC_TOOLSET_VERSION is specified using the major msvc version prefix
+When &cv-MSVC_TOOLSET_VERSION; is specified using the major msvc version prefix
(i.e., 'XX.Y') and the major msvc version is that of the latest release of
Visual Studio, the selected toolset version may not be the same as the default Visual C++ toolset version.
@@ -5235,14 +5251,18 @@ Important usage details:
-MSVC_TOOLSET_VERSION must be passed as an argument to the Environment() constructor;
-setting it later has no effect.
+&cv-MSVC_TOOLSET_VERSION; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_TOOLSET_VERSION; must be set before the first msvc tool is
+loaded into the environment.
The existence of the toolset host architecture and target architecture folders are not verified
-when MSVC_TOOLSET_VERSION is specified which could result in build failures.
+when &cv-MSVC_TOOLSET_VERSION; is specified which could result in build failures.
The burden is on the user to ensure the requisite toolset target architecture build tools are installed.
@@ -5283,7 +5303,7 @@ is, if you are sure everything is set correctly already and
you don't want &SCons; to change anything.
-&cv-MSVC_USE_SCRIPT; overrides &cv-link-MSVC_VERSION; and &cv-link-TARGET_ARCH;.
+&cv-MSVC_USE_SCRIPT; ignores &cv-link-MSVC_VERSION; and &cv-link-TARGET_ARCH;.
@@ -5360,8 +5380,12 @@ Important usage details:
-MSVC_USE_SETTINGS must be passed as an argument to the Environment() constructor;
-setting it later has no effect.
+&cv-MSVC_USE_SETTINGS; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_USE_SETTINGS; must be set before the first msvc tool is
+loaded into the environment.
@@ -5387,13 +5411,13 @@ Build with the Universal Windows Platform (UWP) application Visual C++ libraries
-The valid values for MSVC_UWP_APP are True,
+The valid values for &cv-MSVC_UWP_APP; are: True,
'1', False, '0',
or None.
-When MSVC_UWP_APP is enabled (i.e., True or
+When &cv-MSVC_UWP_APP; is enabled (i.e., True or
'1'), the Visual C++ environment will be set up to point
to the Windows Store compatible libraries and Visual C++ runtimes. In doing so,
any libraries that are built will be able to be used in a UWP App and published
@@ -5407,11 +5431,11 @@ constructor; setting it later has no effect. -->
An exception is raised when any of the following conditions are satisfied:
-MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier.
+&cv-MSVC_UWP_APP; is enabled for Visual Studio 2013 and earlier.
-MSVC_UWP_APP is enabled and a UWP argument is specified in
-&cv-link-MSVC_SCRIPT_ARGS;. Multiple UWP declarations via MSVC_UWP_APP
+&cv-MSVC_UWP_APP; is enabled and a UWP argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple UWP declarations via &cv-MSVC_UWP_APP;
and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
@@ -5429,13 +5453,17 @@ Important usage details:
-MSVC_UWP_APP must be passed as an argument to the Environment() constructor;
-setting it later has no effect.
+&cv-MSVC_UWP_APP; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_UWP_APP; must be set before the first msvc tool is
+loaded into the environment.
-The existence of the UWP libraries is not verified when MSVC_UWP_APP is enabled
+The existence of the UWP libraries is not verified when &cv-MSVC_UWP_APP; is enabled
which could result in build failures.
The burden is on the user to ensure the requisite UWP libraries are installed.
@@ -5458,8 +5486,15 @@ Sets the preferred version of Microsoft Visual C/C++ to use.
If &cv-MSVC_VERSION; is not set, SCons will (by default) select the
latest version of Visual C/C++ installed on your system. If the
specified version isn't installed, tool initialization will fail.
-This variable must be passed as an argument to the &f-link-Environment;
-constructor; setting it later has no effect.
+
+
+
+&cv-MSVC_VERSION; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_VERSION; must be set before the first msvc tool is
+loaded into the environment.
--
cgit v0.12
From 56d8f264280d47bafa19ce982641191bfca5cad2 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Sat, 25 Jun 2022 10:56:42 -0400
Subject: updated blurb in CHANGES.txt and RELEASE.txt to indicate more
specifically what's fixed. Address a few lint issues in the lex.py file
---
CHANGES.txt | 5 ++---
RELEASE.txt | 5 +++--
SCons/Tool/lex.py | 13 ++++++++-----
3 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index c986c1e..87766c3 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -135,9 +135,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
Note that these are called for every build command run by SCons. It could have considerable
performance impact if not used carefully.
to connect to the server during start up.
- - Updated lex emitter to respect escaped spaces when climbing out of a the SConscript dir.
- Previously if the build/source directories absolute path included a space the lex emitter
- would fail to use the correct paths.
+ - lex: Fixed an issue with the lex tool where file arguments specified to either "--header-file="
+ or "--tables-file=" which included a space in the path to the file would be processed incorrectly
- Ninja: added option "--skip-ninja-regen" to enable skipping regeneration of the ninja file
if scons can determine the ninja file doesnot need to be regenerated, which will also
skip restarting the scons daemon. Note this option is could result in incorrect rebuilds
diff --git a/RELEASE.txt b/RELEASE.txt
index da580b8..fce184f 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -111,9 +111,9 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
if scons can determine the ninja file doesnot need to be regenerated, which will also
skip restarting the scons daemon. Note this option is could result in incorrect rebuilds
if scons Glob or scons generated files are used in ninja build target's command lines.
+
FIXES
-----
-
- Fix a number of Python ResourceWarnings which are issued when running SCons and/or it's tests
with python 3.9 (or higher)
- Ninja: Fix issue where Configure files weren't being properly processed when build run
@@ -156,7 +156,8 @@ FIXES
- The system environment variable names imported for MSVC 7.0 and 6.0 were updated to be
consistent with the variables names defined by their respective installers. This fixes an
error caused when bypassing MSVC detection by specifying the MSVC 7.0 batch file directly.
-- Updated lex emitter to respect escaped spaces when climbing out of a the SConscript dir
+- lex: Fixed an issue with the lex tool where file arguments specified to either "--header-file="
+ or "--tables-file=" which included a space in the path to the file would be processed incorrectly
- Suppress issuing a warning when there are no installed Visual Studio instances for the default
tools configuration (issue #2813). When msvc is the default compiler because there are no
compilers installed, a build may fail due to the cl.exe command not being recognized. At
diff --git a/SCons/Tool/lex.py b/SCons/Tool/lex.py
index c33d0fa..96f9bcb 100644
--- a/SCons/Tool/lex.py
+++ b/SCons/Tool/lex.py
@@ -46,6 +46,7 @@ if sys.platform == 'win32':
else:
BINS = ["flex", "lex"]
+
def lexEmitter(target, source, env):
sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0]))
@@ -56,18 +57,19 @@ def lexEmitter(target, source, env):
# files generated by flex.
# Different options that are used to trigger the creation of extra files.
- fileGenOptions = ["--header-file=", "--tables-file="]
+ file_gen_options = ["--header-file=", "--tables-file="]
lexflags = env.subst_list("$LEXFLAGS", target=target, source=source)
for option in SCons.Util.CLVar(lexflags):
- for fileGenOption in fileGenOptions:
+ for fileGenOption in file_gen_options:
l = len(fileGenOption)
if option[:l] == fileGenOption:
# A file generating option is present, so add the
# file name to the target list.
- fileName = option[l:].strip()
- target.append(fileName)
- return (target, source)
+ file_name = option[l:].strip()
+ target.append(file_name)
+ return target, source
+
def get_lex_path(env, append_paths=False):
"""
@@ -128,6 +130,7 @@ def generate(env):
env["LEX"] = env.Detect(BINS)
env["LEXCOM"] = "$LEX $LEXFLAGS -t $SOURCES > $TARGET"
+
def exists(env):
if sys.platform == 'win32':
return get_lex_path(env)
--
cgit v0.12
From 9b7ba04890c6a88b49ff282659199183dda996f9 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Sat, 25 Jun 2022 14:06:38 -0400
Subject: Minor edits. Link to Action Objects instead of just referencing it,
and removed extraneous '(see)'
---
SCons/Environment.xml | 2 +-
doc/man/scons.xml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/SCons/Environment.xml b/SCons/Environment.xml
index 23fba62..485fe39 100644
--- a/SCons/Environment.xml
+++ b/SCons/Environment.xml
@@ -1646,7 +1646,7 @@ and then executed.
Any additional arguments to &f-Execute;
are passed on to the &f-link-Action; factory function
which actually creates the Action object
-(see the manpage section "Action Objects"
+(see the manpage section Action Objects
for a description). Example:
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index 8545cb7..ef96d28 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -6076,7 +6076,7 @@ keyword arguments may not both be supplied in a single call to &f-Action;
Printing of action strings is affected by the setting of
-&cv-link-PRINT_CMD_LINE_FUNC; (see).
+&cv-link-PRINT_CMD_LINE_FUNC;.
Examples:
--
cgit v0.12
From 773f267b2ec42b4e629d01bab51f69d49d1605c8 Mon Sep 17 00:00:00 2001
From: William Deegan
Date: Sat, 25 Jun 2022 15:31:38 -0400
Subject: minor edits plus adding a warning about using chdir
---
doc/man/scons.xml | 27 ++++++++++++++++++++++-----
1 file changed, 22 insertions(+), 5 deletions(-)
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index 2e7784c..19f5eb1 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -2635,7 +2635,7 @@ often (though not always) by tools which determine
whether the external dependencies for the builder are satisfied,
and which perform the necessary setup
(see Tools).
-Builers are attached to a &consenv; as methods.
+Builders are attached to a &consenv; as methods.
The available builder methods are registered as
key-value pairs in the
&cv-link-BUILDERS; attribute of the &consenv;,
@@ -2694,8 +2694,8 @@ env.Program('bar', source='bar.c foo.c'.split())
-Sources and targets can be specified as a a scalar or as a list,
-either of strings or nodes (more on nodes below).
+Sources and targets can be specified as a scalar or as a list,
+composed of either strings or nodes (more on nodes below).
When specifying path strings,
&Python; follows the POSIX pathname convention:
if a string begins with the operating system pathname separator
@@ -2806,7 +2806,7 @@ env.Program('build/prog', ['f1.c', 'f2.c'], srcdir='src')
The optional
parse_flags
-keyword argument causes behavior similarl to the
+keyword argument causes behavior similar to the
&f-link-env-MergeFlags; method, where the argument value is
broken into individual settings and merged into the appropriate &consvars;.
@@ -2841,6 +2841,23 @@ and evaluates true,
then &scons; will change to the
target file's directory.
+
+
+Python only keeps one current directory
+location even if there are multiple threads.
+This means that use of the
+chdir
+argument
+will
+not
+work with the SCons
+
+option,
+because individual worker threads spawned
+by SCons interfere with each other
+when they start changing directory.
+
+
# scons will change to the "sub" subdirectory
# before executing the "cp" command.
@@ -2950,7 +2967,7 @@ which causes it to produce a linker map file in addition
to the executable file actually being linked.
If the &b-link-Program; builder's emitter is configured
to add this mapfile if the option is set,
-then two targets will be returned when you only asked for one.
+then two targets will be returned when you only provided for one.
--
cgit v0.12
From 607d719e44d58de52f261b95a6c7c8a4dfa5b225 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Sat, 25 Jun 2022 19:18:59 -0400
Subject: Minor documentation update based on stress tests [ci skip]
---
SCons/Tool/msvc.xml | 10 ++++++----
doc/generated/variables.gen | 10 ++++++----
2 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml
index de82db0..ab02252 100644
--- a/SCons/Tool/msvc.xml
+++ b/SCons/Tool/msvc.xml
@@ -994,10 +994,12 @@ the requested SDK version is installed with the necessary platform type componen
-There is a known issue with the Microsoft libraries for SDK version '10.0.22000.0'
-when using the v141 build tools and the target architecture is ARM64.
-Should build failures arise with this combination of settings, &cv-MSVC_SDK_VERSION; may be
-employed to specify a different SDK version for the build.
+There is a known issue with the Microsoft libraries when the target architecture is
+ARM64 and a Windows 11 SDK (version '10.0.22000.0' and later) is used
+with the v141 build tools and older v142 toolsets
+(versions '14.28.29333' and earlier). Should build failures arise with these combinations
+of settings due to unresolved symbols in the Microsoft libraries, &cv-MSVC_SDK_VERSION; may be employed to
+specify a Windows 10 SDK (e.g., '10.0.20348.0') for the build.
diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen
index a0c2dc4..c34b70c 100644
--- a/doc/generated/variables.gen
+++ b/doc/generated/variables.gen
@@ -5026,10 +5026,12 @@ the requested SDK version is installed with the necessary platform type componen
-There is a known issue with the Microsoft libraries for SDK version '10.0.22000.0'
-when using the v141 build tools and the target architecture is ARM64.
-Should build failures arise with this combination of settings, &cv-MSVC_SDK_VERSION; may be
-employed to specify a different SDK version for the build.
+There is a known issue with the Microsoft libraries when the target architecture is
+ARM64 and a Windows 11 SDK (version '10.0.22000.0' and later) is used
+with the v141 build tools and older v142 toolsets
+(versions '14.28.29333' and earlier). Should build failures arise with these combinations
+of settings due to unresolved symbols in the Microsoft libraries, &cv-MSVC_SDK_VERSION; may be employed to
+specify a Windows 10 SDK (e.g., '10.0.20348.0') for the build.
--
cgit v0.12
From d98f0faf2c06cda35b3da02a245ad78a32f245a7 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Sun, 26 Jun 2022 09:12:39 -0400
Subject: Update CHANGES.txt and RELEASE.txt
---
CHANGES.txt | 23 ++++++++++++++++++++++-
RELEASE.txt | 24 +++++++++++++++++++++---
2 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index bea5838..c6c6732 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -31,8 +31,29 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
determination when configuring the build environment. This could lead to build failures when
only an MSVC Express instance is installed and the MSVC version is not explicitly specified
(issue #2668 and issue #2697).
- - Added MSVC_USE_SETTINGS variable to pass a dictionary to configure the msvc compiler
+ - Added MSVC_USE_SETTINGS construction variable to pass a dictionary to configure the msvc compiler
system environment as an alternative to bypassing Visual Studio autodetection entirely.
+ - Added MSVC_SDK_VERSION construction variable which allows building with a specific Microsoft
+ SDK version. This variable is used with the msvc batch file determined via autodetection subject
+ to validation constraints. Refer to the documentation for additional requirements and validation
+ details.
+ - Added MSVC_TOOLSET_VERSION construction variable which allows building with a specific toolset
+ version. This variable is used with the msvc batch file determined via autodetection subject to
+ validation constraints. This variable does not affect the autodetection and selection of msvc
+ instances. The toolset version is applied after an msvc instance is selected. This could be the
+ default version of msvc. Refer to the documentation for additional requirements and validation
+ details. Addresses issue #3265, issue #3664, and pull request #4149.
+ - Added MSVC_SPECTRE_LIBS construction variable which allows building with spectre-mitigated
+ Visual C++ libraries. This variable is used with the msvc batch file determined via autodetection
+ subject to validation constraints. Refer to the documentation for additional requirements and
+ validation details.
+ - Added MSVC_SCRIPT_ARGS construction variable which specifies command line arguments that are
+ passed to the msvc batch file determined via autodetection subject to validation constraints.
+ Refer to the documentation for additional requirements and validation details. Addresses
+ enhancement issue #4106.
+ - An exception is raised when MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier.
+ Previous behavior was to silently ignore MSVC_UWP_APP when enabled for Visual Studio 2013
+ and earlier. Refer to the documentation for additional requirements and validation details.
- The imported system environment variable names for MSVC 7.0 and 6.0 have been changed to the
names set by their respective installers. Prior to this change, bypassing MSVC detection by
specifying the MSVC 7.0 batch file directly would fail due to using an erroneous environment
diff --git a/RELEASE.txt b/RELEASE.txt
index c86c1d5..b244fda 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -23,8 +23,23 @@ NEW FUNCTIONALITY
variables). This allows the user to customize how (for example) PATH is constructed.
Note that these are called for every build command run by SCons. It could have considerable
performance impact if not used carefully.
-- Added MSVC_USE_SETTINGS variable to pass a dictionary to configure the msvc compiler
+- Added MSVC_USE_SETTINGS construction variable to pass a dictionary to configure the msvc compiler
system environment as an alternative to bypassing Visual Studio autodetection entirely.
+- Added MSVC_SDK_VERSION construction variable which allows building with a specific Microsoft
+ SDK version. This variable is used with the msvc batch file determined via autodetection. Refer
+ to the documentation for additional requirements and validation details.
+- Added MSVC_TOOLSET_VERSION construction variable which allows building with a specific toolset
+ version. This variable is used with the msvc batch file determined via autodetection. This
+ variable does not affect the autodetection and selection of msvc instances. The toolset version
+ is applied after an msvc instance is selected. This could be the default version of msvc. Refer
+ to the documentation for additional requirements and validation details. Addresses issue #3265,
+ issue #3664, and pull request #4149.
+- Added MSVC_SPECTRE_LIBS construction variable which allows building with spectre-mitigated
+ Visual C++ libraries. This variable is used with the msvc batch file determined via autodetection.
+ Refer to the documentation for additional requirements and validation details.
+- Added MSVC_SCRIPT_ARGS construction variable which specifies command line arguments that are
+ passed to the msvc batch file determined via autodetection. Refer to the documentation for
+ additional requirements and validation details. Addresses enhancement issue #4106.
- Ninja: Added new alias "shutdown-ninja-scons-daemon" to allow ninja to shutdown the daemon.
Also added cleanup to test framework to kill ninja scons daemons and clean ip daemon logs.
NOTE: Test for this requires python psutil module. It will be skipped if not present.
@@ -96,8 +111,8 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
automatically executes ninja.
- Add JavaScanner to include JAVACLASSPATH as a dependency when using the Java tool.
- The build argument (i.e., x86) is no longer passed to the MSVC 6.0 to 7.1 batch
- files. This may improve the effectiveness of the internal msvc cache when using
- MSVC detection and when bypassing MSVC detection as the MSVC 6.0 to 7.1 batch files
+ files. This may improve the effectiveness of the internal msvc cache when using
+ MSVC detection and when bypassing MSVC detection as the MSVC 6.0 to 7.1 batch files
do not expect any arguments.
- Propagate the OS and windir environment variables from the system environment to the msvc
environment. The OS and windir environment variables are used in the MSVC 6.0 batch file
@@ -109,6 +124,9 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
require delayed expansion to be enabled which is currently not supported and is
typically not enabled by default on the host system. The batch files may also require
environment variables that are not included by default in the msvc environment.
+- An exception is raised when MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier.
+ Previous behavior was to silently ignore MSVC_UWP_APP when enabled for Visual Studio 2013
+ and earlier. Refer to the documentation for additional requirements and validation details.
- Ninja: added option "--skip-ninja-regen" to enable skipping regeneration of the ninja file
if scons can determine the ninja file doesnot need to be regenerated, which will also
skip restarting the scons daemon. Note this option is could result in incorrect rebuilds
--
cgit v0.12
From 7a38a4b6547c3776b20f4b1435773a7458a8ebd8 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Sun, 26 Jun 2022 10:54:38 -0400
Subject: Set global lxml etree XSLT maximum traversal depth. Update generated
documentation artifacts.
---
SCons/Tool/docbook/__init__.py | 12 +++++
doc/generated/functions.gen | 52 +++++++++++----------
doc/generated/tools.gen | 17 ++++---
doc/generated/variables.gen | 103 +++++++++++++++++++++++++++--------------
doc/generated/variables.mod | 2 +
5 files changed, 117 insertions(+), 69 deletions(-)
diff --git a/SCons/Tool/docbook/__init__.py b/SCons/Tool/docbook/__init__.py
index 4c3f60c..5cf5e61 100644
--- a/SCons/Tool/docbook/__init__.py
+++ b/SCons/Tool/docbook/__init__.py
@@ -66,6 +66,18 @@ re_manvolnum = re.compile(r"([^<]*)")
re_refname = re.compile(r"([^<]*)")
#
+# lxml etree XSLT global max traversal depth
+#
+
+lmxl_xslt_global_max_depth = 3100
+
+if has_lxml and lmxl_xslt_global_max_depth:
+ def __lxml_xslt_set_global_max_depth(max_depth):
+ from lxml import etree
+ etree.XSLT.set_global_max_depth(max_depth)
+ __lxml_xslt_set_global_max_depth(lmxl_xslt_global_max_depth)
+
+#
# Helper functions
#
def __extend_targets_sources(target, source):
diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen
index a2d9acd..efc4c9e 100644
--- a/doc/generated/functions.gen
+++ b/doc/generated/functions.gen
@@ -1611,45 +1611,49 @@ See the manpage section "Construction Environments" for more details.
- Execute(action, [strfunction, varlist])
- env.Execute(action, [strfunction, varlist])
+ Execute(action, [actionargs ...])
+ env.Execute(action, [actionargs ...])
-Executes an Action object.
-The specified
+Executes an Action.
action
may be an Action object
-(see manpage section "Action Objects"
-for an explanation of behavior),
or it may be a command-line string,
list of commands,
or executable &Python; function,
-each of which will be converted
+each of which will first be converted
into an Action object
and then executed.
Any additional arguments to &f-Execute;
-(strfunction, varlist)
are passed on to the &f-link-Action; factory function
-which actually creates the Action object.
-The exit value of the command
-or return value of the &Python; function
-will be returned.
+which actually creates the Action object
+(see the manpage section Action Objects
+for a description). Example:
+
+
+
+Execute(Copy('file.out', 'file.in'))
+
+
+&f-Execute; performs its action immediately,
+as part of the SConscript-reading phase.
+There are no sources or targets declared in an
+&f-Execute; call, so any objects it manipulates
+will not be tracked as part of the &SCons; dependency graph.
+In the example above, neither
+file.out nor
+file.in will be tracked objects.
-Note that
+&f-Execute; returns the exit value of the command
+or return value of the &Python; function.
&scons;
-will print an error message if the executed
+prints an error message if the executed
action
-fails--that is,
-exits with or returns a non-zero value.
-&scons;
-will
+fails (exits with or returns a non-zero value),
+however it does
not,
-however,
-automatically terminate the build
-if the specified
-action
-fails.
+automatically terminate the build for such a failure.
If you want the build to stop in response to a failed
&f-Execute;
call,
@@ -1657,8 +1661,6 @@ you must explicitly check for a non-zero return value:
-Execute(Copy('file.out', 'file.in'))
-
if Execute("mkdir sub/dir/ectory"):
# The mkdir failed, don't try to build.
Exit(1)
diff --git a/doc/generated/tools.gen b/doc/generated/tools.gen
index ecc301a..35ded17 100644
--- a/doc/generated/tools.gen
+++ b/doc/generated/tools.gen
@@ -389,35 +389,35 @@ Sets construction variables for the dvips utility.
Set construction variables for generic POSIX Fortran 03 compilers.
-Sets: &cv-link-F03;, &cv-link-F03COM;, &cv-link-F03FLAGS;, &cv-link-F03PPCOM;, &cv-link-SHF03;, &cv-link-SHF03COM;, &cv-link-SHF03FLAGS;, &cv-link-SHF03PPCOM;, &cv-link-_F03INCFLAGS;.Uses: &cv-link-F03COMSTR;, &cv-link-F03PPCOMSTR;, &cv-link-SHF03COMSTR;, &cv-link-SHF03PPCOMSTR;.
+Sets: &cv-link-F03;, &cv-link-F03COM;, &cv-link-F03FLAGS;, &cv-link-F03PPCOM;, &cv-link-SHF03;, &cv-link-SHF03COM;, &cv-link-SHF03FLAGS;, &cv-link-SHF03PPCOM;, &cv-link-_F03INCFLAGS;.Uses: &cv-link-F03COMSTR;, &cv-link-F03PPCOMSTR;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-SHF03COMSTR;, &cv-link-SHF03PPCOMSTR;.f08
Set construction variables for generic POSIX Fortran 08 compilers.
-Sets: &cv-link-F08;, &cv-link-F08COM;, &cv-link-F08FLAGS;, &cv-link-F08PPCOM;, &cv-link-SHF08;, &cv-link-SHF08COM;, &cv-link-SHF08FLAGS;, &cv-link-SHF08PPCOM;, &cv-link-_F08INCFLAGS;.Uses: &cv-link-F08COMSTR;, &cv-link-F08PPCOMSTR;, &cv-link-SHF08COMSTR;, &cv-link-SHF08PPCOMSTR;.
+Sets: &cv-link-F08;, &cv-link-F08COM;, &cv-link-F08FLAGS;, &cv-link-F08PPCOM;, &cv-link-SHF08;, &cv-link-SHF08COM;, &cv-link-SHF08FLAGS;, &cv-link-SHF08PPCOM;, &cv-link-_F08INCFLAGS;.Uses: &cv-link-F08COMSTR;, &cv-link-F08PPCOMSTR;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-SHF08COMSTR;, &cv-link-SHF08PPCOMSTR;.f77
Set construction variables for generic POSIX Fortran 77 compilers.
-Sets: &cv-link-F77;, &cv-link-F77COM;, &cv-link-F77FILESUFFIXES;, &cv-link-F77FLAGS;, &cv-link-F77PPCOM;, &cv-link-F77PPFILESUFFIXES;, &cv-link-FORTRAN;, &cv-link-FORTRANCOM;, &cv-link-FORTRANFLAGS;, &cv-link-SHF77;, &cv-link-SHF77COM;, &cv-link-SHF77FLAGS;, &cv-link-SHF77PPCOM;, &cv-link-SHFORTRAN;, &cv-link-SHFORTRANCOM;, &cv-link-SHFORTRANFLAGS;, &cv-link-SHFORTRANPPCOM;, &cv-link-_F77INCFLAGS;.Uses: &cv-link-F77COMSTR;, &cv-link-F77PPCOMSTR;, &cv-link-FORTRANCOMSTR;, &cv-link-FORTRANPPCOMSTR;, &cv-link-SHF77COMSTR;, &cv-link-SHF77PPCOMSTR;, &cv-link-SHFORTRANCOMSTR;, &cv-link-SHFORTRANPPCOMSTR;.
+Sets: &cv-link-F77;, &cv-link-F77COM;, &cv-link-F77FILESUFFIXES;, &cv-link-F77FLAGS;, &cv-link-F77PPCOM;, &cv-link-F77PPFILESUFFIXES;, &cv-link-FORTRAN;, &cv-link-FORTRANCOM;, &cv-link-FORTRANFLAGS;, &cv-link-SHF77;, &cv-link-SHF77COM;, &cv-link-SHF77FLAGS;, &cv-link-SHF77PPCOM;, &cv-link-SHFORTRAN;, &cv-link-SHFORTRANCOM;, &cv-link-SHFORTRANFLAGS;, &cv-link-SHFORTRANPPCOM;, &cv-link-_F77INCFLAGS;.Uses: &cv-link-F77COMSTR;, &cv-link-F77PPCOMSTR;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-FORTRANCOMSTR;, &cv-link-FORTRANFLAGS;, &cv-link-FORTRANPPCOMSTR;, &cv-link-SHF77COMSTR;, &cv-link-SHF77PPCOMSTR;, &cv-link-SHFORTRANCOMSTR;, &cv-link-SHFORTRANFLAGS;, &cv-link-SHFORTRANPPCOMSTR;.f90
Set construction variables for generic POSIX Fortran 90 compilers.
-Sets: &cv-link-F90;, &cv-link-F90COM;, &cv-link-F90FLAGS;, &cv-link-F90PPCOM;, &cv-link-SHF90;, &cv-link-SHF90COM;, &cv-link-SHF90FLAGS;, &cv-link-SHF90PPCOM;, &cv-link-_F90INCFLAGS;.Uses: &cv-link-F90COMSTR;, &cv-link-F90PPCOMSTR;, &cv-link-SHF90COMSTR;, &cv-link-SHF90PPCOMSTR;.
+Sets: &cv-link-F90;, &cv-link-F90COM;, &cv-link-F90FLAGS;, &cv-link-F90PPCOM;, &cv-link-SHF90;, &cv-link-SHF90COM;, &cv-link-SHF90FLAGS;, &cv-link-SHF90PPCOM;, &cv-link-_F90INCFLAGS;.Uses: &cv-link-F90COMSTR;, &cv-link-F90PPCOMSTR;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-SHF90COMSTR;, &cv-link-SHF90PPCOMSTR;.f95
Set construction variables for generic POSIX Fortran 95 compilers.
-Sets: &cv-link-F95;, &cv-link-F95COM;, &cv-link-F95FLAGS;, &cv-link-F95PPCOM;, &cv-link-SHF95;, &cv-link-SHF95COM;, &cv-link-SHF95FLAGS;, &cv-link-SHF95PPCOM;, &cv-link-_F95INCFLAGS;.Uses: &cv-link-F95COMSTR;, &cv-link-F95PPCOMSTR;, &cv-link-SHF95COMSTR;, &cv-link-SHF95PPCOMSTR;.
+Sets: &cv-link-F95;, &cv-link-F95COM;, &cv-link-F95FLAGS;, &cv-link-F95PPCOM;, &cv-link-SHF95;, &cv-link-SHF95COM;, &cv-link-SHF95FLAGS;, &cv-link-SHF95PPCOM;, &cv-link-_F95INCFLAGS;.Uses: &cv-link-F95COMSTR;, &cv-link-F95PPCOMSTR;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-SHF95COMSTR;, &cv-link-SHF95PPCOMSTR;.fortran
@@ -437,10 +437,8 @@ Set construction variables for the &gXX; C++ compiler.
g77
Set construction variables for the &g77; Fortran compiler.
-Calls the &t-f77; Tool module
-to set variables.
-
+Sets: &cv-link-F77;, &cv-link-F77COM;, &cv-link-F77FILESUFFIXES;, &cv-link-F77PPCOM;, &cv-link-F77PPFILESUFFIXES;, &cv-link-FORTRAN;, &cv-link-FORTRANCOM;, &cv-link-FORTRANPPCOM;, &cv-link-SHF77;, &cv-link-SHF77COM;, &cv-link-SHF77FLAGS;, &cv-link-SHF77PPCOM;, &cv-link-SHFORTRAN;, &cv-link-SHFORTRANCOM;, &cv-link-SHFORTRANFLAGS;, &cv-link-SHFORTRANPPCOM;.Uses: &cv-link-F77FLAGS;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-FORTRANFLAGS;.gas
@@ -516,7 +514,8 @@ environment:
gfortran
-Sets construction variables for the GNU F95/F2003 GNU compiler.
+Sets construction variables for the GNU Fortran compiler.
+Calls the &t-link-fortran; Tool module to set variables.
Sets: &cv-link-F77;, &cv-link-F90;, &cv-link-F95;, &cv-link-FORTRAN;, &cv-link-SHF77;, &cv-link-SHF77FLAGS;, &cv-link-SHF90;, &cv-link-SHF90FLAGS;, &cv-link-SHF95;, &cv-link-SHF95FLAGS;, &cv-link-SHFORTRAN;, &cv-link-SHFORTRANFLAGS;.
diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen
index c34b70c..32db05e 100644
--- a/doc/generated/variables.gen
+++ b/doc/generated/variables.gen
@@ -2682,6 +2682,17 @@ in the &cv-link-FORTRANFLAGS;,
+
+
+ FORTRANCOMMONFLAGS
+
+
+General user-specified options that are passed to the Fortran compiler.
+Similar to &cv-link-FORTRANFLAGS;,
+but this variable is applied to all dialects.
+
+
+ FORTRANCOMSTR
@@ -2709,7 +2720,8 @@ default, this is ['.f', '.for', '.ftn']FORTRANFLAGS
-General user-specified options that are passed to the Fortran compiler.
+General user-specified options for the FORTRAN dialect
+that are passed to the Fortran compiler.
Note that this variable does
not
contain
@@ -6578,44 +6590,65 @@ A Python function used to print the command lines as they are executed
or
options or their equivalents).
-The function should take four arguments:
+The function must accept four arguments:
s,
-the command being executed (a string),
target,
-the target being built (file node, list, or string name(s)),
+source and
+env.
+s
+is a string showing the command being executed,
+target,
+is the target being built (file node, list, or string name(s)),
source,
-the source(s) used (file node, list, or string name(s)), and
-env,
-the environment being used.
+is the source(s) used (file node, list, or string name(s)),
+and env
+is the environment being used.
-The function must do the printing itself. The default implementation,
-used if this variable is not set or is None, is:
+The function must do the printing itself.
+The default implementation,
+used if this variable is not set or is None,
+is to just print the string, as in:
def print_cmd_line(s, target, source, env):
- sys.stdout.write(s + "\n")
+ sys.stdout.write(s + "\n")
-Here's an example of a more interesting function:
+Here is an example of a more interesting function:
def print_cmd_line(s, target, source, env):
- sys.stdout.write("Building %s -> %s...\n" %
- (' and '.join([str(x) for x in source]),
- ' and '.join([str(x) for x in target])))
-env=Environment(PRINT_CMD_LINE_FUNC=print_cmd_line)
-env.Program('foo', 'foo.c')
+ sys.stdout.write(
+ "Building %s -> %s...\n"
+ % (
+ ' and '.join([str(x) for x in source]),
+ ' and '.join([str(x) for x in target]),
+ )
+ )
+
+env = Environment(PRINT_CMD_LINE_FUNC=print_cmd_line)
+env.Program('foo', ['foo.c', 'bar.c'])
-This just prints "Building targetname from sourcename..." instead
-of the actual commands.
-Such a function could also log the actual commands to a log file,
-for example.
+This prints:
+
+
+
+...
+scons: Building targets ...
+Building bar.c -> bar.o...
+Building foo.c -> foo.o...
+Building foo.o and bar.o -> foo...
+scons: done building targets.
+
+
+
+Another example could be a function that logs the actual commands to a file.
@@ -7587,9 +7620,9 @@ targets.
def custom_shell_env(env, target, source, shell_env):
"""customize shell_env if desired"""
if str(target[0]) == 'special_target':
- shell_env['SPECIAL_VAR'] = env.subst('SOME_VAR', target=target, source=source)
+ shell_env['SPECIAL_VAR'] = env.subst('SOME_VAR', target=target, source=source)
return shell_env
-
+
env["SHELL_ENV_GENERATORS"] = [custom_shell_env]
@@ -7663,7 +7696,7 @@ Options that are passed to the Fortran 03 compiler
to generated shared-library objects.
You only need to set &cv-link-SHF03FLAGS; if you need to define specific
user options for Fortran 03 files.
-You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
@@ -7751,7 +7784,7 @@ Options that are passed to the Fortran 08 compiler
to generated shared-library objects.
You only need to set &cv-link-SHF08FLAGS; if you need to define specific
user options for Fortran 08 files.
-You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
@@ -7839,7 +7872,7 @@ Options that are passed to the Fortran 77 compiler
to generated shared-library objects.
You only need to set &cv-link-SHF77FLAGS; if you need to define specific
user options for Fortran 77 files.
-You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
@@ -7927,7 +7960,7 @@ Options that are passed to the Fortran 90 compiler
to generated shared-library objects.
You only need to set &cv-link-SHF90FLAGS; if you need to define specific
user options for Fortran 90 files.
-You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
@@ -8015,7 +8048,7 @@ Options that are passed to the Fortran 95 compiler
to generated shared-library objects.
You only need to set &cv-link-SHF95FLAGS; if you need to define specific
user options for Fortran 95 files.
-You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
@@ -8389,7 +8422,7 @@ which would be a symlink and point to libtest.so.0.1.2
A command interpreter function that will be called to execute command line
-strings. The function must expect the following arguments:
+strings. The function must accept five arguments:
@@ -8397,18 +8430,18 @@ def spawn(shell, escape, cmd, args, env):
-sh
-is a string naming the shell program to use.
+shell
+is a string naming the shell program to use,
escape
is a function that can be called to escape shell special characters in
-the command line.
+the command line,
cmd
-is the path to the command to be executed.
+is the path to the command to be executed,
args
-is the arguments to the command.
+holds the arguments to the command and
env
-is a dictionary of the environment variables
-in which the command should be executed.
+is a dictionary of environment variables
+defining the execution environment in which the command should be executed.
diff --git a/doc/generated/variables.mod b/doc/generated/variables.mod
index d3d29f2..cc51043 100644
--- a/doc/generated/variables.mod
+++ b/doc/generated/variables.mod
@@ -186,6 +186,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
$File">
$FORTRAN">
$FORTRANCOM">
+$FORTRANCOMMONFLAGS">
$FORTRANCOMSTR">
$FORTRANFILESUFFIXES">
$FORTRANFLAGS">
@@ -854,6 +855,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
$File">
$FORTRAN">
$FORTRANCOM">
+$FORTRANCOMMONFLAGS">
$FORTRANCOMSTR">
$FORTRANFILESUFFIXES">
$FORTRANFLAGS">
--
cgit v0.12
From 20a092ffda8d2492a5745b5aa93c48c14d0d4e76 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Sun, 26 Jun 2022 12:26:12 -0400
Subject: Add blurb for additional MSVC_UWP_APP values accepted.
---
CHANGES.txt | 1 +
RELEASE.txt | 1 +
2 files changed, 2 insertions(+)
diff --git a/CHANGES.txt b/CHANGES.txt
index c6c6732..381b061 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -54,6 +54,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- An exception is raised when MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier.
Previous behavior was to silently ignore MSVC_UWP_APP when enabled for Visual Studio 2013
and earlier. Refer to the documentation for additional requirements and validation details.
+ MSVC_UWP_APP was extended to accept True, False, and None in addition to '1' and '0'.
- The imported system environment variable names for MSVC 7.0 and 6.0 have been changed to the
names set by their respective installers. Prior to this change, bypassing MSVC detection by
specifying the MSVC 7.0 batch file directly would fail due to using an erroneous environment
diff --git a/RELEASE.txt b/RELEASE.txt
index b244fda..12dec62 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -127,6 +127,7 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
- An exception is raised when MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier.
Previous behavior was to silently ignore MSVC_UWP_APP when enabled for Visual Studio 2013
and earlier. Refer to the documentation for additional requirements and validation details.
+ MSVC_UWP_APP was extended to accept True, False, and None in addition to '1' and '0'.
- Ninja: added option "--skip-ninja-regen" to enable skipping regeneration of the ninja file
if scons can determine the ninja file doesnot need to be regenerated, which will also
skip restarting the scons daemon. Note this option is could result in incorrect rebuilds
--
cgit v0.12
From 2e2b4640b09a94b0aa2b2f90581295e28ba83650 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Mon, 27 Jun 2022 10:40:55 -0400
Subject: Rework SxS toolset version support and vcvars bug fix handling.
Update MSVC_TOOLSET_VERSION documentation.
---
SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 81 ++++++++++++++++++++++-------
SCons/Tool/msvc.xml | 39 +++++++++-----
doc/generated/variables.gen | 39 +++++++++-----
3 files changed, 113 insertions(+), 46 deletions(-)
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
index e56dd4a..6a4ce3e 100644
--- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -86,9 +86,22 @@ re_toolset_140 = re.compile(r'''^(?:
(?:14[.]0{2}[.]0{1,5}) # 14.00.0 - 14.00.00000
)$''', re.VERBOSE)
-# valid SxS formats will be matched with re_toolset_full: match 3 '.' format
+# SxS toolset version: MM.mm.VV.vv format
re_toolset_sxs = re.compile(r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$')
+# SxS version bugfix
+_msvc_sxs_bugfix_folder = set()
+_msvc_sxs_bugfix_version = {}
+
+for msvc_version, sxs_version, sxs_bugfix in [
+ # VS2019\Common7\Tools\vsdevcmd\ext\vcvars.bat AzDO Bug#1293526
+ # special handling of the 16.8 SxS toolset, use VC\Auxiliary\Build\14.28 directory and SxS files
+ # if SxS version 14.28 not present/installed, fallback selection of toolset VC\Tools\MSVC\14.28.nnnnn.
+ ('14.2', '14.28.16.8', '14.28')
+]:
+ _msvc_sxs_bugfix_folder.add((msvc_version, sxs_bugfix))
+ _msvc_sxs_bugfix_version[(msvc_version, sxs_version)] = sxs_bugfix
+
# MSVC_SCRIPT_ARGS
re_vcvars_uwp = re.compile(r'(?:(?(?:uwp|store))(?:(?!\S)|$)',re.IGNORECASE)
re_vcvars_sdk = re.compile(r'(?:(?(?:[1-9][0-9]*[.]\S*))(?:(?!\S)|$)',re.IGNORECASE)
@@ -327,14 +340,42 @@ def _msvc_read_toolset_file(msvc, filename):
debug('IndexError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename))
return toolset_version
+def _msvc_sxs_toolset_folder(msvc, sxs_folder):
+
+ if re_toolset_sxs.match(sxs_folder):
+ return sxs_folder
+
+ key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_folder)
+ if key in _msvc_sxs_bugfix_folder:
+ return sxs_folder
+
+ debug('sxs folder: ignore version=%s', repr(sxs_folder))
+ return None
+
+def _msvc_sxs_toolset_version(msvc, sxs_toolset):
+
+ if not re_toolset_sxs.match(sxs_toolset):
+ return None, False
+
+ key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_toolset)
+ sxs_bugfix = _msvc_sxs_bugfix_version.get(key)
+ if not sxs_bugfix:
+ return sxs_toolset, False
+
+ debug('sxs bugfix: version=%s => version=%s', repr(sxs_toolset), repr(sxs_bugfix))
+ return sxs_bugfix, True
+
def _msvc_read_toolset_folders(msvc, vc_dir):
toolsets_sxs = {}
toolsets_full = []
build_dir = os.path.join(vc_dir, "Auxiliary", "Build")
- sxs_toolsets = [f.name for f in os.scandir(build_dir) if f.is_dir()]
- for sxs_toolset in sxs_toolsets:
+ sxs_folders = [f.name for f in os.scandir(build_dir) if f.is_dir()]
+ for sxs_folder in sxs_folders:
+ sxs_toolset = _msvc_sxs_toolset_folder(msvc, sxs_folder)
+ if not sxs_toolset:
+ continue
filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset)
filepath = os.path.join(build_dir, sxs_toolset, filename)
debug('sxs toolset: check file=%s', repr(filepath))
@@ -428,23 +469,27 @@ def _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version):
toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir)
- if msvc.vs_def.vc_buildtools_def.vc_version_numeric == VS2019.vc_buildtools_def.vc_version_numeric:
- # necessary to detect toolset not found
- if toolset_version == '14.28.16.8':
- new_toolset_version = '14.28'
- # VS2019\Common7\Tools\vsdevcmd\ext\vcvars.bat AzDO Bug#1293526
- # special handling of the 16.8 SxS toolset, use VC\Auxiliary\Build\14.28 directory and SxS files
- # if SxS version 14.28 not present/installed, fallback selection of toolset VC\Tools\MSVC\14.28.nnnnn.
- debug(
- 'rewrite toolset_version=%s => toolset_version=%s',
- repr(toolset_version), repr(new_toolset_version)
- )
- toolset_version = new_toolset_version
-
- if toolset_version in toolsets_sxs:
- toolset_vcvars = toolsets_sxs[toolset_version]
+ if toolset_version in toolsets_full:
+ # full toolset version provided
+ toolset_vcvars = toolset_version
return toolset_vcvars
+ sxs_toolset, sxs_isbugfix = _msvc_sxs_toolset_version(msvc, toolset_version)
+ if sxs_toolset:
+ # SxS version provided
+ sxs_version = toolsets_sxs.get(sxs_toolset, None)
+ if sxs_version:
+ # SxS full toolset version
+ if sxs_version in toolsets_full:
+ toolset_vcvars = sxs_version
+ return toolset_vcvars
+ return None
+ # SxS version file missing
+ if not sxs_isbugfix:
+ return None
+ # SxS version bugfix: check toolset version
+ toolset_version = sxs_toolset
+
for toolset_full in toolsets_full:
if toolset_full.startswith(toolset_version):
toolset_vcvars = toolset_full
diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml
index ab02252..11c6478 100644
--- a/SCons/Tool/msvc.xml
+++ b/SCons/Tool/msvc.xml
@@ -1106,13 +1106,6 @@ Toolset selection details:
-SxS version numbers are not always in three dot format (e.g., 'XX.YY.ZZ.NN') as shown above.
-
-In Visual Studio 2022 for example, 14.16 is an SxS toolset version that is directly
-mapped to toolset version 14.16.27023.
-
-
-
When &cv-MSVC_TOOLSET_VERSION; is not an SxS version number or a full toolset version number:
the first toolset version, ranked in descending order, that matches the &cv-MSVC_TOOLSET_VERSION;
prefix is selected.
@@ -1131,13 +1124,31 @@ toolset with the largest version number.
-Example environment constructor invocations with toolset version specifications:
-
-Environment(MSVC_TOOLSET_VERSION='14.2')
-Environment(MSVC_TOOLSET_VERSION='14.29')
-Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.30133')
-Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.16.11')
-
+Example 1 - A default Visual Studio build with a partial toolset version specified:
+
+env = Environment(MSVC_TOOLSET_VERSION='14.2')
+
+
+
+
+Example 2 - A default Visual Studio build with a partial toolset version specified:
+
+env = Environment(MSVC_TOOLSET_VERSION='14.29')
+
+
+
+
+Example 3 - A Visual Studio 2022 build with a full toolset version specified:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.30133')
+
+
+
+
+Example 4 - A Visual Studio 2022 build with an SxS toolset version specified:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.16.11')
+
diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen
index 32db05e..d5980ff 100644
--- a/doc/generated/variables.gen
+++ b/doc/generated/variables.gen
@@ -5226,13 +5226,6 @@ Toolset selection details:
-SxS version numbers are not always in three dot format (e.g., 'XX.YY.ZZ.NN') as shown above.
-
-In Visual Studio 2022 for example, 14.16 is an SxS toolset version that is directly
-mapped to toolset version 14.16.27023.
-
-
-
When &cv-MSVC_TOOLSET_VERSION; is not an SxS version number or a full toolset version number:
the first toolset version, ranked in descending order, that matches the &cv-MSVC_TOOLSET_VERSION;
prefix is selected.
@@ -5251,13 +5244,31 @@ toolset with the largest version number.
-Example environment constructor invocations with toolset version specifications:
-
-Environment(MSVC_TOOLSET_VERSION='14.2')
-Environment(MSVC_TOOLSET_VERSION='14.29')
-Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.30133')
-Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.16.11')
-
+Example 1 - A default Visual Studio build with a partial toolset version specified:
+
+env = Environment(MSVC_TOOLSET_VERSION='14.2')
+
+
+
+
+Example 2 - A default Visual Studio build with a partial toolset version specified:
+
+env = Environment(MSVC_TOOLSET_VERSION='14.29')
+
+
+
+
+Example 3 - A Visual Studio 2022 build with a full toolset version specified:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.30133')
+
+
+
+
+Example 4 - A Visual Studio 2022 build with an SxS toolset version specified:
+
+env = Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.16.11')
+
--
cgit v0.12
From b94e801aba5a6864644a156b5260ce6d721f4fc3 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Mon, 27 Jun 2022 14:36:02 -0400
Subject: Move verify invocation to last line of vc.py. Add verify method to
Config module to check that all _VCVER versions are defined locally. Update
module docstrings for Dispatcher and MSVC init.
---
SCons/Tool/MSCommon/MSVC/Config.py | 15 +++++++++++++++
SCons/Tool/MSCommon/MSVC/Dispatcher.py | 25 ++++++++++++++++++++++---
SCons/Tool/MSCommon/MSVC/__init__.py | 14 ++++++++++++--
SCons/Tool/MSCommon/vc.py | 3 +++
4 files changed, 52 insertions(+), 5 deletions(-)
diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py
index 8f3a2cc..a4cb874 100644
--- a/SCons/Tool/MSCommon/MSVC/Config.py
+++ b/SCons/Tool/MSCommon/MSVC/Config.py
@@ -29,6 +29,12 @@ from collections import (
namedtuple,
)
+from . import Util
+
+from .Exceptions import (
+ MSVCInternalError,
+)
+
from . import Dispatcher
Dispatcher.register_modulename(__name__)
@@ -282,3 +288,12 @@ for policy_value, policy_symbol_list in [
MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol] = policy_def
MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.upper()] = policy_def
+def verify():
+ from .. import vc
+ for msvc_version in vc._VCVER:
+ vc_version = Util.get_version_prefix(msvc_version)
+ if vc_version in MSVC_VERSION_INTERNAL:
+ continue
+ err_msg = 'vc_version {} not in MSVC_VERSION_INTERNAL'.format(repr(vc_version))
+ raise MSVCInternalError(err_msg)
+
diff --git a/SCons/Tool/MSCommon/MSVC/Dispatcher.py b/SCons/Tool/MSCommon/MSVC/Dispatcher.py
index 0b216ca..dab1e15 100644
--- a/SCons/Tool/MSCommon/MSVC/Dispatcher.py
+++ b/SCons/Tool/MSCommon/MSVC/Dispatcher.py
@@ -23,6 +23,25 @@
"""
Internal method dispatcher for Microsoft Visual C/C++.
+
+MSVC modules can register their module (register_modulename) and individual
+classes (register_class) with the method dispatcher during initialization. MSVC
+modules tend to be registered immediately after the Dispatcher import near the
+top of the file. Methods in the MSVC modules can be invoked indirectly without
+having to hard-code the method calls effectively decoupling the upstream module
+with the downstream modules:
+
+The reset method dispatches calls to all registered objects with a reset method
+and/or a _reset method. The reset methods are used to restore data structures
+to their initial state for testing purposes. Typically, this involves clearing
+cached values.
+
+The verify method dispatches calls to all registered objects with a verify
+method and/or a _verify method. The verify methods are used to check that
+initialized data structures distributed across multiple modules are internally
+consistent. An exception is raised when a verification constraint violation
+is detected. Typically, this verifies that initialized dictionaries support
+all of the requisite keys as new versions are added.
"""
import sys
@@ -34,13 +53,13 @@ from ..common import (
_refs = []
-def register_class(ref):
- _refs.append(ref)
-
def register_modulename(modname):
module = sys.modules[modname]
_refs.append(module)
+def register_class(ref):
+ _refs.append(ref)
+
def reset():
debug('')
for ref in _refs:
diff --git a/SCons/Tool/MSCommon/MSVC/__init__.py b/SCons/Tool/MSCommon/MSVC/__init__.py
index c07e849..b0ef5dd 100644
--- a/SCons/Tool/MSCommon/MSVC/__init__.py
+++ b/SCons/Tool/MSCommon/MSVC/__init__.py
@@ -23,6 +23,16 @@
"""
Functions for Microsoft Visual C/C++.
+
+The reset method is used to restore MSVC module data structures to their
+initial state for testing purposes.
+
+The verify method is used as a sanity check that MSVC module data structures
+are internally consistent.
+
+Currently:
+* reset is invoked from reset_installed_vcs in the vc module.
+* verify is invoked from the last line in the vc module.
"""
from . import Exceptions # noqa: F401
@@ -40,6 +50,6 @@ from . import Dispatcher as _Dispatcher
def reset():
_Dispatcher.reset()
-#reset() # testing
-_Dispatcher.verify()
+def verify():
+ _Dispatcher.verify()
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index 675b8d0..7c65879 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -1352,3 +1352,6 @@ def get_msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False):
rval = MSVC.WinSDK.get_msvc_sdk_version_list(msvc_version, msvc_uwp_app)
return rval
+# internal consistency check (should be last)
+MSVC.verify()
+
--
cgit v0.12
From 90922c1195eef8d75664d59ed8668fd8e5679390 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Mon, 27 Jun 2022 15:09:13 -0400
Subject: Add docstrings to MSVC/Util.py methods.
---
SCons/Tool/MSCommon/MSVC/Util.py | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py
index 15abdcd..ba70b24 100644
--- a/SCons/Tool/MSCommon/MSVC/Util.py
+++ b/SCons/Tool/MSCommon/MSVC/Util.py
@@ -29,6 +29,16 @@ import os
import re
def listdir_dirs(p):
+ """Get a list of qualified subdirectory paths from a windows path.
+
+ Args:
+ p: str
+ windows path
+
+ Returns:
+ List[str]: list of qualified subdirectory paths
+
+ """
dirs = []
for dir_name in os.listdir(p):
dir_path = os.path.join(p, dir_name)
@@ -37,6 +47,16 @@ def listdir_dirs(p):
return dirs
def process_path(p):
+ """Normalize a windows path.
+
+ Args:
+ p: str
+ windows path
+
+ Returns:
+ str: normalized windows path
+
+ """
if p:
p = os.path.normpath(p)
p = os.path.realpath(p)
@@ -46,6 +66,16 @@ def process_path(p):
re_version_prefix = re.compile(r'^(?P[0-9.]+).*')
def get_version_prefix(version):
+ """Get the version number prefix from a string.
+
+ Args:
+ version: str
+ version string
+
+ Returns:
+ str: the version number prefix
+
+ """
m = re_version_prefix.match(version)
if m:
rval = m.group('version')
--
cgit v0.12
From d5a2a52087c968058fff3f2cc983f75953235e1a Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Tue, 28 Jun 2022 17:16:49 -0400
Subject: Additional validation for MSVC_SDK_VERSION and MSVC_SPECTRE_LIBS.
Adjust documentation. Add additional exceptions for SDK version not found,
toolset version not found, and spectre libraries not found. Add data
structure for platform type.
---
SCons/Tool/MSCommon/MSVC/Config.py | 27 +++
SCons/Tool/MSCommon/MSVC/Exceptions.py | 11 +-
SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 262 ++++++++++++++++++++++------
SCons/Tool/MSCommon/MSVC/Util.py | 20 +++
SCons/Tool/MSCommon/MSVC/WinSDK.py | 39 +++--
SCons/Tool/msvc.xml | 17 +-
doc/generated/variables.gen | 17 +-
7 files changed, 314 insertions(+), 79 deletions(-)
diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py
index a4cb874..3b71cd8 100644
--- a/SCons/Tool/MSCommon/MSVC/Config.py
+++ b/SCons/Tool/MSCommon/MSVC/Config.py
@@ -55,6 +55,33 @@ for bool, symbol_list, symbol_case_list in [
for symbol in BOOLEAN_SYMBOLS[bool]:
BOOLEAN_EXTERNAL[symbol] = bool
+MSVC_PLATFORM_DEFINITION = namedtuple('MSVCPlatform', [
+ 'vc_platform',
+ 'is_uwp',
+])
+
+MSVC_PLATFORM_DEFINITION_LIST = []
+
+MSVC_PLATFORM_INTERNAL = {}
+MSVC_PLATFORM_EXTERNAL = {}
+
+for vc_platform, is_uwp in [
+ ('Desktop', False),
+ ('UWP', True),
+]:
+
+ vc_platform_def = MSVC_PLATFORM_DEFINITION(
+ vc_platform = vc_platform,
+ is_uwp = is_uwp,
+ )
+
+ MSVC_PLATFORM_DEFINITION_LIST.append(vc_platform_def)
+
+ MSVC_PLATFORM_INTERNAL[vc_platform] = vc_platform_def
+
+ for symbol in [vc_platform, vc_platform.lower(), vc_platform.upper()]:
+ MSVC_PLATFORM_EXTERNAL[symbol] = vc_platform_def
+
MSVC_RUNTIME_DEFINITION = namedtuple('MSVCRuntime', [
'vc_runtime',
'vc_runtime_numeric',
diff --git a/SCons/Tool/MSCommon/MSVC/Exceptions.py b/SCons/Tool/MSCommon/MSVC/Exceptions.py
index 7a61ec5..015fede 100644
--- a/SCons/Tool/MSCommon/MSVC/Exceptions.py
+++ b/SCons/Tool/MSCommon/MSVC/Exceptions.py
@@ -28,10 +28,19 @@ Exceptions for Microsoft Visual C/C++.
class VisualCException(Exception):
pass
+class MSVCInternalError(VisualCException):
+ pass
+
class MSVCVersionNotFound(VisualCException):
pass
-class MSVCInternalError(VisualCException):
+class MSVCSDKVersionNotFound(VisualCException):
+ pass
+
+class MSVCToolsetVersionNotFound(VisualCException):
+ pass
+
+class MSVCSpectreLibsNotFound(VisualCException):
pass
class MSVCArgumentError(VisualCException):
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
index 6a4ce3e..33e13fd 100644
--- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -44,6 +44,9 @@ from . import WinSDK
from .Exceptions import (
MSVCInternalError,
+ MSVCSDKVersionNotFound,
+ MSVCToolsetVersionNotFound,
+ MSVCSpectreLibsNotFound,
MSVCArgumentError,
)
@@ -131,6 +134,18 @@ def msvc_force_default_arguments(force=True):
if CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS:
msvc_force_default_arguments(force=True)
+# UWP SDK 8.1 and SDK 10:
+#
+# https://stackoverflow.com/questions/46659238/build-windows-app-compatible-for-8-1-and-10
+# VS2019 - UWP (Except for Win10Mobile)
+# VS2017 - UWP
+# VS2015 - UWP, Win8.1 StoreApp, WP8/8.1 StoreApp
+# VS2013 - Win8/8.1 StoreApp, WP8/8.1 StoreApp
+
+# SPECTRE LIBS (msvc documentation):
+# "There are no versions of Spectre-mitigated libraries for Universal Windows (UWP) apps or
+# components. App-local deployment of such libraries isn't possible."
+
# MSVC batch file arguments:
#
# VS2022: UWP, SDK, TOOLSET, SPECTRE
@@ -159,7 +174,7 @@ VS2017 = Config.MSVS_VERSION_INTERNAL['2017']
VS2015 = Config.MSVS_VERSION_INTERNAL['2015']
MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [
- 'version', # fully qualified msvc version (e.g., '14.1Exp')
+ 'version', # full version (e.g., '14.1Exp', '14.32.31326')
'vs_def',
])
@@ -175,6 +190,18 @@ def _msvc_version(version):
return version_args
+def _toolset_version(version):
+
+ verstr = Util.get_msvc_version_prefix(version)
+ vs_def = Config.MSVC_VERSION_INTERNAL[verstr]
+
+ version_args = MSVC_VERSION_ARGS_DEFINITION(
+ version = version,
+ vs_def = vs_def,
+ )
+
+ return version_args
+
def _msvc_script_argument_uwp(env, msvc, arglist):
uwp_app = env['MSVC_UWP_APP']
@@ -252,12 +279,38 @@ def _msvc_script_argument_sdk_constraints(msvc, sdk_version):
err_msg = "MSVC_SDK_VERSION ({}) is not supported".format(repr(sdk_version))
return err_msg
-def _msvc_script_argument_sdk(env, msvc, platform_type, arglist):
+def _msvc_script_argument_sdk_platform_constraints(msvc, toolset, sdk_version, platform_def):
+
+ if sdk_version == '8.1' and platform_def.is_uwp:
+
+ vs_def = toolset.vs_def if toolset else msvc.vs_def
+
+ if vs_def.vc_buildtools_def.vc_version_numeric > VS2015.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: uwp/store SDK 8.1 msvc_version constraint: %s > %s VS2015',
+ repr(vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(VS2015.vc_buildtools_def.vc_version_numeric)
+ )
+ if toolset and toolset.vs_def != msvc.vs_def:
+ err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: toolset version {} > {} VS2015".format(
+ repr(sdk_version), repr(platform_def.vc_platform),
+ repr(toolset.version), repr(VS2015.vc_buildtools_def.vc_version)
+ )
+ else:
+ err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: MSVC_VERSION {} > {} VS2015".format(
+ repr(sdk_version), repr(platform_def.vc_platform),
+ repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version)
+ )
+ return err_msg
+
+ return None
+
+def _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist):
sdk_version = env['MSVC_SDK_VERSION']
debug(
'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, platform_type=%s',
- repr(msvc.version), repr(sdk_version), repr(platform_type)
+ repr(msvc.version), repr(sdk_version), repr(platform_def.vc_platform)
)
if not sdk_version:
@@ -267,12 +320,16 @@ def _msvc_script_argument_sdk(env, msvc, platform_type, arglist):
if err_msg:
raise MSVCArgumentError(err_msg)
- sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type)
+ sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_def)
if sdk_version not in sdk_list:
err_msg = "MSVC_SDK_VERSION {} not found for platform type {}".format(
- repr(sdk_version), repr(platform_type)
+ repr(sdk_version), repr(platform_def.vc_platform)
)
+ raise MSVCSDKVersionNotFound(err_msg)
+
+ err_msg = _msvc_script_argument_sdk_platform_constraints(msvc, toolset, sdk_version, platform_def)
+ if err_msg:
raise MSVCArgumentError(err_msg)
argpair = (SortOrder.SDK, sdk_version)
@@ -280,12 +337,12 @@ def _msvc_script_argument_sdk(env, msvc, platform_type, arglist):
return sdk_version
-def _msvc_script_default_sdk(env, msvc, platform_type, arglist):
+def _msvc_script_default_sdk(env, msvc, platform_def, arglist):
if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric:
return None
- sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type)
+ sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_def)
if not len(sdk_list):
return None
@@ -293,7 +350,7 @@ def _msvc_script_default_sdk(env, msvc, platform_type, arglist):
debug(
'MSVC_VERSION=%s, sdk_default=%s, platform_type=%s',
- repr(msvc.version), repr(sdk_default), repr(platform_type)
+ repr(msvc.version), repr(sdk_default), repr(platform_def.vc_platform)
)
argpair = (SortOrder.SDK, sdk_default)
@@ -597,8 +654,9 @@ def _msvc_script_argument_toolset(env, msvc, vc_dir, arglist):
err_msg = "MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}".format(
repr(toolset_version), repr(msvc.version)
)
- raise MSVCArgumentError(err_msg)
+ raise MSVCToolsetVersionNotFound(err_msg)
+ # toolset may not be installed for host/target
argpair = (SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_vcvars))
arglist.append(argpair)
@@ -644,16 +702,7 @@ def _user_script_argument_toolset(env, toolset_version, user_argstr):
raise MSVCArgumentError(err_msg)
-def _msvc_script_argument_spectre(env, msvc, arglist):
-
- spectre_libs = env['MSVC_SPECTRE_LIBS']
- debug('MSVC_VERSION=%s, MSVC_SPECTRE_LIBS=%s', repr(msvc.version), repr(spectre_libs))
-
- if not spectre_libs:
- return None
-
- if spectre_libs not in _ARGUMENT_BOOLEAN_TRUE:
- return None
+def _msvc_script_argument_spectre_constraints(msvc, toolset, spectre_libs, platform_def):
if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric:
debug(
@@ -664,11 +713,63 @@ def _msvc_script_argument_spectre(env, msvc, arglist):
err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
repr(spectre_libs), repr(msvc.version), repr(VS2017.vc_buildtools_def.vc_version)
)
+ return err_msg
+
+ if toolset:
+ if toolset.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: toolset version constraint: %s < %s VS2017',
+ repr(toolset.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(VS2017.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: toolset version {} < {} VS2017".format(
+ repr(spectre_libs), repr(toolset.version), repr(VS2017.vc_buildtools_def.vc_version)
+ )
+ return err_msg
+
+
+ if platform_def.is_uwp:
+ debug(
+ 'invalid: spectre_libs=%s and platform_type=%s',
+ repr(spectre_libs), repr(platform_def.vc_platform)
+ )
+ err_msg = "MSVC_SPECTRE_LIBS ({}) are not supported for platform type ({})".format(
+ repr(spectre_libs), repr(platform_def.vc_platform)
+ )
+ return err_msg
+
+ return None
+
+def _msvc_script_argument_spectre(env, msvc, vc_dir, toolset, platform_def, arglist):
+
+ spectre_libs = env['MSVC_SPECTRE_LIBS']
+ debug('MSVC_VERSION=%s, MSVC_SPECTRE_LIBS=%s', repr(msvc.version), repr(spectre_libs))
+
+ if not spectre_libs:
+ return None
+
+ if spectre_libs not in _ARGUMENT_BOOLEAN_TRUE:
+ return None
+
+ err_msg = _msvc_script_argument_spectre_constraints(msvc, toolset, spectre_libs, platform_def)
+ if err_msg:
raise MSVCArgumentError(err_msg)
+ if toolset:
+ spectre_dir = os.path.join(vc_dir, "Tools", "MSVC", toolset.version, "lib", "spectre")
+ if not os.path.exists(spectre_dir):
+ debug(
+ 'spectre libs: msvc_version=%s, toolset_version=%s, spectre_dir=%s',
+ repr(msvc.version), repr(toolset.version), repr(spectre_dir)
+ )
+ err_msg = "Spectre libraries not found for MSVC_VERSION {} toolset version {}".format(
+ repr(msvc.version), repr(toolset.version)
+ )
+ raise MSVCSpectreLibsNotFound(err_msg)
+
spectre_arg = 'spectre'
- # spectre libs may not be installed
+ # spectre libs may not be installed for host/target
argpair = (SortOrder.SPECTRE, '-vcvars_spectre_libs={}'.format(spectre_arg))
arglist.append(argpair)
@@ -723,66 +824,111 @@ def _msvc_script_argument_user(env, msvc, arglist):
return script_args
+def _msvc_process_construction_variables(env):
+
+ for cache_variable in [
+ _MSVC_FORCE_DEFAULT_TOOLSET,
+ _MSVC_FORCE_DEFAULT_SDK,
+ ]:
+ if cache_variable:
+ return True
+
+ for env_variable in [
+ 'MSVC_UWP_APP',
+ 'MSVC_TOOLSET_VERSION',
+ 'MSVC_SDK_VERSION',
+ 'MSVC_SPECTRE_LIBS',
+ ]:
+ if env.get(env_variable, None) != None:
+ return True
+
+ return False
+
def msvc_script_arguments(env, version, vc_dir, arg):
arglist = []
- msvc = _msvc_version(version)
-
if arg:
argpair = (SortOrder.ARCH, arg)
arglist.append(argpair)
+ msvc = _msvc_version(version)
+
if 'MSVC_SCRIPT_ARGS' in env:
user_argstr = _msvc_script_argument_user(env, msvc, arglist)
else:
user_argstr = None
- if 'MSVC_UWP_APP' in env:
- uwp = _msvc_script_argument_uwp(env, msvc, arglist)
- else:
- uwp = None
+ if _msvc_process_construction_variables(env):
- if user_argstr:
- _user_script_argument_uwp(env, uwp, user_argstr)
+ # MSVC_UWP_APP
- platform_type = 'uwp' if uwp else 'desktop'
+ if 'MSVC_UWP_APP' in env:
+ uwp = _msvc_script_argument_uwp(env, msvc, arglist)
+ else:
+ uwp = None
- if 'MSVC_SDK_VERSION' in env:
- sdk_version = _msvc_script_argument_sdk(env, msvc, platform_type, arglist)
- else:
- sdk_version = None
+ if user_argstr:
+ _user_script_argument_uwp(env, uwp, user_argstr)
- if user_argstr:
- user_sdk = _user_script_argument_sdk(env, sdk_version, user_argstr)
- else:
- user_sdk = None
+ is_uwp = True if uwp else False
+ platform_def = WinSDK.get_msvc_platform(is_uwp)
- if _MSVC_FORCE_DEFAULT_SDK:
- if not sdk_version and not user_sdk:
- sdk_version = _msvc_script_default_sdk(env, msvc, platform_type, arglist)
+ # MSVC_TOOLSET_VERSION
- if 'MSVC_TOOLSET_VERSION' in env:
- toolset_version = _msvc_script_argument_toolset(env, msvc, vc_dir, arglist)
- else:
- toolset_version = None
+ if 'MSVC_TOOLSET_VERSION' in env:
+ toolset_version = _msvc_script_argument_toolset(env, msvc, vc_dir, arglist)
+ else:
+ toolset_version = None
- if user_argstr:
- user_toolset = _user_script_argument_toolset(env, toolset_version, user_argstr)
- else:
- user_toolset = None
+ if user_argstr:
+ user_toolset = _user_script_argument_toolset(env, toolset_version, user_argstr)
+ else:
+ user_toolset = None
- if _MSVC_FORCE_DEFAULT_TOOLSET:
if not toolset_version and not user_toolset:
- toolset_version = _msvc_script_default_toolset(env, msvc, vc_dir, arglist)
-
- if 'MSVC_SPECTRE_LIBS' in env:
- spectre = _msvc_script_argument_spectre(env, msvc, arglist)
- else:
- spectre = None
-
- if user_argstr:
- _user_script_argument_spectre(env, spectre, user_argstr)
+ default_toolset = _msvc_script_default_toolset(env, msvc, vc_dir, arglist)
+ else:
+ default_toolset = None
+
+ if _MSVC_FORCE_DEFAULT_TOOLSET:
+ if default_toolset:
+ toolset_version = default_toolset
+
+ if user_toolset:
+ toolset = None
+ elif toolset_version:
+ toolset = _toolset_version(toolset_version)
+ elif default_toolset:
+ toolset = _toolset_version(default_toolset)
+ else:
+ toolset = None
+
+ # MSVC_SDK_VERSION
+
+ if 'MSVC_SDK_VERSION' in env:
+ sdk_version = _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist)
+ else:
+ sdk_version = None
+
+ if user_argstr:
+ user_sdk = _user_script_argument_sdk(env, sdk_version, user_argstr)
+ else:
+ user_sdk = None
+
+ if _MSVC_FORCE_DEFAULT_SDK:
+ if not sdk_version and not user_sdk:
+ sdk_version = _msvc_script_default_sdk(env, msvc, platform_def, arglist)
+
+ # MSVC_SPECTRE_LIBS
+
+ if 'MSVC_SPECTRE_LIBS' in env:
+ spectre = _msvc_script_argument_spectre(env, msvc, vc_dir, toolset, platform_def, arglist)
+ else:
+ spectre = None
+
+ if user_argstr:
+ _user_script_argument_spectre(env, spectre, user_argstr)
if arglist:
arglist.sort()
diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py
index ba70b24..ed87f9d 100644
--- a/SCons/Tool/MSCommon/MSVC/Util.py
+++ b/SCons/Tool/MSCommon/MSVC/Util.py
@@ -83,3 +83,23 @@ def get_version_prefix(version):
rval = ''
return rval
+re_msvc_version_prefix = re.compile(r'^(?P[1-9][0-9]?[.][0-9]).*')
+
+def get_msvc_version_prefix(version):
+ """Get the msvc version number prefix from a string.
+
+ Args:
+ version: str
+ version string
+
+ Returns:
+ str: the msvc version number prefix
+
+ """
+ m = re_msvc_version_prefix.match(version)
+ if m:
+ rval = m.group('version')
+ else:
+ rval = ''
+ return rval
+
diff --git a/SCons/Tool/MSCommon/MSVC/WinSDK.py b/SCons/Tool/MSCommon/MSVC/WinSDK.py
index 8338c27..42526c2 100644
--- a/SCons/Tool/MSCommon/MSVC/WinSDK.py
+++ b/SCons/Tool/MSCommon/MSVC/WinSDK.py
@@ -43,10 +43,13 @@ from . import Dispatcher
Dispatcher.register_modulename(__name__)
+_DESKTOP = Config.MSVC_PLATFORM_INTERNAL['Desktop']
+_UWP = Config.MSVC_PLATFORM_INTERNAL['UWP']
+
def _new_sdk_map():
sdk_map = {
- 'desktop': [],
- 'uwp': [],
+ _DESKTOP.vc_platform: [],
+ _UWP.vc_platform: [],
}
return sdk_map
@@ -84,20 +87,20 @@ def _sdk_10_layout(version):
if not os.path.exists(sdk_inc_path):
continue
- for platform_type, sdk_inc_file in [
- ('desktop', 'winsdkver.h'),
- ('uwp', 'windows.h'),
+ for vc_platform, sdk_inc_file in [
+ (_DESKTOP.vc_platform, 'winsdkver.h'),
+ (_UWP.vc_platform, 'windows.h'),
]:
if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)):
continue
- key = (version_nbr, platform_type)
+ key = (version_nbr, vc_platform)
if key in sdk_version_platform_seen:
continue
sdk_version_platform_seen.add(key)
- sdk_map[platform_type].append(version_nbr)
+ sdk_map[vc_platform].append(version_nbr)
for key, val in sdk_map.items():
val.sort(reverse=True)
@@ -128,20 +131,20 @@ def _sdk_81_layout(version):
if not os.path.exists(sdk_inc_path):
continue
- for platform_type, sdk_inc_file in [
- ('desktop', 'winsdkver.h'),
- ('uwp', 'windows.h'),
+ for vc_platform, sdk_inc_file in [
+ (_DESKTOP.vc_platform, 'winsdkver.h'),
+ (_UWP.vc_platform, 'windows.h'),
]:
if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)):
continue
- key = (version_nbr, platform_type)
+ key = (version_nbr, vc_platform)
if key in sdk_version_platform_seen:
continue
sdk_version_platform_seen.add(key)
- sdk_map[platform_type].append(version_nbr)
+ sdk_map[vc_platform].append(version_nbr)
for key, val in sdk_map.items():
val.sort(reverse=True)
@@ -218,9 +221,13 @@ def _sdk_map(version_list):
_sdk_cache[key] = sdk_map
return sdk_map
-def get_sdk_version_list(version_list, platform_type):
+def get_msvc_platform(is_uwp=False):
+ platform_def = _UWP if is_uwp else _DESKTOP
+ return platform_def
+
+def get_sdk_version_list(version_list, platform_def):
sdk_map = _sdk_map(version_list)
- sdk_list = sdk_map.get(platform_type, [])
+ sdk_list = sdk_map.get(platform_def.vc_platform, [])
return sdk_list
def get_msvc_sdk_version_list(msvc_version, msvc_uwp_app=False):
@@ -235,8 +242,8 @@ def get_msvc_sdk_version_list(msvc_version, msvc_uwp_app=False):
return sdk_versions
is_uwp = True if msvc_uwp_app in Config.BOOLEAN_SYMBOLS[True] else False
- platform_type = 'uwp' if is_uwp else 'desktop'
- sdk_list = get_sdk_version_list(vs_def.vc_sdk_versions, platform_type)
+ platform_def = get_msvc_platform(is_uwp)
+ sdk_list = get_sdk_version_list(vs_def.vc_sdk_versions, platform_def)
sdk_versions.extend(sdk_list)
debug('sdk_versions=%s', repr(sdk_versions))
diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml
index 11c6478..9297100 100644
--- a/SCons/Tool/msvc.xml
+++ b/SCons/Tool/msvc.xml
@@ -950,6 +950,13 @@ type (i.e., UWP or Desktop). The requeste
platform type components do not appear to be installed.
+
+The &cv-MSVC_SDK_VERSION; version is 8.1, the platform type is
+UWP, and the build tools selected are from Visual Studio 2017
+and later (i.e., &cv-link-MSVC_VERSION; must be '14.0' or &cv-link-MSVC_TOOLSET_VERSION;
+must be '14.0').
+
+
@@ -1209,6 +1216,12 @@ An exception is raised when any of the following conditions are satisfied:
and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+
+&cv-MSVC_SPECTRE_LIBS; is enabled and the platform type is UWP. There
+are no spectre-mitigated libraries for Universal Windows Platform (UWP) applications or
+components.
+
+
@@ -1240,8 +1253,8 @@ details.
-The existence of the spectre mitigations libraries is not verified when &cv-MSVC_SPECTRE_LIBS;
-is enabled which could result in build failures.
+The existence of the spectre libraries host architecture and target architecture folders are not
+verified when &cv-MSVC_SPECTRE_LIBS; is enabled which could result in build failures.
The burden is on the user to ensure the requisite libraries with spectre mitigations are installed.
diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen
index d5980ff..80d5b18 100644
--- a/doc/generated/variables.gen
+++ b/doc/generated/variables.gen
@@ -4994,6 +4994,13 @@ type (i.e., UWP or Desktop). The requeste
platform type components do not appear to be installed.
+
+The &cv-MSVC_SDK_VERSION; version is 8.1, the platform type is
+UWP, and the build tools selected are from Visual Studio 2017
+and later (i.e., &cv-link-MSVC_VERSION; must be '14.0' or &cv-link-MSVC_TOOLSET_VERSION;
+must be '14.0').
+
+
@@ -5084,6 +5091,12 @@ An exception is raised when any of the following conditions are satisfied:
and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+
+&cv-MSVC_SPECTRE_LIBS; is enabled and the platform type is UWP. There
+are no spectre-mitigated libraries for Universal Windows Platform (UWP) applications or
+components.
+
+
@@ -5115,8 +5128,8 @@ details.
-The existence of the spectre mitigations libraries is not verified when &cv-MSVC_SPECTRE_LIBS;
-is enabled which could result in build failures.
+The existence of the spectre libraries host architecture and target architecture folders are not
+verified when &cv-MSVC_SPECTRE_LIBS; is enabled which could result in build failures.
The burden is on the user to ensure the requisite libraries with spectre mitigations are installed.
--
cgit v0.12
From f500d2e6d12d50feec93517c077194adc9a31306 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Tue, 28 Jun 2022 17:26:56 -0400
Subject: Fix sider issue.
---
SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
index 33e13fd..556cae8 100644
--- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -839,7 +839,7 @@ def _msvc_process_construction_variables(env):
'MSVC_SDK_VERSION',
'MSVC_SPECTRE_LIBS',
]:
- if env.get(env_variable, None) != None:
+ if env.get(env_variable, None) is not None:
return True
return False
--
cgit v0.12
From 8dd2c436317301067f2637829572c36499e24318 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Wed, 29 Jun 2022 06:58:30 -0400
Subject: Use msvc version prefix instead of version prefix for internal
dictionary lookup.
---
SCons/Tool/MSCommon/MSVC/Config.py | 2 +-
SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 2 +-
SCons/Tool/MSCommon/MSVC/WinSDK.py | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py
index 3b71cd8..fa7dbc1 100644
--- a/SCons/Tool/MSCommon/MSVC/Config.py
+++ b/SCons/Tool/MSCommon/MSVC/Config.py
@@ -318,7 +318,7 @@ for policy_value, policy_symbol_list in [
def verify():
from .. import vc
for msvc_version in vc._VCVER:
- vc_version = Util.get_version_prefix(msvc_version)
+ vc_version = Util.get_msvc_version_prefix(msvc_version)
if vc_version in MSVC_VERSION_INTERNAL:
continue
err_msg = 'vc_version {} not in MSVC_VERSION_INTERNAL'.format(repr(vc_version))
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
index 556cae8..56a4676 100644
--- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -180,7 +180,7 @@ MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [
def _msvc_version(version):
- verstr = Util.get_version_prefix(version)
+ verstr = Util.get_msvc_version_prefix(version)
vs_def = Config.MSVC_VERSION_INTERNAL[verstr]
version_args = MSVC_VERSION_ARGS_DEFINITION(
diff --git a/SCons/Tool/MSCommon/MSVC/WinSDK.py b/SCons/Tool/MSCommon/MSVC/WinSDK.py
index 42526c2..b17f850 100644
--- a/SCons/Tool/MSCommon/MSVC/WinSDK.py
+++ b/SCons/Tool/MSCommon/MSVC/WinSDK.py
@@ -235,7 +235,7 @@ def get_msvc_sdk_version_list(msvc_version, msvc_uwp_app=False):
sdk_versions = []
- verstr = Util.get_version_prefix(msvc_version)
+ verstr = Util.get_msvc_version_prefix(msvc_version)
vs_def = Config.MSVC_VERSION_EXTERNAL.get(verstr, None)
if not vs_def:
debug('vs_def is not defined')
--
cgit v0.12
From a63c1cfdeffbcf7e9214702df4f93e5f015ead10 Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Wed, 29 Jun 2022 15:20:44 -0400
Subject: Add 14.0 toolset registry check as done in msvc vsvars140.bat.
---
SCons/Tool/MSCommon/MSVC/Registry.py | 3 +++
SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 19 ++++++++++++++++++-
SCons/Tool/MSCommon/vc.py | 1 -
3 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/SCons/Tool/MSCommon/MSVC/Registry.py b/SCons/Tool/MSCommon/MSVC/Registry.py
index 492f3d0..9ffa01e 100644
--- a/SCons/Tool/MSCommon/MSVC/Registry.py
+++ b/SCons/Tool/MSCommon/MSVC/Registry.py
@@ -107,3 +107,6 @@ def windows_kit_query_paths(version):
q = windows_kits(version)
return microsoft_query_paths(q)
+def vstudio_sxs_vc7(version):
+ return '\\'.join([r'VisualStudio\SxS\VC7', version])
+
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
index 56a4676..4db478a 100644
--- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -40,6 +40,7 @@ from ..common import (
from . import Util
from . import Config
+from . import Registry
from . import WinSDK
from .Exceptions import (
@@ -491,13 +492,16 @@ def _msvc_read_toolset_default(msvc, vc_dir):
_toolset_version_cache = {}
_toolset_default_cache = {}
+_toolset_have140_cache = None
def _reset_toolset_cache():
global _toolset_version_cache
global _toolset_default_cache
+ global _toolset_have140_cache
debug('reset: toolset cache')
_toolset_version_cache = {}
_toolset_default_cache = {}
+ _toolset_have140_cache = None
def _msvc_version_toolsets(msvc, vc_dir):
@@ -519,10 +523,23 @@ def _msvc_default_toolset(msvc, vc_dir):
return toolset_default
+def _msvc_have140_toolset():
+ global _toolset_have140_cache
+
+ if _toolset_have140_cache is None:
+ suffix = Registry.vstudio_sxs_vc7('14.0')
+ vcinstalldirs = [record[0] for record in Registry.microsoft_query_paths(suffix)]
+ debug('vc140 toolset: paths=%s', repr(vcinstalldirs))
+ _toolset_have140_cache = True if vcinstalldirs else False
+
+ return _toolset_have140_cache
+
def _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version):
if toolset_version == '14.0':
- return toolset_version
+ if _msvc_have140_toolset():
+ return toolset_version
+ return None
toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir)
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index 7c65879..502a71f 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -1300,7 +1300,6 @@ def msvc_setup_env_user(env=None):
# Intent is to use msvc tools:
# MSVC_VERSION: defined and evaluates True
# MSVS_VERSION: defined and evaluates True
- # MSVC_TOOLSET_VERSION: defined and evaluates True
# MSVC_USE_SCRIPT: defined and (is string or evaluates False)
# MSVC_USE_SETTINGS: defined and is not None
--
cgit v0.12
From fa7f870b7768fae625396c0a0dfface959fee65f Mon Sep 17 00:00:00 2001
From: Joseph Brill <48932340+jcbrill@users.noreply.github.com>
Date: Wed, 29 Jun 2022 20:44:23 -0400
Subject: Fix Util.py docstring for listdir_dirs. Minor update to reading sxs
toolsets and toolset folders.
---
SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 64 ++++++++++++++---------------
SCons/Tool/MSCommon/MSVC/Util.py | 26 +++++++-----
2 files changed, 48 insertions(+), 42 deletions(-)
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
index 4db478a..14c8c6c 100644
--- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -398,16 +398,16 @@ def _msvc_read_toolset_file(msvc, filename):
debug('IndexError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename))
return toolset_version
-def _msvc_sxs_toolset_folder(msvc, sxs_folder):
+def _msvc_sxs_toolset_folder(msvc, sxs_version):
- if re_toolset_sxs.match(sxs_folder):
- return sxs_folder
+ if re_toolset_sxs.match(sxs_version):
+ return sxs_version
- key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_folder)
+ key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_version)
if key in _msvc_sxs_bugfix_folder:
- return sxs_folder
+ return sxs_version
- debug('sxs folder: ignore version=%s', repr(sxs_folder))
+ debug('sxs folder: ignore version=%s', repr(sxs_version))
return None
def _msvc_sxs_toolset_version(msvc, sxs_toolset):
@@ -429,35 +429,35 @@ def _msvc_read_toolset_folders(msvc, vc_dir):
toolsets_full = []
build_dir = os.path.join(vc_dir, "Auxiliary", "Build")
- sxs_folders = [f.name for f in os.scandir(build_dir) if f.is_dir()]
- for sxs_folder in sxs_folders:
- sxs_toolset = _msvc_sxs_toolset_folder(msvc, sxs_folder)
- if not sxs_toolset:
- continue
- filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset)
- filepath = os.path.join(build_dir, sxs_toolset, filename)
- debug('sxs toolset: check file=%s', repr(filepath))
- if os.path.exists(filepath):
- toolset_version = _msvc_read_toolset_file(msvc, filepath)
- if not toolset_version:
+ if os.path.exists(build_dir):
+ for sxs_version, sxs_path in Util.listdir_dirs(build_dir):
+ sxs_version = _msvc_sxs_toolset_folder(msvc, sxs_version)
+ if not sxs_version:
continue
- toolsets_sxs[sxs_toolset] = toolset_version
- debug(
- 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s',
- repr(msvc.version), repr(sxs_toolset), toolset_version
- )
+ filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_version)
+ filepath = os.path.join(sxs_path, filename)
+ debug('sxs toolset: check file=%s', repr(filepath))
+ if os.path.exists(filepath):
+ toolset_version = _msvc_read_toolset_file(msvc, filepath)
+ if not toolset_version:
+ continue
+ toolsets_sxs[sxs_version] = toolset_version
+ debug(
+ 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s',
+ repr(msvc.version), repr(sxs_version), repr(toolset_version)
+ )
toolset_dir = os.path.join(vc_dir, "Tools", "MSVC")
- toolsets = [f.name for f in os.scandir(toolset_dir) if f.is_dir()]
- for toolset_version in toolsets:
- binpath = os.path.join(toolset_dir, toolset_version, "bin")
- debug('toolset: check binpath=%s', repr(binpath))
- if os.path.exists(binpath):
- toolsets_full.append(toolset_version)
- debug(
- 'toolset: msvc_version=%s, toolset_version=%s',
- repr(msvc.version), repr(toolset_version)
- )
+ if os.path.exists(toolset_dir):
+ for toolset_version, toolset_path in Util.listdir_dirs(toolset_dir):
+ binpath = os.path.join(toolset_path, "bin")
+ debug('toolset: check binpath=%s', repr(binpath))
+ if os.path.exists(binpath):
+ toolsets_full.append(toolset_version)
+ debug(
+ 'toolset: msvc_version=%s, toolset_version=%s',
+ repr(msvc.version), repr(toolset_version)
+ )
toolsets_full.sort(reverse=True)
debug('msvc_version=%s, toolsets=%s', repr(msvc.version), repr(toolsets_full))
diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py
index ed87f9d..f1983ba 100644
--- a/SCons/Tool/MSCommon/MSVC/Util.py
+++ b/SCons/Tool/MSCommon/MSVC/Util.py
@@ -29,14 +29,17 @@ import os
import re
def listdir_dirs(p):
- """Get a list of qualified subdirectory paths from a windows path.
+ """
+ Return a list of tuples for each subdirectory of the given directory path.
+ Each tuple is comprised of the subdirectory name and the qualified subdirectory path.
+ Assumes the given directory path exists and is a directory.
Args:
p: str
- windows path
+ directory path
Returns:
- List[str]: list of qualified subdirectory paths
+ list[tuple[str,str]]: a list of tuples
"""
dirs = []
@@ -47,14 +50,15 @@ def listdir_dirs(p):
return dirs
def process_path(p):
- """Normalize a windows path.
+ """
+ Normalize a system path
Args:
p: str
- windows path
+ system path
Returns:
- str: normalized windows path
+ str: normalized system path
"""
if p:
@@ -66,11 +70,12 @@ def process_path(p):
re_version_prefix = re.compile(r'^(?P[0-9.]+).*')
def get_version_prefix(version):
- """Get the version number prefix from a string.
+ """
+ Get the version number prefix from a string.
Args:
version: str
- version string
+ version specification
Returns:
str: the version number prefix
@@ -86,11 +91,12 @@ def get_version_prefix(version):
re_msvc_version_prefix = re.compile(r'^(?P[1-9][0-9]?[.][0-9]).*')
def get_msvc_version_prefix(version):
- """Get the msvc version number prefix from a string.
+ """
+ Get the msvc version number prefix from a string.
Args:
version: str
- version string
+ version specification
Returns:
str: the msvc version number prefix
--
cgit v0.12
From 3fc497c7775f572a32056a1c6b52ee4592c4bced Mon Sep 17 00:00:00 2001
From: Mats Wichmann
Date: Sun, 26 Jun 2022 13:07:57 -0600
Subject: Improvements to lex and yacc tools
The mocked tools mylex.py and myyacc.py now understand the file-generation
options, and generate a dummy file with predictable contents, for
checking. This allows more testing of the path through the SCons support
for these two without needing live commands.
New tests added which invoke the file-generation options, and make
sure the extra files are created, and that SCons detects and tracks
the added targets. Work is done in a subdirectory, which exposes some
existing known inconsistent behavior (the regular generated file goes
in the subdir per the LEXCOM and YACCOM generated line, while the ones
generated from commandline options go in the topdir) - but we're going
to allow that behavior to continue for backwards compat.
Same fix applied to yacc tool that PR #4168 did for lex - do subst_list()
instead of subst() to preserve spaces in paths. That fix left the lex
tool unable to pass the new test, as it could not see the individual
arguments in the FLAGS variable, which was solved by indexing into the
subst'd list so we can iterate over the args again.
Test and tool cleanup; add DefaultEnvironment calls, etc.
Note this mentions, but does not address the problem described in issue 4154.
Signed-off-by: Mats Wichmann
---
CHANGES.txt | 2 +
SCons/Tool/__init__.py | 8 +--
SCons/Tool/lex.py | 58 ++++++++++++---------
SCons/Tool/lex.xml | 35 ++++++++++++-
SCons/Tool/yacc.py | 73 +++++++++++++++-----------
SCons/Tool/yacc.xml | 70 ++++++++++++++++++++++---
doc/scons.mod | 1 -
test/LEX/FLEXFLAGS.py | 93 ++++++++++++++++++++++++++++++++++
test/LEX/LEX.py | 1 +
test/LEX/LEXCOM.py | 20 ++++----
test/LEX/LEXCOMSTR.py | 22 ++++----
test/LEX/LEXFLAGS.py | 10 ++--
test/LEX/lex_headerfile.py | 4 ++
test/LEX/live.py | 28 ++++------
test/LEX/live_mingw.py | 4 +-
test/LEX/no_lex.py | 12 ++---
test/YACC/BISONFLAGS.py | 92 +++++++++++++++++++++++++++++++++
test/YACC/YACC-fixture/myyacc.py | 12 +++--
test/YACC/YACC.py | 36 +++++++------
test/YACC/YACCCOM.py | 20 ++++----
test/YACC/YACCCOMSTR.py | 22 ++++----
test/YACC/YACCFLAGS-fixture/myyacc.py | 78 ++++++++++++++++++++++------
test/YACC/YACCFLAGS.py | 31 ++++++------
test/YACC/YACCHFILESUFFIX.py | 33 ++++++------
test/YACC/YACCHXXFILESUFFIX.py | 33 ++++++------
test/YACC/YACCVCGFILESUFFIX.py | 33 ++++++------
test/YACC/live-check-output-cleaned.py | 13 +++--
test/YACC/live.py | 21 ++++----
test/fixture/mylex.py | 31 +++++++++++-
testing/framework/TestSCons.py | 33 ++++++++----
30 files changed, 668 insertions(+), 261 deletions(-)
create mode 100644 test/LEX/FLEXFLAGS.py
create mode 100644 test/YACC/BISONFLAGS.py
diff --git a/CHANGES.txt b/CHANGES.txt
index bea5838..6188863 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -202,6 +202,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
but was not applied to other dialects, and e2e tests explicitly checked
that FORTRANFLAGS did not propagate outside the FORTRAN dialect,
so the conclusion is that behavior is intentional (issue #2257)
+ - Improvements to lex and yacc tools: better documentation of
+ extra-file options, add test for extra-file behavior.
From Zhichang Yu:
- Added MSVC_USE_SCRIPT_ARGS variable to pass arguments to MSVC_USE_SCRIPT.
diff --git a/SCons/Tool/__init__.py b/SCons/Tool/__init__.py
index 8e4a22d..afc579f 100644
--- a/SCons/Tool/__init__.py
+++ b/SCons/Tool/__init__.py
@@ -36,6 +36,7 @@ tool specifications.
import sys
import os
import importlib.util
+from typing import Optional
import SCons.Builder
import SCons.Errors
@@ -829,7 +830,7 @@ def tool_list(platform, env):
return [x for x in tools if x]
-def find_program_path(env, key_program, default_paths=None, add_path=False) -> str:
+def find_program_path(env, key_program, default_paths=None, add_path=False) -> Optional[str]:
"""
Find the location of a tool using various means.
@@ -849,6 +850,8 @@ def find_program_path(env, key_program, default_paths=None, add_path=False) -> s
# Then in the OS path
path = SCons.Util.WhereIs(key_program)
if path:
+ if add_path:
+ env.AppendENVPath('PATH', os.path.dirname(path))
return path
# Finally, add the defaults and check again.
@@ -864,8 +867,7 @@ def find_program_path(env, key_program, default_paths=None, add_path=False) -> s
# leave that to the caller, unless add_path is true.
env['ENV']['PATH'] = save_path
if path and add_path:
- bin_dir = os.path.dirname(path)
- env.AppendENVPath('PATH', bin_dir)
+ env.AppendENVPath('PATH', os.path.dirname(path))
return path
diff --git a/SCons/Tool/lex.py b/SCons/Tool/lex.py
index 96f9bcb..262fe25 100644
--- a/SCons/Tool/lex.py
+++ b/SCons/Tool/lex.py
@@ -23,6 +23,9 @@
"""Tool-specific initialization for lex.
+This tool should support multiple lex implementations,
+but is in actuality biased towards GNU Flex.
+
There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.
@@ -30,14 +33,17 @@ selection method.
import os.path
import sys
+from typing import Optional
import SCons.Action
import SCons.Tool
-import SCons.Util
import SCons.Warnings
from SCons.Platform.mingw import MINGW_DEFAULT_PATHS
from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS
from SCons.Platform.win32 import CHOCO_DEFAULT_PATH
+from SCons.Util import CLVar, to_String
+
+DEFAULT_PATHS = CHOCO_DEFAULT_PATH + MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS
LexAction = SCons.Action.Action("$LEXCOM", "$LEXCOMSTR")
@@ -47,20 +53,25 @@ else:
BINS = ["flex", "lex"]
-def lexEmitter(target, source, env):
- sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0]))
+def lexEmitter(target, source, env) -> tuple:
+ """Adds extra files generated by lex program to target list."""
+ sourceBase, sourceExt = os.path.splitext(to_String(source[0]))
if sourceExt == ".lm": # If using Objective-C
target = [sourceBase + ".m"] # the extension is ".m".
- # This emitter essentially tries to add to the target all extra
- # files generated by flex.
-
- # Different options that are used to trigger the creation of extra files.
+ # With --header-file and ----tables-file, the file to write is defined
+ # by the option argument. Extract this and include in the list of targets.
+ # NOTE: a filename passed to the command this way is not modified by SCons,
+ # and so will be interpreted relative to the project top directory at
+ # execution time, while the name added to the target list will be
+ # interpreted relative to the SConscript directory - a possibile mismatch.
+ #
+ # These are GNU flex-only options.
+ # TODO: recognize --outfile also?
file_gen_options = ["--header-file=", "--tables-file="]
-
lexflags = env.subst_list("$LEXFLAGS", target=target, source=source)
- for option in SCons.Util.CLVar(lexflags):
+ for option in lexflags[0]:
for fileGenOption in file_gen_options:
l = len(fileGenOption)
if option[:l] == fileGenOption:
@@ -68,28 +79,29 @@ def lexEmitter(target, source, env):
# file name to the target list.
file_name = option[l:].strip()
target.append(file_name)
+
return target, source
-def get_lex_path(env, append_paths=False):
+def get_lex_path(env, append_paths=False) -> Optional[str]:
"""
- Find the path to the lex tool, searching several possible names
+ Returns the path to the lex tool, searching several possible names.
- Only called in the Windows case, so the default_path
- can be Windows-specific
+ Only called in the Windows case, so the `default_path` argument to
+ :func:`find_program_path` can be Windows-specific.
- :param env: current construction environment
- :param append_paths: if set, add the path to the tool to PATH
- :return: path to lex tool, if found
+ Args:
+ env: current construction environment
+ append_paths: if set, add the path to the tool to PATH
"""
for prog in BINS:
bin_path = SCons.Tool.find_program_path(
env,
prog,
- default_paths=CHOCO_DEFAULT_PATH + MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS )
+ default_paths=DEFAULT_PATHS,
+ add_path=append_paths,
+ )
if bin_path:
- if append_paths:
- env.AppendENVPath('PATH', os.path.dirname(bin_path))
return bin_path
SCons.Warnings.warn(
@@ -98,7 +110,7 @@ def get_lex_path(env, append_paths=False):
)
-def generate(env):
+def generate(env) -> None:
"""Add Builders and construction variables for lex to an Environment."""
c_file, cxx_file = SCons.Tool.createCFileBuilders(env)
@@ -117,21 +129,21 @@ def generate(env):
cxx_file.add_action(".ll", LexAction)
cxx_file.add_emitter(".ll", lexEmitter)
- env["LEXFLAGS"] = SCons.Util.CLVar("")
+ env["LEXFLAGS"] = CLVar("")
if sys.platform == 'win32':
# ignore the return - we do not need the full path here
_ = get_lex_path(env, append_paths=True)
env["LEX"] = env.Detect(BINS)
if not env.get("LEXUNISTD"):
- env["LEXUNISTD"] = SCons.Util.CLVar("")
+ env["LEXUNISTD"] = CLVar("")
env["LEXCOM"] = "$LEX $LEXUNISTD $LEXFLAGS -t $SOURCES > $TARGET"
else:
env["LEX"] = env.Detect(BINS)
env["LEXCOM"] = "$LEX $LEXFLAGS -t $SOURCES > $TARGET"
-def exists(env):
+def exists(env) -> Optional[str]:
if sys.platform == 'win32':
return get_lex_path(env)
else:
diff --git a/SCons/Tool/lex.xml b/SCons/Tool/lex.xml
index 5afb754..8622ced 100644
--- a/SCons/Tool/lex.xml
+++ b/SCons/Tool/lex.xml
@@ -1,6 +1,28 @@