Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Substitution of special attributes of special variables like $SOURCE does not work in builder calls #4660

Open
mwichmann opened this issue Dec 8, 2024 · 3 comments
Labels
subst Problems with quoting, substitution

Comments

@mwichmann
Copy link
Collaborator

mwichmann commented Dec 8, 2024

This topic was previously raised in #2905, which was closed in 2018 without apparently having been really resolved. This issue is created because of a fresh mention.

The User Guide claims that you can do substitutions on SOURCE and TARGET in sources and targets, in the chapter about the Command builder:

https://scons.org/doc/production/HTML/scons-user.html#chap-builders-commands

Specifically:

Note that $SOURCE and $TARGET are expanded in the source and target as well, so you can write:

env.Command('${SOURCE.basename}.out', 'foo.in', build)

Besides that SCons variables don't have a special attribute basename, this fails even if filebase (which is documented) is used. You get deep into a chain of calls, and eventually StringSubber.expand fails because it tries to eval the string to be substituted and because of the apparent attribute access in there, you get an AttributeError:

AttributeError: 'str' object has no attribute 'filebase'

At this point, the call trace looks like this:

(Pdb) bt
  /home/mats/.pyenv/versions/venv-system312/bin/scons(8)<module>()
-> sys.exit(main())
  /home/mats/github/scons/SCons/Script/Main.py(1515)main()
-> _exec_main(parser, values)
  /home/mats/github/scons/SCons/Script/Main.py(1469)_exec_main()
-> _main(parser)
  /home/mats/github/scons/SCons/Script/Main.py(1111)_main()
-> SCons.Script._SConscript._SConscript(fs, script)
  /home/mats/github/scons/SCons/Script/SConscript.py(281)_SConscript()
-> exec(compile(scriptdata, scriptname, 'exec'), call_stack[-1].globals)
  /tmp/dot/SConstruct(7)<module>()
-> env.Command('${SOURCE.filebase}.out', 'foo.in', build)
  /home/mats/github/scons/SCons/Environment.py(2320)Command()
-> return bld(self, target, source, **kw)
  /home/mats/github/scons/SCons/Builder.py(673)__call__()
-> return self._execute(env, target, source, OverrideWarner(kw), ekw)
  /home/mats/github/scons/SCons/Builder.py(579)_execute()
-> tlist, slist = self._create_nodes(env, target, source)
  /home/mats/github/scons/SCons/Builder.py(524)_create_nodes()
-> tlist = env.arg2nodes(target, target_factory, target=target, source=source)
  /home/mats/github/scons/SCons/Environment.py(691)arg2nodes()
-> v = node_factory(self.subst(v, **kw))
  /home/mats/github/scons/SCons/Environment.py(722)subst()
-> return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv, overrides=overrides)
  /home/mats/github/scons/SCons/Subst.py(854)scons_subst()
-> result = ss.substitute(strSubst, lvars)
  /home/mats/github/scons/SCons/Subst.py(459)substitute()
-> result = _dollar_exps.sub(sub_match, args)
  /home/mats/github/scons/SCons/Subst.py(454)sub_match()
-> return self.conv(self.expand(match.group(1), lvars))
> /home/mats/github/scons/SCons/Subst.py(393)expand()
-> raise_exception(e, lvars['TARGETS'], old_s)
(Pdb)

Everything at this point looks as one might expect. The string to sub is '${SOURCE.filebase}', the code has extracted the relevant part so the key variable is 'SOURCE.filebase', lvars has the right values for the special variables in it:

{'__env__': <SCons.Script.SConscript.SConsEnvironment object at 0x7f105c0e2780>,
'TARGETS': ['${SOURCE.filebase}.out'],
'TARGET': '${SOURCE.filebase}.out',
'CHANGED_TARGETS': '$TARGETS',
'UNCHANGED_TARGETS': '$TARGETS',
'SOURCES': ['foo.in'],
'SOURCE': 'foo.in',
'CHANGED_SOURCES': '$SOURCES',
'UNCHANGED_SOURCES': '$SOURCES',
'__return__': None}

But the attempt to evaluate it fails:

s = eval(key, self.gvars, lvars)

A few lines prior to that line, there was an attempt to detect a period in the string, but then nothing is done with that information until later. That initial check is here:

if key[0] == '{' or '.' in key:

