VUnit / vunit

VUnit is a unit testing framework for VHDL/SystemVerilog
http://vunit.github.io/
Other
722 stars 258 forks source link

Dependency generation and output #67

Closed joshrsmith closed 9 years ago

joshrsmith commented 9 years ago

VUnit already has baked in support for managing VHDL projects (collections of files), as well as parsing VHDL enough to generate dependency graphs. It would be useful to be able to obtain a compile-order dependency tree from VUnit, without taking further action (compilation, simulation, etc). It would be reasonable to assume that you don't need a simulator at all to do this. All the functionality is already baked into VUnit.

Possible use cases:

  1. Output all project files in compile order
  2. Output only project files that a specified file / design unit / node depends on, in compile order

Output could be directed to a user specified file. Outputting the library that each file belongs to would also be helpful.

This ordered output file would be generic enough for someone to use automation for other purposes, other than unit testing via VUnit.

I recognize this might run orthogonal to the core goals of VUnit, so perhaps it should only be considered as an ancillary utility (alternate entry point) that uses vunit library functionality.

LarsAsplund commented 9 years ago

I've thought about this as well. Seeing a dependency graph can be a way to understand the structure of the code. That can help you understand what needs to be improved, dependencies that can/should be removed and so on. What is your story? I'm interested too see if that story is something that VUnit should support rather than just the intermediate step.

kraigher commented 9 years ago

I can agree that having a flag to dump the project hierarchy and dependencies as a means of debugging via the main VUnit command line interface would be useful. For more advanced uses I such as further scripting based on the dependency graph I would prefer users to use VUnit as a library. Using it as a library is already possible, the relevant module is vunit.project which has a Project class. I know of at least one guy #27 that already uses project.py/Project as a library.

I will just note that our current dependency mechanism can determine the dependencies of just a single file but not, at least in theory, the dependencies of a single design unit. Since we do not create complete parse trees but rather just extract fragments such as entity instantiations we do for instance not currently keep track of which architecture body in a file (out of many) that an entity instantiation belongs to. This is a simplification that works for our needs. A good design practice is to just have one design unit per file though and then this simplification will not have any effect.

LarsAsplund commented 9 years ago

Maybe we can make the dump using some highly readable but yet defined graph format. An example is the trivial graph format looking like this.

1 vhdl/foo/src/bar.vhd
2 vhdl/spam/src/eggs.vhd
3 vhdl/baz/src/qux.vhd
#
1 2 vhdl/foo/src/bar.vhd depends on vhdl/spam/src/eggs.vhd
3 2 vhdl/baz/src/qux.vhd depends on vhdl/spam/src/eggs.vhd

The first "node list" can be in compile order

With a free tool like yEd you can visualize the graph

graph

If you want some other format you simply have to do the transformation yourself (TGF won't make that much harder) or you access the VUnit library directly.

joshrsmith commented 9 years ago

My specific thought and use case, though I am sure there are others, is to be able to generate an ordered list of files for compilation. In the general case, you could have one VUnit project, which defines all the libraries and files in those libraries. Then you can either: 1) Generate a file which is an order list of what files you need to compile in what order, for all files in the project 2) Generate a file which is an ordered list of dependencies of a user specified file. This way you can create a minimal list of things that would need to be compiled in order to compile your user specified file.

To my understanding this is basically what VUnit is doing under the hood, especially if you have incremental compilation turned on. I am just looking for a way to get this basic functionality exposed for general purpose use.

I think the example that @LarsAsplund gives would achieve what I need, except the library that each of the nodes belongs to (according to the project) would also be helpful information. You could go back to the project to determine this, but it would be helpful if it was all in one place. In your example, if you wanted to compile 1 (bar.vhd), you would have to walk the ordered list backwards (file 2, then file 1) in order to compile things in the correct order. The format is not too important to me as long as it is readily parseable for further use by other tools.

LarsAsplund commented 9 years ago

Sorry, my intention was to have a compile order list in the example.

I can see that the complete project compile order/dependency list is something that you can get from the command line. Getting information for one of the files in the project seems a bit more misplaced though. Is this what you really want or is it more that you want to know what needs to be recompiled given the file changes since last compilation?

kraigher commented 9 years ago

My opinion is that the main command line interface should be kept in line with its role. There could be a purpose within that role to having a flag that visualizes project hierarchy and/or dependencies. If that output can be made easily machine readable it could be valuable.

A pure data dump that is not easily human readable would go against the interactive nature of the command line interface. It is not meant to be a general data dumper of various aspects of the project.

I do not think that the kind of output produced by "trival graph format" is a very good visualization of a graph but rather more a data dump of the graph vertices. Thus I would not think that such an output would be suitable to add to the main command line interface.

