We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
CompilationDatabase tool emits paths into variant directories for source files when not using absolute paths.
**Describe the bug**
When using the CompilationDatabase tool with `COMPILATIONDB_USE_ABSPATH` == False, the `file` field appears to refer into the variant directory, not the source directory:

`"file": "build/cached/third_party/s2/base/int128.cc",`

Instead of 

`"file": "src/third_party/s2/base/int128.cc"`

**Required information**
* Link to SCons Users thread discussing your issue.
Discussed directly with @bdbaddog 

* Version of SCons
master

* Version of Python
3.7

* How you installed SCons
source

* What Platform are you on? (Linux/Windows and which version)
macOS, but shouldn't matter.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `SCons/Tool/compilation_db.py`
Content:
```
1 """
2 Implements the ability for SCons to emit a compilation database for the MongoDB project. See
3 http://clang.llvm.org/docs/JSONCompilationDatabase.html for details on what a compilation
4 database is, and why you might want one. The only user visible entry point here is
5 'env.CompilationDatabase'. This method takes an optional 'target' to name the file that
6 should hold the compilation database, otherwise, the file defaults to compile_commands.json,
7 which is the name that most clang tools search for by default.
8 """
9 
10 # Copyright 2020 MongoDB Inc.
11 #
12 # Permission is hereby granted, free of charge, to any person obtaining
13 # a copy of this software and associated documentation files (the
14 # "Software"), to deal in the Software without restriction, including
15 # without limitation the rights to use, copy, modify, merge, publish,
16 # distribute, sublicense, and/or sell copies of the Software, and to
17 # permit persons to whom the Software is furnished to do so, subject to
18 # the following conditions:
19 #
20 # The above copyright notice and this permission notice shall be included
21 # in all copies or substantial portions of the Software.
22 #
23 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
24 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
25 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 #
31 
32 import json
33 import itertools
34 import SCons
35 
36 from .cxx import CXXSuffixes
37 from .cc import CSuffixes
38 from .asm import ASSuffixes, ASPPSuffixes
39 
40 # TODO: Is there a better way to do this than this global? Right now this exists so that the
41 # emitter we add can record all of the things it emits, so that the scanner for the top level
42 # compilation database can access the complete list, and also so that the writer has easy
43 # access to write all of the files. But it seems clunky. How can the emitter and the scanner
44 # communicate more gracefully?
45 __COMPILATION_DB_ENTRIES = []
46 
47 
48 # We make no effort to avoid rebuilding the entries. Someday, perhaps we could and even
49 # integrate with the cache, but there doesn't seem to be much call for it.
50 class __CompilationDbNode(SCons.Node.Python.Value):
51     def __init__(self, value):
52         SCons.Node.Python.Value.__init__(self, value)
53         self.Decider(changed_since_last_build_node)
54 
55 
56 def changed_since_last_build_node(child, target, prev_ni, node):
57     """ Dummy decider to force always building"""
58     return True
59 
60 
61 def make_emit_compilation_DB_entry(comstr):
62     """
63     Effectively this creates a lambda function to capture:
64     * command line
65     * source
66     * target
67     :param comstr: unevaluated command line
68     :return: an emitter which has captured the above
69     """
70     user_action = SCons.Action.Action(comstr)
71 
72     def emit_compilation_db_entry(target, source, env):
73         """
74         This emitter will be added to each c/c++ object build to capture the info needed
75         for clang tools
76         :param target: target node(s)
77         :param source: source node(s)
78         :param env: Environment for use building this node
79         :return: target(s), source(s)
80         """
81 
82         dbtarget = __CompilationDbNode(source)
83 
84         entry = env.__COMPILATIONDB_Entry(
85             target=dbtarget,
86             source=[],
87             __COMPILATIONDB_UOUTPUT=target,
88             __COMPILATIONDB_USOURCE=source,
89             __COMPILATIONDB_UACTION=user_action,
90             __COMPILATIONDB_ENV=env,
91         )
92 
93         # TODO: Technically, these next two lines should not be required: it should be fine to
94         # cache the entries. However, they don't seem to update properly. Since they are quick
95         # to re-generate disable caching and sidestep this problem.
96         env.AlwaysBuild(entry)
97         env.NoCache(entry)
98 
99         __COMPILATION_DB_ENTRIES.append(dbtarget)
100 
101         return target, source
102 
103     return emit_compilation_db_entry
104 
105 
106 def compilation_db_entry_action(target, source, env, **kw):
107     """
108     Create a dictionary with evaluated command line, target, source
109     and store that info as an attribute on the target
110     (Which has been stored in __COMPILATION_DB_ENTRIES array
111     :param target: target node(s)
112     :param source: source node(s)
113     :param env: Environment for use building this node
114     :param kw:
115     :return: None
116     """
117 
118     command = env["__COMPILATIONDB_UACTION"].strfunction(
119         target=env["__COMPILATIONDB_UOUTPUT"],
120         source=env["__COMPILATIONDB_USOURCE"],
121         env=env["__COMPILATIONDB_ENV"],
122     )
123 
124     entry = {
125         "directory": env.Dir("#").abspath,
126         "command": command,
127         "file": env["__COMPILATIONDB_USOURCE"][0],
128         "output": env['__COMPILATIONDB_UOUTPUT'][0]
129     }
130 
131     target[0].write(entry)
132 
133 
134 def write_compilation_db(target, source, env):
135     entries = []
136 
137     use_abspath = env['COMPILATIONDB_USE_ABSPATH'] in [True, 1, 'True', 'true']
138 
139     for s in __COMPILATION_DB_ENTRIES:
140         entry = s.read()
141         source_file = entry['file']
142         output_file = entry['output']
143 
144         if use_abspath:
145             source_file = source_file.abspath
146             output_file = output_file.abspath
147         else:
148             source_file = source_file.path
149             output_file = output_file.path
150 
151         path_entry = {'directory': entry['directory'],
152                       'command': entry['command'],
153                       'file': source_file,
154                       'output': output_file}
155 
156         entries.append(path_entry)
157 
158     with open(target[0].path, "w") as output_file:
159         json.dump(
160             entries, output_file, sort_keys=True, indent=4, separators=(",", ": ")
161         )
162 
163 
164 def scan_compilation_db(node, env, path):
165     return __COMPILATION_DB_ENTRIES
166 
167 
168 def compilation_db_emitter(target, source, env):
169     """ fix up the source/targets """
170 
171     # Someone called env.CompilationDatabase('my_targetname.json')
172     if not target and len(source) == 1:
173         target = source
174 
175     # Default target name is compilation_db.json
176     if not target:
177         target = ['compile_commands.json', ]
178 
179     # No source should have been passed. Drop it.
180     if source:
181         source = []
182 
183     return target, source
184 
185 
186 def generate(env, **kwargs):
187     static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
188 
189     env["COMPILATIONDB_COMSTR"] = kwargs.get(
190         "COMPILATIONDB_COMSTR", "Building compilation database $TARGET"
191     )
192 
193     components_by_suffix = itertools.chain(
194         itertools.product(
195             CSuffixes,
196             [
197                 (static_obj, SCons.Defaults.StaticObjectEmitter, "$CCCOM"),
198                 (shared_obj, SCons.Defaults.SharedObjectEmitter, "$SHCCCOM"),
199             ],
200         ),
201         itertools.product(
202             CXXSuffixes,
203             [
204                 (static_obj, SCons.Defaults.StaticObjectEmitter, "$CXXCOM"),
205                 (shared_obj, SCons.Defaults.SharedObjectEmitter, "$SHCXXCOM"),
206             ],
207         ),
208         itertools.product(
209             ASSuffixes,
210             [(static_obj, SCons.Defaults.StaticObjectEmitter, "$ASCOM")],
211             [(shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASCOM")],
212         ),
213         itertools.product(
214             ASPPSuffixes,
215             [(static_obj, SCons.Defaults.StaticObjectEmitter, "$ASPPCOM")],
216             [(shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASPPCOM")],
217         ),
218     )
219 
220     for entry in components_by_suffix:
221         suffix = entry[0]
222         builder, base_emitter, command = entry[1]
223 
224         # Assumes a dictionary emitter
225         emitter = builder.emitter.get(suffix, False)
226         if emitter:
227             # We may not have tools installed which initialize all or any of
228             # cxx, cc, or assembly. If not skip resetting the respective emitter.
229             builder.emitter[suffix] = SCons.Builder.ListEmitter(
230                 [emitter, make_emit_compilation_DB_entry(command), ]
231             )
232 
233     env["BUILDERS"]["__COMPILATIONDB_Entry"] = SCons.Builder.Builder(
234         action=SCons.Action.Action(compilation_db_entry_action, None),
235     )
236 
237     env["BUILDERS"]["CompilationDatabase"] = SCons.Builder.Builder(
238         action=SCons.Action.Action(write_compilation_db, "$COMPILATIONDB_COMSTR"),
239         target_scanner=SCons.Scanner.Scanner(
240             function=scan_compilation_db, node_class=None
241         ),
242         emitter=compilation_db_emitter,
243         suffix='json',
244     )
245 
246     env['COMPILATIONDB_USE_ABSPATH'] = False
247 
248 
249 def exists(env):
250     return True
251
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/SCons/Tool/compilation_db.py b/SCons/Tool/compilation_db.py
--- a/SCons/Tool/compilation_db.py
+++ b/SCons/Tool/compilation_db.py
@@ -142,10 +142,10 @@
         output_file = entry['output']
 
         if use_abspath:
-            source_file = source_file.abspath
+            source_file = source_file.srcnode().abspath
             output_file = output_file.abspath
         else:
-            source_file = source_file.path
+            source_file = source_file.srcnode().path
             output_file = output_file.path
 
         path_entry = {'directory': entry['directory'],

{"golden_diff": "diff --git a/SCons/Tool/compilation_db.py b/SCons/Tool/compilation_db.py\n--- a/SCons/Tool/compilation_db.py\n+++ b/SCons/Tool/compilation_db.py\n@@ -142,10 +142,10 @@\n         output_file = entry['output']\n \n         if use_abspath:\n-            source_file = source_file.abspath\n+            source_file = source_file.srcnode().abspath\n             output_file = output_file.abspath\n         else:\n-            source_file = source_file.path\n+            source_file = source_file.srcnode().path\n             output_file = output_file.path\n \n         path_entry = {'directory': entry['directory'],\n", "issue": "CompilationDatabase tool emits paths into variant directories for source files when not using absolute paths.\n**Describe the bug**\r\nWhen using the CompilationDatabase tool with `COMPILATIONDB_USE_ABSPATH` == False, the `file` field appears to refer into the variant directory, not the source directory:\r\n\r\n`\"file\": \"build/cached/third_party/s2/base/int128.cc\",`\r\n\r\nInstead of \r\n\r\n`\"file\": \"src/third_party/s2/base/int128.cc\"`\r\n\r\n**Required information**\r\n* Link to SCons Users thread discussing your issue.\r\nDiscussed directly with @bdbaddog \r\n\r\n* Version of SCons\r\nmaster\r\n\r\n* Version of Python\r\n3.7\r\n\r\n* How you installed SCons\r\nsource\r\n\r\n* What Platform are you on? (Linux/Windows and which version)\r\nmacOS, but shouldn't matter.\r\n\n", "before_files": [{"content": "\"\"\"\nImplements the ability for SCons to emit a compilation database for the MongoDB project. See\nhttp://clang.llvm.org/docs/JSONCompilationDatabase.html for details on what a compilation\ndatabase is, and why you might want one. The only user visible entry point here is\n'env.CompilationDatabase'. This method takes an optional 'target' to name the file that\nshould hold the compilation database, otherwise, the file defaults to compile_commands.json,\nwhich is the name that most clang tools search for by default.\n\"\"\"\n\n# Copyright 2020 MongoDB Inc.\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be included\n# in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\n# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nimport json\nimport itertools\nimport SCons\n\nfrom .cxx import CXXSuffixes\nfrom .cc import CSuffixes\nfrom .asm import ASSuffixes, ASPPSuffixes\n\n# TODO: Is there a better way to do this than this global? Right now this exists so that the\n# emitter we add can record all of the things it emits, so that the scanner for the top level\n# compilation database can access the complete list, and also so that the writer has easy\n# access to write all of the files. But it seems clunky. How can the emitter and the scanner\n# communicate more gracefully?\n__COMPILATION_DB_ENTRIES = []\n\n\n# We make no effort to avoid rebuilding the entries. Someday, perhaps we could and even\n# integrate with the cache, but there doesn't seem to be much call for it.\nclass __CompilationDbNode(SCons.Node.Python.Value):\n    def __init__(self, value):\n        SCons.Node.Python.Value.__init__(self, value)\n        self.Decider(changed_since_last_build_node)\n\n\ndef changed_since_last_build_node(child, target, prev_ni, node):\n    \"\"\" Dummy decider to force always building\"\"\"\n    return True\n\n\ndef make_emit_compilation_DB_entry(comstr):\n    \"\"\"\n    Effectively this creates a lambda function to capture:\n    * command line\n    * source\n    * target\n    :param comstr: unevaluated command line\n    :return: an emitter which has captured the above\n    \"\"\"\n    user_action = SCons.Action.Action(comstr)\n\n    def emit_compilation_db_entry(target, source, env):\n        \"\"\"\n        This emitter will be added to each c/c++ object build to capture the info needed\n        for clang tools\n        :param target: target node(s)\n        :param source: source node(s)\n        :param env: Environment for use building this node\n        :return: target(s), source(s)\n        \"\"\"\n\n        dbtarget = __CompilationDbNode(source)\n\n        entry = env.__COMPILATIONDB_Entry(\n            target=dbtarget,\n            source=[],\n            __COMPILATIONDB_UOUTPUT=target,\n            __COMPILATIONDB_USOURCE=source,\n            __COMPILATIONDB_UACTION=user_action,\n            __COMPILATIONDB_ENV=env,\n        )\n\n        # TODO: Technically, these next two lines should not be required: it should be fine to\n        # cache the entries. However, they don't seem to update properly. Since they are quick\n        # to re-generate disable caching and sidestep this problem.\n        env.AlwaysBuild(entry)\n        env.NoCache(entry)\n\n        __COMPILATION_DB_ENTRIES.append(dbtarget)\n\n        return target, source\n\n    return emit_compilation_db_entry\n\n\ndef compilation_db_entry_action(target, source, env, **kw):\n    \"\"\"\n    Create a dictionary with evaluated command line, target, source\n    and store that info as an attribute on the target\n    (Which has been stored in __COMPILATION_DB_ENTRIES array\n    :param target: target node(s)\n    :param source: source node(s)\n    :param env: Environment for use building this node\n    :param kw:\n    :return: None\n    \"\"\"\n\n    command = env[\"__COMPILATIONDB_UACTION\"].strfunction(\n        target=env[\"__COMPILATIONDB_UOUTPUT\"],\n        source=env[\"__COMPILATIONDB_USOURCE\"],\n        env=env[\"__COMPILATIONDB_ENV\"],\n    )\n\n    entry = {\n        \"directory\": env.Dir(\"#\").abspath,\n        \"command\": command,\n        \"file\": env[\"__COMPILATIONDB_USOURCE\"][0],\n        \"output\": env['__COMPILATIONDB_UOUTPUT'][0]\n    }\n\n    target[0].write(entry)\n\n\ndef write_compilation_db(target, source, env):\n    entries = []\n\n    use_abspath = env['COMPILATIONDB_USE_ABSPATH'] in [True, 1, 'True', 'true']\n\n    for s in __COMPILATION_DB_ENTRIES:\n        entry = s.read()\n        source_file = entry['file']\n        output_file = entry['output']\n\n        if use_abspath:\n            source_file = source_file.abspath\n            output_file = output_file.abspath\n        else:\n            source_file = source_file.path\n            output_file = output_file.path\n\n        path_entry = {'directory': entry['directory'],\n                      'command': entry['command'],\n                      'file': source_file,\n                      'output': output_file}\n\n        entries.append(path_entry)\n\n    with open(target[0].path, \"w\") as output_file:\n        json.dump(\n            entries, output_file, sort_keys=True, indent=4, separators=(\",\", \": \")\n        )\n\n\ndef scan_compilation_db(node, env, path):\n    return __COMPILATION_DB_ENTRIES\n\n\ndef compilation_db_emitter(target, source, env):\n    \"\"\" fix up the source/targets \"\"\"\n\n    # Someone called env.CompilationDatabase('my_targetname.json')\n    if not target and len(source) == 1:\n        target = source\n\n    # Default target name is compilation_db.json\n    if not target:\n        target = ['compile_commands.json', ]\n\n    # No source should have been passed. Drop it.\n    if source:\n        source = []\n\n    return target, source\n\n\ndef generate(env, **kwargs):\n    static_obj, shared_obj = SCons.Tool.createObjBuilders(env)\n\n    env[\"COMPILATIONDB_COMSTR\"] = kwargs.get(\n        \"COMPILATIONDB_COMSTR\", \"Building compilation database $TARGET\"\n    )\n\n    components_by_suffix = itertools.chain(\n        itertools.product(\n            CSuffixes,\n            [\n                (static_obj, SCons.Defaults.StaticObjectEmitter, \"$CCCOM\"),\n                (shared_obj, SCons.Defaults.SharedObjectEmitter, \"$SHCCCOM\"),\n            ],\n        ),\n        itertools.product(\n            CXXSuffixes,\n            [\n                (static_obj, SCons.Defaults.StaticObjectEmitter, \"$CXXCOM\"),\n                (shared_obj, SCons.Defaults.SharedObjectEmitter, \"$SHCXXCOM\"),\n            ],\n        ),\n        itertools.product(\n            ASSuffixes,\n            [(static_obj, SCons.Defaults.StaticObjectEmitter, \"$ASCOM\")],\n            [(shared_obj, SCons.Defaults.SharedObjectEmitter, \"$ASCOM\")],\n        ),\n        itertools.product(\n            ASPPSuffixes,\n            [(static_obj, SCons.Defaults.StaticObjectEmitter, \"$ASPPCOM\")],\n            [(shared_obj, SCons.Defaults.SharedObjectEmitter, \"$ASPPCOM\")],\n        ),\n    )\n\n    for entry in components_by_suffix:\n        suffix = entry[0]\n        builder, base_emitter, command = entry[1]\n\n        # Assumes a dictionary emitter\n        emitter = builder.emitter.get(suffix, False)\n        if emitter:\n            # We may not have tools installed which initialize all or any of\n            # cxx, cc, or assembly. If not skip resetting the respective emitter.\n            builder.emitter[suffix] = SCons.Builder.ListEmitter(\n                [emitter, make_emit_compilation_DB_entry(command), ]\n            )\n\n    env[\"BUILDERS\"][\"__COMPILATIONDB_Entry\"] = SCons.Builder.Builder(\n        action=SCons.Action.Action(compilation_db_entry_action, None),\n    )\n\n    env[\"BUILDERS\"][\"CompilationDatabase\"] = SCons.Builder.Builder(\n        action=SCons.Action.Action(write_compilation_db, \"$COMPILATIONDB_COMSTR\"),\n        target_scanner=SCons.Scanner.Scanner(\n            function=scan_compilation_db, node_class=None\n        ),\n        emitter=compilation_db_emitter,\n        suffix='json',\n    )\n\n    env['COMPILATIONDB_USE_ABSPATH'] = False\n\n\ndef exists(env):\n    return True\n", "path": "SCons/Tool/compilation_db.py"}], "after_files": [{"content": "\"\"\"\nImplements the ability for SCons to emit a compilation database for the MongoDB project. See\nhttp://clang.llvm.org/docs/JSONCompilationDatabase.html for details on what a compilation\ndatabase is, and why you might want one. The only user visible entry point here is\n'env.CompilationDatabase'. This method takes an optional 'target' to name the file that\nshould hold the compilation database, otherwise, the file defaults to compile_commands.json,\nwhich is the name that most clang tools search for by default.\n\"\"\"\n\n# Copyright 2020 MongoDB Inc.\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be included\n# in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\n# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nimport json\nimport itertools\nimport SCons\n\nfrom .cxx import CXXSuffixes\nfrom .cc import CSuffixes\nfrom .asm import ASSuffixes, ASPPSuffixes\n\n# TODO: Is there a better way to do this than this global? Right now this exists so that the\n# emitter we add can record all of the things it emits, so that the scanner for the top level\n# compilation database can access the complete list, and also so that the writer has easy\n# access to write all of the files. But it seems clunky. How can the emitter and the scanner\n# communicate more gracefully?\n__COMPILATION_DB_ENTRIES = []\n\n\n# We make no effort to avoid rebuilding the entries. Someday, perhaps we could and even\n# integrate with the cache, but there doesn't seem to be much call for it.\nclass __CompilationDbNode(SCons.Node.Python.Value):\n    def __init__(self, value):\n        SCons.Node.Python.Value.__init__(self, value)\n        self.Decider(changed_since_last_build_node)\n\n\ndef changed_since_last_build_node(child, target, prev_ni, node):\n    \"\"\" Dummy decider to force always building\"\"\"\n    return True\n\n\ndef make_emit_compilation_DB_entry(comstr):\n    \"\"\"\n    Effectively this creates a lambda function to capture:\n    * command line\n    * source\n    * target\n    :param comstr: unevaluated command line\n    :return: an emitter which has captured the above\n    \"\"\"\n    user_action = SCons.Action.Action(comstr)\n\n    def emit_compilation_db_entry(target, source, env):\n        \"\"\"\n        This emitter will be added to each c/c++ object build to capture the info needed\n        for clang tools\n        :param target: target node(s)\n        :param source: source node(s)\n        :param env: Environment for use building this node\n        :return: target(s), source(s)\n        \"\"\"\n\n        dbtarget = __CompilationDbNode(source)\n\n        entry = env.__COMPILATIONDB_Entry(\n            target=dbtarget,\n            source=[],\n            __COMPILATIONDB_UOUTPUT=target,\n            __COMPILATIONDB_USOURCE=source,\n            __COMPILATIONDB_UACTION=user_action,\n            __COMPILATIONDB_ENV=env,\n        )\n\n        # TODO: Technically, these next two lines should not be required: it should be fine to\n        # cache the entries. However, they don't seem to update properly. Since they are quick\n        # to re-generate disable caching and sidestep this problem.\n        env.AlwaysBuild(entry)\n        env.NoCache(entry)\n\n        __COMPILATION_DB_ENTRIES.append(dbtarget)\n\n        return target, source\n\n    return emit_compilation_db_entry\n\n\ndef compilation_db_entry_action(target, source, env, **kw):\n    \"\"\"\n    Create a dictionary with evaluated command line, target, source\n    and store that info as an attribute on the target\n    (Which has been stored in __COMPILATION_DB_ENTRIES array\n    :param target: target node(s)\n    :param source: source node(s)\n    :param env: Environment for use building this node\n    :param kw:\n    :return: None\n    \"\"\"\n\n    command = env[\"__COMPILATIONDB_UACTION\"].strfunction(\n        target=env[\"__COMPILATIONDB_UOUTPUT\"],\n        source=env[\"__COMPILATIONDB_USOURCE\"],\n        env=env[\"__COMPILATIONDB_ENV\"],\n    )\n\n    entry = {\n        \"directory\": env.Dir(\"#\").abspath,\n        \"command\": command,\n        \"file\": env[\"__COMPILATIONDB_USOURCE\"][0],\n        \"output\": env['__COMPILATIONDB_UOUTPUT'][0]\n    }\n\n    target[0].write(entry)\n\n\ndef write_compilation_db(target, source, env):\n    entries = []\n\n    use_abspath = env['COMPILATIONDB_USE_ABSPATH'] in [True, 1, 'True', 'true']\n\n    for s in __COMPILATION_DB_ENTRIES:\n        entry = s.read()\n        source_file = entry['file']\n        output_file = entry['output']\n\n        if use_abspath:\n            source_file = source_file.srcnode().abspath\n            output_file = output_file.abspath\n        else:\n            source_file = source_file.srcnode().path\n            output_file = output_file.path\n\n        path_entry = {'directory': entry['directory'],\n                      'command': entry['command'],\n                      'file': source_file,\n                      'output': output_file}\n\n        entries.append(path_entry)\n\n    with open(target[0].path, \"w\") as output_file:\n        json.dump(\n            entries, output_file, sort_keys=True, indent=4, separators=(\",\", \": \")\n        )\n\n\ndef scan_compilation_db(node, env, path):\n    return __COMPILATION_DB_ENTRIES\n\n\ndef compilation_db_emitter(target, source, env):\n    \"\"\" fix up the source/targets \"\"\"\n\n    # Someone called env.CompilationDatabase('my_targetname.json')\n    if not target and len(source) == 1:\n        target = source\n\n    # Default target name is compilation_db.json\n    if not target:\n        target = ['compile_commands.json', ]\n\n    # No source should have been passed. Drop it.\n    if source:\n        source = []\n\n    return target, source\n\n\ndef generate(env, **kwargs):\n    static_obj, shared_obj = SCons.Tool.createObjBuilders(env)\n\n    env[\"COMPILATIONDB_COMSTR\"] = kwargs.get(\n        \"COMPILATIONDB_COMSTR\", \"Building compilation database $TARGET\"\n    )\n\n    components_by_suffix = itertools.chain(\n        itertools.product(\n            CSuffixes,\n            [\n                (static_obj, SCons.Defaults.StaticObjectEmitter, \"$CCCOM\"),\n                (shared_obj, SCons.Defaults.SharedObjectEmitter, \"$SHCCCOM\"),\n            ],\n        ),\n        itertools.product(\n            CXXSuffixes,\n            [\n                (static_obj, SCons.Defaults.StaticObjectEmitter, \"$CXXCOM\"),\n                (shared_obj, SCons.Defaults.SharedObjectEmitter, \"$SHCXXCOM\"),\n            ],\n        ),\n        itertools.product(\n            ASSuffixes,\n            [(static_obj, SCons.Defaults.StaticObjectEmitter, \"$ASCOM\")],\n            [(shared_obj, SCons.Defaults.SharedObjectEmitter, \"$ASCOM\")],\n        ),\n        itertools.product(\n            ASPPSuffixes,\n            [(static_obj, SCons.Defaults.StaticObjectEmitter, \"$ASPPCOM\")],\n            [(shared_obj, SCons.Defaults.SharedObjectEmitter, \"$ASPPCOM\")],\n        ),\n    )\n\n    for entry in components_by_suffix:\n        suffix = entry[0]\n        builder, base_emitter, command = entry[1]\n\n        # Assumes a dictionary emitter\n        emitter = builder.emitter.get(suffix, False)\n        if emitter:\n            # We may not have tools installed which initialize all or any of\n            # cxx, cc, or assembly. If not skip resetting the respective emitter.\n            builder.emitter[suffix] = SCons.Builder.ListEmitter(\n                [emitter, make_emit_compilation_DB_entry(command), ]\n            )\n\n    env[\"BUILDERS\"][\"__COMPILATIONDB_Entry\"] = SCons.Builder.Builder(\n        action=SCons.Action.Action(compilation_db_entry_action, None),\n    )\n\n    env[\"BUILDERS\"][\"CompilationDatabase\"] = SCons.Builder.Builder(\n        action=SCons.Action.Action(write_compilation_db, \"$COMPILATIONDB_COMSTR\"),\n        target_scanner=SCons.Scanner.Scanner(\n            function=scan_compilation_db, node_class=None\n        ),\n        emitter=compilation_db_emitter,\n        suffix='json',\n    )\n\n    env['COMPILATIONDB_USE_ABSPATH'] = False\n\n\ndef exists(env):\n    return True\n", "path": "SCons/Tool/compilation_db.py"}]}