diff --git a/CHANGES.txt b/CHANGES.txt index bfd3eb18a9..40cfeb9a82 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,6 +15,13 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From John Doe: - Whatever John Doe did. + From Cornelii Sandberg: + - Fix Variables() not reading a saved-variables file (e.g. custom.py) + from the source directory when building in a separate variant + directory. The file is now resolved through the File() node + infrastructure, so it is found in the source directory or a + repository (issue #816). + From Joseph Brill: - Add possible build failure when targeting 32-bit arm using Visual Studio 2022 with Windows SDK version 10.0.26100.0 or later diff --git a/SCons/Variables/VariablesTests.py b/SCons/Variables/VariablesTests.py index bca6047efc..3b647a9ab1 100644 --- a/SCons/Variables/VariablesTests.py +++ b/SCons/Variables/VariablesTests.py @@ -27,6 +27,7 @@ import SCons.Variables import SCons.Subst +import SCons.Node.FS import SCons.Warnings from SCons.Util import cmp from SCons.Variables import * @@ -43,6 +44,11 @@ def __getitem__(self, key): return self.dict[key] def __contains__(self, key) -> bool: return key in self.dict + def File(self, name): + # Real Environments resolve files through the File() node + # infrastructure; provide the same here so Variables.Update() can + # locate saved-variables files (issue #816). + return SCons.Node.FS.get_default_fs().File(name) def check(key, value, env) -> None: diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index dea36ddd0b..8bf9b000a9 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -271,18 +271,25 @@ def Update(self, env, args: dict | None = None) -> None: # next set the values specified in any saved-variables script(s) for filename in self.files: - # TODO: issue #816 use Node to access saved-variables file? - if os.path.exists(filename): + # Resolve the saved-variables file through the File() node so it + # is found in the source directory (or a repository) as well as + # the build directory, e.g. when using a variant dir (issue #816). + node = env.File(filename) + if not node.rexists(): + node = node.srcnode() + if node.rexists(): + # rfile() resolves to the actual on-disk file, which may live + # in a repository rather than the local (build) directory. + rfile = node.rfile() # issue #4645: don't exec directly into values, # so we can iterate through for unknown variables. temp_values = {} - dirname = os.path.split(os.path.abspath(filename))[0] + dirname = os.path.split(rfile.get_abspath())[0] if dirname: sys.path.insert(0, dirname) try: temp_values['__name__'] = filename - with open(filename) as f: - contents = f.read() + contents = rfile.get_text_contents() exec(contents, {}, temp_values) finally: if dirname: diff --git a/test/Variables/source-dir.py b/test/Variables/source-dir.py new file mode 100644 index 0000000000..e1669f3c69 --- /dev/null +++ b/test/Variables/source-dir.py @@ -0,0 +1,62 @@ +#!/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 that a Variables() saved-variables file (e.g. custom.py) is read from +the source tree when it is not present in the build directory, i.e. when +building against a separate source directory via Repository() (issue #816). +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('repository', 'work') + +opts = "-Y " + test.workpath('repository') + +# The saved-variables file lives only in the source tree (the repository), +# not in the 'work' directory where SCons is actually invoked. +test.write(['repository', 'custom.py'], """\ +MY_VARIABLE = 'from_source_tree' +""") + +test.write(['repository', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) +vars = Variables('custom.py') +vars.Add('MY_VARIABLE', 'a test variable', 'default_value') +env = Environment(variables=vars, tools=[]) +print("MY_VARIABLE =", env['MY_VARIABLE']) +""") + +# Before the issue #816 fix, custom.py was looked up relative to the build +# directory only, so it was not found here and the default value was used. +test.run(chdir='work', options=opts, arguments='.') + +expect = "MY_VARIABLE = from_source_tree" +if expect not in test.stdout(): + test.fail_test() + +test.pass_test()