Skip to content

Commit

Permalink
Update docs on special method evaluation
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
mwichmann committed Dec 16, 2024
1 parent 0c21de9 commit 3ee18c0
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 33 deletions.
3 changes: 3 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
and other attributes rather than indexing into stat return.
- The update-release-info test is adapted to accept changed help output
introduced in Python 3.12.8/3.13.1.
- Update the User Guide Command() example which now shows a target name
being created from '${SOURCE.base}.out' to use a valid special
attribute and to explain what's being done in the example.


RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700
Expand Down
4 changes: 4 additions & 0 deletions RELEASE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ DOCUMENTATION

- Improved Variables documentation.

- Update the User Guide Command() example which now shows a target name
being created from '${SOURCE.base}.out' to use a valid special
attribute and to explain what's being done in the example.

DEVELOPMENT
-----------

Expand Down
64 changes: 39 additions & 25 deletions doc/user/builders-commands.xml
Original file line number Diff line number Diff line change
Expand Up @@ -142,37 +142,49 @@ foo.in

<para>

Note that &cv-link-SOURCE; and &cv-link-TARGET; are expanded
in the source and target as well, so you can write:
&cv-link-SOURCE; and &cv-link-TARGET; are expanded
in the source and target as well:

</para>

<scons_example name="builderscommands_ex3">
<file name="SConstruct" printme="1">
env.Command('${SOURCE.basename}.out', 'foo.in', build)
</file>
</scons_example>

<para>

which does the same thing as the previous example, but allows you
to avoid repeating yourself.

</para>
<!-- NOTE: this used to be an scons_example, but was not complete and
didn't have a matching scons_output, which meant problems were not
detected. The style of this line is now reused in the last example
of the section to make sure it's actually tested
(see issue #2905 - which was closed prematurely).
-->
<sconstruct>
env.Command('${SOURCE.base}.out', File('foo.in'), build)
</sconstruct>

<para>

It may be helpful to use the <parameter>action</parameter>
keyword to specify the action, is this makes things more clear
to the reader:
Which does the same thing as the previous example, but allows you
to write a more generic rule for transforming the source filename
to the target filename, since unlike regular Builders,
&Command; does not have any built-in rules for that.

</para>

<scons_example name="builderscommands_ex4">
<file name="SConstruct" printme="1">
env.Command('${SOURCE.basename}.out', 'foo.in', action=build)
</file>
</scons_example>
<tip><para>

The example uses a <firstterm>Node special attribute</firstterm>
(<literal>.base</literal>, the file without its suffix),
a concept which has not been introduced yet,
but will appear in several subsequent examples
(see details in the Reference Manual section
<emphasis>Substitution: Special Attributes</emphasis>).
Due to the quirks of &SCons;' deferred evaluation scheme,
node special attribues do not currently work
in source and target arguments <emphasis>if</emphasis> the
replacement is a string (like <literal>'foo.in'</literal>).
They do work fine in strings describing actions.
You can give &SCons; a little help by
manually converting the filename string to a Node
(see <xref linkend="sect-creating-nodes"/>),
which is the approach used in the example.

</para></tip>

<para>

Expand All @@ -187,11 +199,13 @@ env.Command('${SOURCE.basename}.out', 'foo.in', action=build)
which include a message based on the type of action.
However, you can also construct the &Action; object yourself
to pass to &f-Command;, which gives you much more control.
Using the <parameter>action</parameter> keyword can also help
make such lines easier to read.
Here's an evolution of the example from above showing this approach:

</para>

<scons_example name="builderscommands_ex5">
<scons_example name="builderscommands_ex3">
<file name="SConstruct" printme="1">
env = Environment()

Expand All @@ -200,7 +214,7 @@ def build(target, source, env):
return None

act = Action(build, cmdstr="Building ${TARGET}")
env.Command('foo.out', 'foo.in', action=act)
env.Command('${SOURCE.base}.out', File('foo.in'), action=act)
</file>
<file name="foo.in">
foo.in
Expand All @@ -213,7 +227,7 @@ foo.in

</para>

<scons_output example="builderscommands_ex5" suffix="1">
<scons_output example="builderscommands_ex3" suffix="1">
<scons_output_command>scons -Q</scons_output_command>
</scons_output>

Expand Down
2 changes: 1 addition & 1 deletion doc/user/environments.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1876,9 +1876,9 @@ env = Environment()
env.Command('foo', [], '__ROOT__/usr/bin/printenv.py')
</file>
<file name="__ROOT__/usr/bin/printenv.py" chmod="0o755">
#!/usr/bin/env python
import os
import sys
if len(sys.argv) &gt; 1:
keys = sys.argv[1:]
else:
Expand Down
14 changes: 7 additions & 7 deletions doc/user/nodes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ This file is processed by the bin/SConsDoc.py module.

</para>

<section>
<section id="sect-builder-nodelists">
<title>Builder Methods Return Lists of Target Nodes</title>

<para>
Expand Down Expand Up @@ -150,7 +150,7 @@ int main() { printf("Goodbye, world!\n"); }

</section>

<section>
<section id="sect-creating-nodes">
<title>Explicitly Creating File and Directory Nodes</title>

<para>
Expand Down Expand Up @@ -228,7 +228,7 @@ xyzzy = Entry('xyzzy')

</section>

<section>
<section id="sect-printing-nodes">
<title>Printing &Node; File Names</title>

<para>
Expand Down Expand Up @@ -290,7 +290,7 @@ int main() { printf("Hello, world!\n"); }

</section>

<section>
<section id="sect-node-names">
<title>Using a &Node;'s File Name as a String</title>

<para>
Expand Down Expand Up @@ -337,7 +337,7 @@ int main() { printf("Hello, world!\n"); }

</section>

<section>
<section id="sect-node-paths">
<title>&GetBuildPath;: Getting the Path From a &Node; or String</title>

<para>
Expand Down Expand Up @@ -384,7 +384,7 @@ print(env.GetBuildPath([n, "sub/dir/$VAR"]))

<!--
<section>
<section id="sect-node-contents">
<title>Fetching the Contents of a &Node;</title>
<para>
Expand Down Expand Up @@ -422,7 +422,7 @@ int main() { printf("Hello, world!\n"); }

<!--
<section>
<section id="sect-value-nodes">
<title>Python Value &Node;</title>
<para>
Expand Down

0 comments on commit 3ee18c0

Please sign in to comment.