Repository: SCons/scons

Issue:
The target and source variables are not usable for generators in CPPDEFINES
**Describe the bug**
If `CPPDEFINES` contains a generator, that generator is called, but with empty `target` and `source` variables.

**Required information**
* Link to SCons Users thread discussing your issue.
Discussed on Discord devel channel

* Version of SCons
3.1.2

* Version of Python
3.7

* Which python distribution if applicable (python.org, cygwin, anaconda, macports, brew,etc)
N/A

* How you installed SCons
Vendored

* What Platform are you on? (Linux/Windows and which version)
macOS

* How to reproduce your issue?  Please include a small self contained reproducer. Likely a SConstruct should do for most issues.
```python
env = Environment()

def define_generator(target, source, env, for_signature):
    print('XXX', [str(t) for t in target], [str(s) for s in source])
    return "DEFINE"
env['DEFINE_GENERATOR'] = define_generator

env.Program(
    target='main',
    source='main.c',
    CPPDEFINES='$DEFINE_GENERATOR'
)
```

Will print

```
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
XXX [] []
XXX [] []
scons: *** [main.o] Source `main.c' not found, needed by target `main.o'.
scons: building terminated because of errors.
```

Those `XXX` things should contain the target and source lists.

A fix is pretty easy, in case I don't get to a PR:

```python
diff --git a/src/third_party/scons-3.1.2/scons-local-3.1.2/SCons/Defaults.py b/src/third_party/scons-3.1.2/scons-local-3.1.2/SCons/Defaults.py
index c39c417647..118356d1b5 100644
--- a/src/third_party/scons-3.1.2/scons-local-3.1.2/SCons/Defaults.py
+++ b/src/third_party/scons-3.1.2/scons-local-3.1.2/SCons/Defaults.py
@@ -492,12 +492,12 @@ def processDefines(defs):
     return l


-def _defines(prefix, defs, suffix, env, c=_concat_ixes):
+def _defines(prefix, defs, suffix, env, target, source, c=_concat_ixes):
     """A wrapper around _concat_ixes that turns a list or string
     into a list of C preprocessor command-line definitions.
     """

-    return c(prefix, env.subst_path(processDefines(defs)), suffix, env)
+    return c(prefix, env.subst_path(processDefines(defs), target=target, source=source), suffix, env)


 class NullCmdGenerator(object):
@@ -574,7 +574,7 @@ ConstructionEnvironment = {
     '_LIBFLAGS'     : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
     '_LIBDIRFLAGS'  : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
     '_CPPINCFLAGS'  : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
-    '_CPPDEFFLAGS'  : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}',
+    '_CPPDEFFLAGS'  : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__, TARGET, SOURCE)}',

     '__libversionflags'      : __libversionflags,
     '__SHLIBVERSIONFLAGS'    : '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}',
```



* How you invoke scons (The command line you're using "scons --flags some_arguments")



Discussion:
Marking it has_patch as it has the beginnings of one (not complete)
Adding this to 4.2 milestone for now - we should be able to implement whatever the decision is fairly easily, but do need a decision.

Resolving patch:
diff --git a/SCons/Defaults.py b/SCons/Defaults.py
--- a/SCons/Defaults.py
+++ b/SCons/Defaults.py
@@ -498,12 +498,12 @@ def processDefines(defs):
     return l
 
 
-def _defines(prefix, defs, suffix, env, c=_concat_ixes):
+def _defines(prefix, defs, suffix, env, target, source, c=_concat_ixes):
     """A wrapper around _concat_ixes that turns a list or string
     into a list of C preprocessor command-line definitions.
     """
 
-    return c(prefix, env.subst_list(processDefines(defs)), suffix, env)
+    return c(prefix, env.subst_list(processDefines(defs), target=target, source=source), suffix, env)
 
 
 class NullCmdGenerator:
@@ -608,7 +608,7 @@ def __lib_either_version_flag(env, version_var1, version_var2, flags_var):
     '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
     '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
     '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
-    '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}',
+    '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__, TARGET, SOURCE)}',
 
     '__libversionflags': __libversionflags,
     '__SHLIBVERSIONFLAGS': '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}',


Test patch:
diff --git a/test/CPPDEFINES/basic.py b/test/CPPDEFINES/basic.py
old mode 100644
new mode 100755
--- a/test/CPPDEFINES/basic.py
+++ b/test/CPPDEFINES/basic.py
@@ -39,13 +39,24 @@
     ['x', ['y', 123], 'z', ('int', '$INTEGER')],
     { 'c' : 3, 'b': None, 'a' : 1 },
     "${TESTDEFS}",
+    "${GEN}",
 ]
+
+def generator(target, source, env, for_signature):
+    if target and source:
+        return '_'.join([str(target[0]), 'GENERATED', str(source[0])])
+    return 'TARGET_AND_SOURCE_ARE_MISSING'
+
 for i in test_list:
-    env = Environment(CPPDEFPREFIX='-D', CPPDEFSUFFIX='', INTEGER=0, TESTDEFS=["FOO", "BAR=1"])
-    print(env.Clone(CPPDEFINES=i).subst('$_CPPDEFFLAGS'))
+    env = Environment(CPPDEFPREFIX='-D', CPPDEFSUFFIX='', INTEGER=0, TESTDEFS=["FOO", "BAR=1"], GEN=generator)
+    ttt = env.Entry('#ttt')
+    sss = env.Entry('#sss')
+    print(env.Clone(CPPDEFINES=i).subst('$_CPPDEFFLAGS', target=[ttt], source=[sss]))
 for i in test_list:
-    env = Environment(CPPDEFPREFIX='|', CPPDEFSUFFIX='|', INTEGER=1, TESTDEFS=["FOO", "BAR=1"])
-    print(env.Clone(CPPDEFINES=i).subst('$_CPPDEFFLAGS'))
+    env = Environment(CPPDEFPREFIX='|', CPPDEFSUFFIX='|', INTEGER=1, TESTDEFS=["FOO", "BAR=1"], GEN=generator)
+    ttt = env.Entry('#ttt')
+    sss = env.Entry('#sss')
+    print(env.Clone(CPPDEFINES=i).subst('$_CPPDEFFLAGS', target=[ttt], source=[sss]))
 """)
 
 expect = test.wrap_stdout(build_str="scons: `.' is up to date.\n",
@@ -55,11 +66,13 @@
 -Dx -Dy=123 -Dz -Dint=0
 -Da=1 -Db -Dc=3
 -DFOO -DBAR=1
+-Dttt_GENERATED_sss
 |xyz|
 |x| |y| |z|
 |x| |y=123| |z| |int=1|
 |a=1| |b| |c=3|
 |FOO| |BAR=1|
+|ttt_GENERATED_sss|
 """)
 
 test.run(arguments = '.', stdout=expect)