General purpose access to the project hierarchy and dependencies should go through the Python API which currently is embodied by project.py:Project. We could also expose this API on the "public" VUnit class that users instantiate in their run.py files to make it more convenient.

It is currently possible to add custom command line options to the run.py files enabling user to implement custom flows such as test data generation and synthesis/implementation project generation.

kraigher commented 9 years ago

It is already possible to "privately" access the underlying project.py:Project object via ui._project. The Project object for instance has a get_files_in_compile_order method.


    def get_files_in_compile_order(self, incremental=True):
        """
        Get a list of all files in compile order
        incremental -- Only return files that need recompile if True
        """
joshrsmith commented 9 years ago

I agree that getting the dependency tree for a single file in the project may be a bit misplaced, which is why I wasn't sure if this was a part of the core functionality, or an ancillary tool.

What I really want is a way to figure out the minimal dependencies to compile, in order, given a "top level source file" and a project (which is really just a set of files and the libraries they belong to). If the top level is not specified, then all files in the project would simply be given in proper compilation order. I am not really concerned about incremental compilation, but if that could be achieved in some meaningful way, that could be helpful as well.

This functionality, by itself, has nothing to do with unit testing. But it does represent functionality needed by VUnit. By exposing the functionality, it represents another useful tool to VHDL developers.

Also, I recognize that you can get access to Project directly, but there are some other dependencies which may not be fulfilled -- for example, you may not have a simulator installed. For this functionality, you don't need a simulator, but because of some other assumptions that are already in place, it is required.

kraigher commented 9 years ago

@joshrsmith We could unlike today avoid complaining that there is no simulator unless there is an action performed that would require it such as running tests and compiling. That way you could use your run.py file to build the project and then use the Python API to extract the information you want without having a simulator installed. While I can agree that this process would require some private access at the moment it is still quite possible to achieve with minimal coding effort. It should even be possible to add a method to project.py:Project that extracts all files in compile order that are required to build a specific top level. Such a method would not require much work since the graph is already built. If you are proficient in Python you could maybe add the method yourself (with unit tests of course) and submit a pull request.

LarsAsplund commented 9 years ago

@kraigher Do you have have an idea for what a more visual dependency output should look like?

kraigher commented 9 years ago

@LarsAsplund What I have in mind as a useful output to debug project hierarchy and dependencies would be a listing of all files. For each file it could list; library name, design units within, if it contains a test bench as well as forward/backward dependencies with an annotated reason why they exist. Trying to visualize a "graph" with ascii arrows and similar stuff is not really useful in my opinion:

Example output:

* src/top.vhd
     library: top_lib
     design units:
        - entity top
        - architecture str of top
     backward dependencies:
        - module.vhd (Instantiates)
     forward dependencies:
        None

* src/module.vhd
   library: module_lib
   design units:
      - entity module
      - architecture rtl of module
   backward dependencies:
      None
   forward dependencies:
      - top.vhd (Instantiates)
kraigher commented 9 years ago

@joshrsmith I made a modification so that the no-simulator error is postponed until a simulator is actually required: 4bbd629b1ac86038c47715e0688a27c17631cc63

LarsAsplund commented 9 years ago

We're really talking three different use cases here:

  1. Compile order and dependency listings as a way to integrate VUnit dependency scanning with unsupported tool chains.
  2. Dependency graphs as a way to guide refactoring of working code
  3. Dependency graphs and other structural information as a way to support debug of failing test runs

I agree with you @kraigher that only the third use case is the core business of the command line tool and I can't find any good solution for putting all that structural information you're showing into a readable format which can also be used directly with a graph tool. In fact, I can't think of any ascii format that's really good at showing dependencies regardless if the purpose is refactoring or debugging. I might add such a utility based on the VUnit API but that would be another issue.

I haven't looked into the details of the new pull request other than that it's using the API. I think that is the way to go for this use case as well. I'm not against "data dumping" as such, we already have that when writing the test result into an XUnit XML file. The difference is that interfacing with Jenkins is very much at the core of unit testing.

kraigher commented 9 years ago

@joshrsmith Does the functionality that was just merged to master fulfill your needs so that we can close this issue?

joshrsmith commented 9 years ago

Yes, this gives me the basic access I need to resolve this issue.

On Fri, Aug 28, 2015 at 11:28 AM, kraigher notifications@github.com wrote:

@joshrsmith https://github.com/joshrsmith Does the functionality that was just merged to master fulfill your needs so that we can close this issue?

— Reply to this email directly or view it on GitHub https://github.com/LarsAsplund/vunit/issues/67#issuecomment-135806611.