run hil_hfp on gcc build

Co-authored-by: Copilot <copilot@github.com>
Signed-off-by: HiFiPhile <admin@hifiphile.com>
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 60f4c7c..cd71740 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -12,9 +12,6 @@
   group: ${{ github.workflow }}-${{ github.ref }}
   cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
 
-env:
-  HIL_JSON: test/hil/tinyusb.json
-
 jobs:
   # Check if the code changes and we need to run ci build
   # Cannot use paths filter in the on-event since we want this workflow to run even when there are no code changes, to register the commit chain
@@ -59,11 +56,12 @@
         id: set-matrix-json
         run: |
           # build matrix
-          MATRIX_JSON=$(python .github/workflows/ci_set_matrix.py)/
+          MATRIX_JSON=$(python .github/workflows/ci_set_matrix.py)
           echo "matrix=$MATRIX_JSON"
           echo "matrix=$MATRIX_JSON" >> $GITHUB_OUTPUT
-          # hil matrix
-          HIL_MATRIX_JSON=$(python test/hil/hil_ci_set_matrix.py ${{ env.HIL_JSON }})
+
+          # HIL matrix (merged from tinyusb + hifiphile configs)
+          HIL_MATRIX_JSON=$(python test/hil/hil_ci_set_matrix.py test/hil/tinyusb.json test/hil/hfp.json)
           echo "hil_matrix=$HIL_MATRIX_JSON"
           echo "hil_matrix=$HIL_MATRIX_JSON" >> $GITHUB_OUTPUT
 
@@ -239,7 +237,7 @@
 
   # ---------------------------------------
   # Hardware in the loop (HIL)
-  # Run on PR only (hil-tinyusb), hil-hfp only run on non-forked PR
+  # Run on PR only (hil-tinyusb), hil-hfp-iar only run on non-forked PR
   # ---------------------------------------
   hil-build:
     needs: [ check-paths, set-matrix ]
@@ -263,7 +261,17 @@
   # ---------------------------------------
   hil-tinyusb:
     needs: hil-build
-    runs-on: [ self-hosted, X64, hathach, hardware-in-the-loop ]
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - runner: [ self-hosted, X64, hathach, hardware-in-the-loop ]
+            hil_json: test/hil/tinyusb.json
+          - runner: [ self-hosted, Linux, X64, hifiphile ]
+            hil_json: test/hil/hfp.json
+    runs-on: ${{ matrix.runner }}
+    env:
+      HIL_JSON: ${{ matrix.hil_json }}
     steps:
       - name: Get Skip Boards from previous run
         if: github.run_attempt != '1'
@@ -308,7 +316,7 @@
   # self-hosted by HFP, build with IAR toolchain, for attached hardware checkout test/hil/hfp.json
   # Since IAR Token secret is not passed to forked PR, only build non-forked PR
   # ---------------------------------------
-  hil-hfp:
+  hil-hfp-iar:
     needs: [ check-paths ]
     if: |
       needs.check-paths.outputs.code_changed == 'true' &&
diff --git a/test/hil/hil_ci_set_matrix.py b/test/hil/hil_ci_set_matrix.py
index ecd964d..2cce35a 100644
--- a/test/hil/hil_ci_set_matrix.py
+++ b/test/hil/hil_ci_set_matrix.py
@@ -3,45 +3,60 @@
 import os
 
 
+def _resolve_config_path(config_file):
+    if os.path.exists(config_file):
+        return config_file
+
+    script_relative = os.path.join(os.path.dirname(__file__), config_file)
+    if os.path.exists(script_relative):
+        return script_relative
+
+    raise FileNotFoundError(f'Config file not found: {config_file}')
+
+
 def main():
     parser = argparse.ArgumentParser()
-    parser.add_argument('config_file', help='Configuration JSON file')
+    parser.add_argument('config_files', nargs='+', help='Configuration JSON file(s)')
     args = parser.parse_args()
 
-    config_file = args.config_file
-
-    # if config file is not found, try to find it in the same directory as this script
-    if not os.path.exists(config_file):
-        config_file = os.path.join(os.path.dirname(__file__), config_file)
-    with open(config_file) as f:
-        config = json.load(f)
-
     matrix = {
         'arm-gcc': [],
         'esp-idf': []
     }
-    for board in config['boards']:
-        name = board['name']
-        flasher = board['flasher']
-        if flasher['name'] == 'esptool':
-            toolchain = 'esp-idf'
-        else:
-            toolchain = 'arm-gcc'
 
-        build_board = f'-b {name}'
-        if 'build' in board:
-            if 'args' in board['build']:
-                build_board += ' ' + ' '.join(f'-D{a}' for a in board['build']['args'])
-            if 'flags_on' in board['build']:
-                for f in board['build']['flags_on']:
-                    if f == '':
-                        matrix[toolchain].append(build_board)
-                    else:
-                        matrix[toolchain].append(f'{build_board} -f1 {f.replace(" ", " -f1 ")}')
+    seen = {toolchain: set() for toolchain in matrix}
+
+    def append_build_arg(toolchain, build_arg):
+        if build_arg not in seen[toolchain]:
+            seen[toolchain].add(build_arg)
+            matrix[toolchain].append(build_arg)
+
+    for config_file in args.config_files:
+        with open(_resolve_config_path(config_file)) as f:
+            config = json.load(f)
+
+        for board in config['boards']:
+            name = board['name']
+            flasher = board['flasher']
+            if flasher['name'] == 'esptool':
+                toolchain = 'esp-idf'
             else:
-                matrix[toolchain].append(build_board)
-        else:
-            matrix[toolchain].append(build_board)
+                toolchain = 'arm-gcc'
+
+            build_board = f'-b {name}'
+            if 'build' in board:
+                if 'args' in board['build']:
+                    build_board += ' ' + ' '.join(f'-D{a}' for a in board['build']['args'])
+                if 'flags_on' in board['build']:
+                    for f in board['build']['flags_on']:
+                        if f == '':
+                            append_build_arg(toolchain, build_board)
+                        else:
+                            append_build_arg(toolchain, f'{build_board} -f1 {f.replace(" ", " -f1 ")}')
+                else:
+                    append_build_arg(toolchain, build_board)
+            else:
+                append_build_arg(toolchain, build_board)
 
     print(json.dumps(matrix))