sysbuild: support Zephyr modules

This commit extends the Zephyr module yaml scheme with additional
entries for sysbuild in the build section.

This allows for Zephyr modules to extend the sysbuild infrastructure
by providing additional CMake and Kconfig files to be included in
sysbuild.

The new settings are:
build:
  sysbuild-cmake: <path>
  sysbuild-kconfig: <path>/<file>
  sysbuild-ext: <true>|<false>
  sysbuild-kconfig-ext:  <true>|<false>

those settings follow the same pattern as the equivalent Zephyr build
settings but are processed by sysbuild.

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
diff --git a/scripts/zephyr_module.py b/scripts/zephyr_module.py
index 65fb7c5..e0cf18d 100755
--- a/scripts/zephyr_module.py
+++ b/scripts/zephyr_module.py
@@ -57,6 +57,20 @@
         required: false
         type: bool
         default: false
+      sysbuild-cmake:
+        required: false
+        type: str
+      sysbuild-kconfig:
+        required: false
+        type: str
+      sysbuild-cmake-ext:
+        required: false
+        type: bool
+        default: false
+      sysbuild-kconfig-ext:
+        required: false
+        type: bool
+        default: false
       depends:
         required: false
         type: seq
@@ -216,6 +230,40 @@
                        module_path.as_posix()))
 
 
+def process_sysbuildcmake(module, meta):
+    section = meta.get('build', dict())
+    module_path = PurePath(module)
+    module_yml = module_path.joinpath('zephyr/module.yml')
+
+    cmake_extern = section.get('sysbuild-cmake-ext', False)
+    if cmake_extern:
+        return('\"{}\":\"{}\":\"{}\"\n'
+               .format(meta['name'],
+                       module_path.as_posix(),
+                       "${SYSBUILD_" + meta['name-sanitized'].upper() + "_CMAKE_DIR}"))
+
+    cmake_setting = section.get('sysbuild-cmake', None)
+    if not validate_setting(cmake_setting, module, 'CMakeLists.txt'):
+        sys.exit('ERROR: "cmake" key in {} has folder value "{}" which '
+                 'does not contain a CMakeLists.txt file.'
+                 .format(module_yml.as_posix(), cmake_setting))
+
+    if cmake_setting is None:
+        return ""
+
+    cmake_path = os.path.join(module, cmake_setting or 'zephyr')
+    cmake_file = os.path.join(cmake_path, 'CMakeLists.txt')
+    if os.path.isfile(cmake_file):
+        return('\"{}\":\"{}\":\"{}\"\n'
+               .format(meta['name'],
+                       module_path.as_posix(),
+                       Path(cmake_path).resolve().as_posix()))
+    else:
+        return('\"{}\":\"{}\":\"\"\n'
+               .format(meta['name'],
+                       module_path.as_posix()))
+
+
 def process_settings(module, meta):
     section = meta.get('build', dict())
     build_settings = section.get('settings', None)
@@ -260,13 +308,14 @@
     return blobs
 
 
-def kconfig_snippet(meta, path, kconfig_file=None, blobs=False):
+def kconfig_snippet(meta, path, kconfig_file=None, blobs=False, sysbuild=False):
     name = meta['name']
     name_sanitized = meta['name-sanitized']
 
     snippet = [f'menu "{name} ({path.as_posix()})"',
                f'osource "{kconfig_file.resolve().as_posix()}"' if kconfig_file
-               else f'osource "$(ZEPHYR_{name_sanitized.upper()}_KCONFIG)"',
+               else f'osource "$(SYSBUILD_{name_sanitized.upper()}_KCONFIG)"' if sysbuild is True
+	       else f'osource "$(ZEPHYR_{name_sanitized.upper()}_KCONFIG)"',
                f'config ZEPHYR_{name_sanitized.upper()}_MODULE',
                '	bool',
                '	default y',
@@ -301,6 +350,30 @@
         return ""
 
 
+def process_sysbuildkconfig(module, meta):
+    section = meta.get('build', dict())
+    module_path = PurePath(module)
+    module_yml = module_path.joinpath('zephyr/module.yml')
+    kconfig_extern = section.get('sysbuild-kconfig-ext', False)
+    if kconfig_extern:
+        return kconfig_snippet(meta, module_path, sysbuild=True)
+
+    kconfig_setting = section.get('sysbuild-kconfig', None)
+    if not validate_setting(kconfig_setting, module):
+        sys.exit('ERROR: "kconfig" key in {} has value "{}" which does '
+                 'not point to a valid Kconfig file.'
+                 .format(module_yml, kconfig_setting))
+
+    if kconfig_setting is None:
+        return ""
+
+    kconfig_file = os.path.join(module, kconfig_setting)
+    if os.path.isfile(kconfig_file):
+        return kconfig_snippet(meta, module_path, Path(kconfig_file))
+    else:
+        return ""
+
+
 def process_twister(module, meta):
 
     out = ""
@@ -553,6 +626,12 @@
     parser.add_argument('--cmake-out',
                         help="""File to write with resulting <name>:<path>
                              values to use for including in CMake""")
+    parser.add_argument('--sysbuild-kconfig-out',
+                        help="""File to write with resulting KConfig import
+                             statements.""")
+    parser.add_argument('--sysbuild-cmake-out',
+                        help="""File to write with resulting <name>:<path>
+                             values to use for including in CMake""")
     parser.add_argument('--meta-out',
                         help="""Write a build meta YaML file containing a list
                              of Zephyr modules and west projects.
@@ -576,6 +655,8 @@
 
     kconfig = ""
     cmake = ""
+    sysbuild_kconfig = ""
+    sysbuild_cmake = ""
     settings = ""
     twister = ""
 
@@ -586,6 +667,8 @@
     for module in modules:
         kconfig += process_kconfig(module.project, module.meta)
         cmake += process_cmake(module.project, module.meta)
+        sysbuild_kconfig += process_sysbuildkconfig(module.project, module.meta)
+        sysbuild_cmake += process_sysbuildcmake(module.project, module.meta)
         settings += process_settings(module.project, module.meta)
         twister += process_twister(module.project, module.meta)
 
@@ -597,6 +680,14 @@
         with open(args.cmake_out, 'w', encoding="utf-8") as fp:
             fp.write(cmake)
 
+    if args.sysbuild_kconfig_out:
+        with open(args.sysbuild_kconfig_out, 'w', encoding="utf-8") as fp:
+            fp.write(sysbuild_kconfig)
+
+    if args.sysbuild_cmake_out:
+        with open(args.sysbuild_cmake_out, 'w', encoding="utf-8") as fp:
+            fp.write(sysbuild_cmake)
+
     if args.settings_out:
         with open(args.settings_out, 'w', encoding="utf-8") as fp:
             fp.write('''\