feat(tlc): add command gen-header

Introduce the gen-header command to the tool, enabling developers to
create language bindings. Currently, it supports generating C headers
from a transfer list.

Change-Id: Ibec75639c38577802d5abe55c7bc718740aad2b8
Signed-off-by: Harrison Mutai <harrison.mutai@arm.com>
diff --git a/tools/tlc/tests/test_cli.py b/tools/tlc/tests/test_cli.py
index 4fad349..a5ef30e 100644
--- a/tools/tlc/tests/test_cli.py
+++ b/tools/tlc/tests/test_cli.py
@@ -11,6 +11,7 @@
 
 from math import ceil, log2
 from pathlib import Path
+from re import findall, search
 from unittest import mock
 
 import pytest
@@ -383,6 +384,69 @@
     assert actual == expected
 
 
+@pytest.mark.parametrize("option", ["-O", "--output"])
+def test_gen_tl_header_with_output_name(tlcrunner, tmptlstr, option, filename="test.h"):
+    with tlcrunner.isolated_filesystem():
+        result = tlcrunner.invoke(
+            cli,
+            [
+                "gen-header",
+                option,
+                filename,
+                tmptlstr,
+            ],
+        )
+
+        assert result.exit_code == 0
+        assert Path(filename).exists()
+
+
+def test_gen_tl_with_fdt_header(tmptlstr, tmpfdt):
+    tlcrunner = CliRunner()
+
+    with tlcrunner.isolated_filesystem():
+        tlcrunner.invoke(cli, ["create", "--size", 1000, "--fdt", tmpfdt, tmptlstr])
+
+        result = tlcrunner.invoke(
+            cli,
+            [
+                "gen-header",
+                tmptlstr,
+            ],
+        )
+
+        assert result.exit_code == 0
+        assert Path("header.h").exists()
+
+        with open("header.h", "r") as f:
+            dtb_match = search(r"DTB_OFFSET\s+(\d+)", "".join(f.readlines()))
+            assert dtb_match and dtb_match[1].isnumeric()
+
+
+def test_gen_empty_tl_c_header(tlcrunner, tmptlstr):
+    with tlcrunner.isolated_filesystem():
+        result = tlcrunner.invoke(
+            cli,
+            [
+                "gen-header",
+                tmptlstr,
+            ],
+        )
+
+        assert result.exit_code == 0
+        assert Path("header.h").exists()
+
+        with open("header.h", "r") as f:
+            lines = "".join(f.readlines())
+
+            assert TransferList.hdr_size == int(
+                findall(r"SIZE\s+(0x[0-9a-fA-F]+|\d+)", lines)[0], 16
+            )
+            assert TransferList.version == int(
+                findall(r"VERSION.+(0x[0-9a-fA-F]+|\d+)", lines)[0]
+            )
+
+
 def bytes_to_hex(data: bytes) -> str:
     """Convert bytes to a hex string in the same format as the debugger in
     ArmDS