diff --git a/CHANGES.txt b/CHANGES.txt index 62e9e756c..717af2226 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -30,6 +30,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From William Deegan: - Fix SCons Docbook schema to work with lxml > 5 - Update pyproject.toml to support Python 3.14 and remove restrictions on lxml version install + - MSVS: Fix msvs project file creation to work with scons-local packages, and with windows scoop.sh + package manager installed SCons. (Based on work by Joseph Brill and Marcel Admiraal) From Mats Wichmann: - Introduce some unit tests for the file locking utility routines diff --git a/RELEASE.txt b/RELEASE.txt index 6760b306d..98ce7c964 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -48,6 +48,9 @@ IMPROVEMENTS an override. Also, if override dict is empty, don't even call the Override factory function. +- MSVS: Fix msvs project file creation to work with scons-local packages, and with windows scoop.sh + package manager installed SCons. (Based on work by Joseph Brill and Marcel Admiraal) + - Test runner reworked to use Python logging; the portion of the test suite which tests the runner was adjusted to match. diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index 04a8ec81f..f4f19bfef 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -1478,11 +1478,14 @@ def lookupmodule(self, filename: str) -> str | None: _main(parser) -def main() -> None: +def main(script_path) -> None: global OptionsParser global exit_status global first_command_start global ENABLE_JSON + global SCONS_SCRIPT_PATH + + SCONS_SCRIPT_PATH = script_path # Check up front for a Python version we do not support. We # delay the check for deprecated Python versions until later, diff --git a/SCons/Tool/msvs.py b/SCons/Tool/msvs.py index b35e26780..22ab3fd3a 100644 --- a/SCons/Tool/msvs.py +++ b/SCons/Tool/msvs.py @@ -53,6 +53,19 @@ tool_name = 'msvs' + +# The string for the Python executable we tell the Project file to use +# is either sys.executable or, if an external PYTHON_ROOT environment +# variable exists, $(PYTHON)ROOT\\python.exe (generalized a little to +# pluck the actual executable name from sys.executable). +try: + python_root = os.environ['PYTHON_ROOT'] +except KeyError: + python_executable = sys.executable +else: + python_executable = os.path.join('$$(PYTHON_ROOT)', + os.path.split(sys.executable)[1]) + ############################################################################## # Below here are the classes and functions for generation of # DSP/DSW/SLN/VCPROJ files. @@ -152,40 +165,30 @@ def msvs_parse_version(s): num, suite = version_re.match(s).groups() return float(num), suite -# This is how we re-invoke SCons from inside MSVS Project files. -# The problem is that we might have been invoked as either scons.bat -# or scons.py. If we were invoked directly as scons.py, then we could -# use sys.argv[0] to find the SCons "executable," but that doesn't work -# if we were invoked as scons.bat, which uses "python -c" to execute -# things and ends up with "-c" as sys.argv[0]. Consequently, we have -# the MSVS Project file invoke SCons the same way that scons.bat does, -# which works regardless of how we were invoked. -def getExecScriptMain(env, xml=None): - if 'SCONS_HOME' not in env: - env['SCONS_HOME'] = os.environ.get('SCONS_HOME') - scons_home = env.get('SCONS_HOME') - if not scons_home and 'SCONS_LIB_DIR' in os.environ: - scons_home = os.environ['SCONS_LIB_DIR'] - if scons_home: - exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % scons_home - else: - version = SCons.__version__ - exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%(version)s'), join(sys.prefix, 'scons-%(version)s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % locals() + +def get_msvs_scons(env, xml=None): + """ + This is how we re-invoke SCons from inside MSVS Project files. + + Simplifying old logic. + Now there are two paths: + 1 - MSVS_SCONS is set in env (or os.environ) us that scons.py + 2 - otherwise we use the current running scons.py which is + available in SCons.Script.Main.SCONS_SCRIPT_PATH + """ + if 'MSVS_SCONS' not in env: + env['MSVS_SCONS'] = os.environ.get('MSVS_SCONS') + scons_script_path = env.get('MSVS_SCONS') + if not scons_script_path: + scons_script_path = SCons.Script.Main.SCONS_SCRIPT_PATH + + exec_script_main = f'"{python_executable}" "{scons_script_path}"' + if xml: exec_script_main = xmlify(exec_script_main) return exec_script_main -# The string for the Python executable we tell the Project file to use -# is either sys.executable or, if an external PYTHON_ROOT environment -# variable exists, $(PYTHON)ROOT\\python.exe (generalized a little to -# pluck the actual executable name from sys.executable). -try: - python_root = os.environ['PYTHON_ROOT'] -except KeyError: - python_executable = sys.executable -else: - python_executable = os.path.join('$$(PYTHON_ROOT)', - os.path.split(sys.executable)[1]) + class Config: pass @@ -2147,7 +2150,7 @@ def generate(env) -> None: # MSVSSCONSFLAGS. This helps support consumers who use wrapper scripts to # invoke scons. if 'MSVSSCONS' not in env: - env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, getExecScriptMain(env)) + env['MSVSSCONS'] = get_msvs_scons(env) if 'MSVSSCONSFLAGS' not in env: env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.get_abspath()}" -f ${MSVSSCONSCRIPT.name}' diff --git a/scripts/scons.py b/scripts/scons.py index a6a113f71..075338f5a 100755 --- a/scripts/scons.py +++ b/scripts/scons.py @@ -94,4 +94,4 @@ # this does all the work, and calls sys.exit # with the proper exit status when done. - SCons.Script.main() + SCons.Script.main(os.path.abspath(__file__)) diff --git a/test/MSVS/common-prefix.py b/test/MSVS/common-prefix.py index e8b47d19f..5bc603270 100644 --- a/test/MSVS/common-prefix.py +++ b/test/MSVS/common-prefix.py @@ -64,9 +64,9 @@ \t\t\t> \t\t\t \t\t\t" -c "" -C "" -f SConstruct "Test.exe"" -# PROP BASE Rebuild_Opt "-c && echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" +# PROP BASE Cmd_Line "echo Starting SCons && "" "" -C "" -f SConstruct "Test.exe"" +# PROP BASE Rebuild_Opt "-c && echo Starting SCons && "" "" -C "" -f SConstruct "Test.exe"" # PROP BASE Target_File "Test.exe" # PROP BASE Bsc_Name "" # PROP BASE Target_Dir "" @@ -101,8 +101,8 @@ # PROP Use_Debug_Libraries 0 # PROP Output_Dir "" # PROP Intermediate_Dir "" -# PROP Cmd_Line "echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" -# PROP Rebuild_Opt "-c && echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" +# PROP Cmd_Line "echo Starting SCons && "" "" -C "" -f SConstruct "Test.exe"" +# PROP Rebuild_Opt "-c && echo Starting SCons && "" "" -C "" -f SConstruct "Test.exe"" # PROP Target_File "Test.exe" # PROP Bsc_Name "" # PROP Target_Dir "" @@ -263,9 +263,9 @@ \t\t\tATLMinimizesCRunTimeLibraryUsage="FALSE"> \t\t\t \t\t \t @@ -388,9 +388,9 @@ \t\t\tATLMinimizesCRunTimeLibraryUsage="FALSE"> \t\t\t \t\t \t @@ -513,9 +513,9 @@ \t\t\t> \t\t\t \t \t<_ProjectFileVersion>10.0.30319.1 -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "%(PROJECT_BASENAME)s.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "%(PROJECT_BASENAME)s.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "%(PROJECT_BASENAME)s.exe" +\t\techo Starting SCons && "" "" -C "" -f SConstruct "%(PROJECT_BASENAME)s.exe" +\t\techo Starting SCons && "" "" -C "" -f SConstruct "%(PROJECT_BASENAME)s.exe" +\t\techo Starting SCons && "" "" -C "" -f SConstruct -c "%(PROJECT_BASENAME)s.exe" \t\t%(PROJECT_BASENAME)s.exe \t\tDEF1;DEF2;DEF3=1234 \t\t%(INCLUDE_DIRS)s @@ -899,17 +899,19 @@ def msvs_substitute( if project_guid is None: project_guid = PROJECT_GUID - if 'SCONS_LIB_DIR' in os.environ: - exec_script_main = f"from os.path import join; import sys; sys.path = [ r'{os.environ['SCONS_LIB_DIR']}' ] + sys.path; import SCons.Script; SCons.Script.main()" + if 'MSVS_SCONS' in os.environ: + exec_script_main = f'{os.environ['MSVS_SCONS']}' else: - exec_script_main = f"from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-{self.scons_version}'), join(sys.prefix, 'scons-{self.scons_version}'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" + exec_script_main = f'{self.program}' + + exec_script_main_xml = exec_script_main.replace("'", "'") result = input.replace(r'', workpath) result = result.replace(r'', python) result = result.replace(r'', sconscript) - result = result.replace(r'', exec_script_main) - result = result.replace(r'', exec_script_main_xml) + result = result.replace(r'', exec_script_main) + result = result.replace(r'', exec_script_main_xml) result = result.replace(r'', project_guid) result = result.replace('\n', vcproj_sccinfo) result = result.replace('\n', sln_sccinfo) @@ -1176,17 +1178,18 @@ def msvs_substitute_projects( if solution_guid_2 is None: solution_guid_2 = SOLUTION_GUID_2 - if 'SCONS_LIB_DIR' in os.environ: - exec_script_main = f"from os.path import join; import sys; sys.path = [ r'{os.environ['SCONS_LIB_DIR']}' ] + sys.path; import SCons.Script; SCons.Script.main()" + if 'MSVS_SCONS' in os.environ: + exec_script_main = f'{os.environ['MSVS_SCONS']}' else: - exec_script_main = f"from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-{self.scons_version}'), join(sys.prefix, 'scons-{self.scons_version}'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" + exec_script_main = f'{self.program}' + exec_script_main_xml = exec_script_main.replace("'", "'") result = input.replace(r'', workpath) result = result.replace(r'', python) result = result.replace(r'', sconscript) - result = result.replace(r'', exec_script_main) - result = result.replace(r'', exec_script_main_xml) + result = result.replace(r'', exec_script_main) + result = result.replace(r'', exec_script_main_xml) result = result.replace(r'', project_guid_1) result = result.replace(r'', project_guid_2) result = result.replace(r'', solution_guid_1)