[patch] Parsing of Xilinx Vivado XCI files

I must first apologize that as a new user I’m unable to upload a file apparently, so instead the contents are pasted at the end of this message.

Please find attached a parser for Xilinx XCI files, this will locate the module type name from the XCI file and mark the file as providing the given module name. The xml path that is used has been found by observation, and seems at least to hold for IP generated in Vivado versions 2017.3, 2018.2 & 2018.3.

From 8f6cb33e3178b290b8ab4be521d24c5e15f14971 Mon Sep 17 00:00:00 2001
From: Nick Brereton 
Date: Thu, 2 May 2019 23:23:53 -0400
Subject: [PATCH] Add parsing of XCI files to locate Vivado IP module names.

---
 hdlmake/srcfile.py      |  7 ++++--
 hdlmake/tools/vivado.py | 11 +++++++--
 hdlmake/xci_parser.py   | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 73 insertions(+), 4 deletions(-)
 create mode 100644 hdlmake/xci_parser.py

diff --git a/hdlmake/srcfile.py b/hdlmake/srcfile.py
index 150b980..63264ed 100644
--- a/hdlmake/srcfile.py
+++ b/hdlmake/srcfile.py
@@ -194,10 +194,13 @@ class VEOFile(File):
     pass


-class XCIFile(File):
+class XCIFile(SourceFile):
     """Xilinx Core IP File"""
-    pass

+    def __init__(self, path, module, library=None):
+        SourceFile.__init__(self, path=path, module=module, library=library)
+        from hdlmake.xci_parser import XCIParser
+        self.parser = XCIParser(self)

 XILINX_FILE_DICT = {
     'xise': XISEFile,
diff --git a/hdlmake/tools/vivado.py b/hdlmake/tools/vivado.py
index 2637a04..0e59e06 100644
--- a/hdlmake/tools/vivado.py
+++ b/hdlmake/tools/vivado.py
@@ -26,7 +26,8 @@

 from __future__ import absolute_import
 from .xilinx import ToolXilinx
-from hdlmake.srcfile import (XDCFile, XCIFile, NGCFile, XMPFile,
+from hdlmake.srcfile import (VHDLFile, VerilogFile, SVFile,
+                             XDCFile, XCIFile, NGCFile, XMPFile,
                              XCOFile, COEFile, BDFile, TCLFile, BMMFile,
                              MIFFile, RAMFile, VHOFile, VEOFile, XCFFile)

@@ -48,7 +49,6 @@ class ToolVivado(ToolXilinx):
     SUPPORTED_FILES = {
          XDCFile: ToolXilinx._XILINX_SOURCE,
          XCFFile: ToolXilinx._XILINX_SOURCE,
-         XCIFile: ToolXilinx._XILINX_SOURCE,
          NGCFile: ToolXilinx._XILINX_SOURCE,
          XMPFile: ToolXilinx._XILINX_SOURCE,
          XCOFile: ToolXilinx._XILINX_SOURCE,
@@ -61,6 +61,12 @@ class ToolVivado(ToolXilinx):
          VHOFile: ToolXilinx._XILINX_SOURCE,
          VEOFile: ToolXilinx._XILINX_SOURCE}

+    HDL_FILES = {
+        VHDLFile:    ToolXilinx._XILINX_SOURCE,
+        VerilogFile: ToolXilinx._XILINX_SOURCE,
+        SVFile:      ToolXilinx._XILINX_SOURCE,
+        XCIFile:     ToolXilinx._XILINX_SOURCE}
+
     CLEAN_TARGETS = {'clean': [".Xil", "*.jou", "*.log", "*.pb", "*.dmp",
                                "$(PROJECT).cache", "$(PROJECT).data", "work",
                                "$(PROJECT).runs", "$(PROJECT).hw",
@@ -79,3 +85,4 @@ class ToolVivado(ToolXilinx):
         self._standard_libs.extend(ToolVivado.STANDARD_LIBS)
         self._clean_targets.update(ToolVivado.CLEAN_TARGETS)
         self._tcl_controls.update(ToolVivado.TCL_CONTROLS)
+        self._hdl_files.update(ToolVivado.HDL_FILES)
diff --git a/hdlmake/xci_parser.py b/hdlmake/xci_parser.py
new file mode 100644
index 0000000..cc27fcc
--- /dev/null
+++ b/hdlmake/xci_parser.py
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+#
+# Author: Nick Brereton
+#
+# This file is part of Hdlmake.
+#
+# Hdlmake is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Hdlmake is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Hdlmake.  If not, see .
+#
+
+"""This module provides a Xilinx XCI IP description parser for HDLMake"""
+
+from __future__ import absolute_import
+import re
+import logging
+
+from xml.etree import ElementTree as ET
+
+from .new_dep_solver import DepParser
+from .dep_file import DepRelation
+from hdlmake.srcfile import create_source_file
+
+class XCIParser(DepParser):
+    """Class providing the Xilinx XCI parser"""
+
+    def __init__(self, dep_file):
+        DepParser.__init__(self, dep_file)
+
+    def parse(self, dep_file):
+        """Parse a Xilinx XCI IP description file to determine the provided module(s)"""
+        if dep_file.is_parsed:
+            return
+        logging.debug("Parsing %s", dep_file.path)
+
+        with open(dep_file.path) as f:
+            # extract namespaces with a regex -- not really ideal, but without pulling in
+            # an external xml lib I can't think of a better way.
+            xmlnsre = re.compile(r'''\bxmlns:(\w+)\s*=\s*"(\w+://[^"]*)"''', re.MULTILINE)
+            xml = f.read()
+            nsmap = dict(xmlnsre.findall(xml))
+            value = ET.fromstring(xml).find('spirit:componentInstances/spirit:componentInstance/spirit:instanceName', nsmap)
+            if not value is None:
+                modulename = value.text
+                logging.debug("found module %s.%s", dep_file.library, modulename)
+                dep_file.add_relation(
+                    DepRelation("%s.%s" % (dep_file.library, modulename),
+                                DepRelation.PROVIDE, DepRelation.MODULE))
+
+        dep_file.is_parsed = True
-- 
2.5.5

Hello,
this looks ok to me; I will merge it soon.

Tristan.