@mwichmann mwichmann changed the title Substitution of special attributes does not work in builder calls (maybe only Command?)( Substitution of special attributes does not work in builder calls (maybe only Command?) Dec 8, 2024
@mwichmann mwichmann added the subst Problems with quoting, substitution label Dec 8, 2024
@mwichmann
Copy link
Collaborator Author

mwichmann commented Dec 8, 2024

Just to wrap up, at this point, the value of $SOURCE is not expected to be a string... you can simulate this a lot more simply:

>>> eval('SOURCE.filebase', {}, {'SOURCE': 'foo'})
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    eval('SOURCE.filebase', {}, {'SOURCE': 'foo'})
  File "<string>", line 1, in <module>
AttributeError: 'str' object has no attribute 'filebase'

@mwichmann mwichmann changed the title Substitution of special attributes does not work in builder calls (maybe only Command?) Substitution of special attributes of special variables like $SOURCE does not work in builder calls Dec 9, 2024
@bdbaddog
Copy link
Contributor

A workaround is to use File(), like this:

env.Command('${SOURCE.filebase}.out', File('foo.in') , "echo $TARGET $SOURCE")

It should work.
This should get you unstuck if you were stuck. Until such time as we can get it working as you'd expect.

@mwichmann
Copy link
Collaborator Author

Just to wrap up, at this point, the value of $SOURCE is not expected to be a string.

To update the details a bit: it's not actually a string. It's a Target_or_Source, which wraps an NLWrapper, which wraps a NodeList (the flavor defined in SCons.Util - there are two NodeList classes in SCons, to add to general confusion). NLWrapper defers the conversion of sources and targets to nodes - you get a NodeList whose entries are strings rather than nodes. Target_or_Source and Targets_or_Sources have __getattr__ methods which should be able to retrieve the underlying node attributes, and it's this that fails on the deferred-conversion strings, which of course don't have attributes other than those defined for string objects in general. (anybody got a headache yet?). Apparently there's a path through that doesn't trigger the conversion to nodes that is what trips up this example.

The current proposal is to update the User Guide to give the guidance above, and leave this issue open for a while longer, in case anyone is motivated to dig a bit further.

mwichmann added a commit to mwichmann/scons that referenced this issue Dec 16, 2024
The User Guide uses an example (in the Command chapter) of making a target
name out of the source name, a special attribute, and concaetenation
with a new suffix.  The special attribute part is not something that has
been introduced. Some searching suggests it's never actually described in
the User Guide, though .file, .abspath and .srcdir are used in examples.

The attribute used, .basename, doesn't actually exist - there's a .base
(and also a .filebase. Furthermmore, the substitution suggested doesn't
work. Expansion of special variables like $SOURCE into nodes is deferred -
see the docstring of SCons.Subst.NLWrapper - so the internal expansion
ends up trying to lookup the attribute on a string, which fails with
an AttributeError.  The way the user guide entry is written, it was
not actually evaluated: it was described as an <scons_example>, but
an incomplete one, and since there was no corresponding <scons_output>
the problem was not detected.

The changes fix up the example to have it use an existing attribute
and do File() on the source, and add a sidebar to provide a bit of an
explanation so this isn't just "magic".  A subsequent example (that
I added) is dropped as it doesn't add enough value, and actually
causing the evaluation to be run is added to the final example of the
chapter - the troublesom example was ex3, and wasn't set up to ever
be executed by the doc machinery, so the failure wasn't spotted;
now it can be shown to be working correctly.

This finally fixes SCons#2905, which was closed in 2018 as having been addressed,
though the non-working example actually remained. The issue is
also mentioned in SCons#4660, which is not resolved by changing the docs -
leaving that open, at least for the time being.

Signed-off-by: Mats Wichmann <[email protected]>
mwichmann added a commit to mwichmann/scons that referenced this issue Dec 16, 2024
The User Guide uses an example (in the Command chapter) of making a target
name out of the source name, a special attribute, and concatenation
with a new suffix.  The special attribute part is not something that has
been introduced. Some searching suggests it's never actually described in
the User Guide, though .file, .abspath and .srcdir are used in examples.

The attribute used, .basename, doesn't actually exist - there's a
.base and a .filebase. Furthermore, the substitution suggested doesn't
work. Expansion of special variables like $SOURCE into nodes is deferred -
see the docstring of SCons.Subst.NLWrapper - so the internal expansion
ends up trying to lookup the attribute on a string, which fails with
an AttributeError.  The way the user guide entry is written, it was
not actually evaluated: it was described as an <scons_example>, but
an incomplete one, and since there was no corresponding <scons_output>
the problem was not detected.

The changes fix up the example to have it use an existing attribute
(.base) and do File() on the source, and add a sidebar to provide a
bit of an explanation so this isn't just "magic".  A subsequent example
(ex4, which I added) is dropped as it doesn't add enough value by itself,
and the final example (formerly ex5, renamed to ex3) now includes this
substitution so it's actually run by the doc machinery, and can be
seen to be working correctly.

This finally fixes SCons#2905, which was closed in 2018 as having been addressed,
though the non-working example actually remained. The issue is
also mentioned in SCons#4660, which is not resolved by changing the docs -
leaving that open, at least for the time being.

Signed-off-by: Mats Wichmann <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
subst Problems with quoting, substitution
Projects
None yet
Development

No branches or pull requests

2 participants