dts: marshal the EDT object for later use

We need to save and restore the devicetree data to generate optimized
dependency information later on in the build, in particular during the
final application link.

Make this happen by pickling the EDT object in BUILD_DIR/edt.pickle.

The existence of this file is an implementation detail, so do not add
it to the documentation.

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py
index 94f385a..be725e4 100755
--- a/scripts/dts/gen_defines.py
+++ b/scripts/dts/gen_defines.py
@@ -22,6 +22,7 @@
 from collections import defaultdict
 import os
 import pathlib
+import pickle
 import re
 import sys
 
@@ -67,6 +68,7 @@
         edt.compat2nodes[compat] = sorted(
             nodes, key=lambda node: 0 if node.status == "okay" else 1)
 
+    # Create the generated header.
     with open(args.header_out, "w", encoding="utf-8") as header_file:
         write_top_comment(edt)
 
@@ -92,6 +94,9 @@
         write_chosen(edt)
         write_global_compat_info(edt)
 
+    if args.edt_pickle_out:
+        write_pickled_edt(edt, args.edt_pickle_out)
+
 
 def node_z_path_id(node):
     # Return the node specific bit of the node's path identifier:
@@ -127,6 +132,8 @@
     parser.add_argument("--dts-out", required=True,
                         help="path to write merged DTS source code to (e.g. "
                              "as a debugging aid)")
+    parser.add_argument("--edt-pickle-out",
+                        help="path to write pickled edtlib.EDT object to")
 
     return parser.parse_args()
 
@@ -713,6 +720,21 @@
     return f'"{escape(s)}"'
 
 
+def write_pickled_edt(edt, out_file):
+    # Writes the edt object in pickle format to out_file.
+
+    with open(out_file, 'wb') as f:
+        # Pickle protocol version 4 is the default as of Python 3.8
+        # and was introduced in 3.4, so it is both available and
+        # recommended on all versions of Python that Zephyr supports
+        # (at time of writing, Python 3.6 was Zephyr's minimum
+        # version, and 3.8 the most recent CPython release).
+        #
+        # Using a common protocol version here will hopefully avoid
+        # reproducibility issues in different Python installations.
+        pickle.dump(edt, f, protocol=4)
+
+
 def err(s):
     raise Exception(s)