【问题标题】:How can I make a library dependency graph with waf?如何使用 waf 制作库依赖关系图?
【发布时间】:2022-05-05 02:52:56
【问题描述】:

我想在使用 waf 构建 C++ 项目时生成一个简单的 DOT 文件。理想情况下,我只想使用bld 命令的usetarget 属性来生成文件。这很容易注入系统吗?

例如这个 wscript 文件(仅提及我想使用的部分)

def build(bld):
    bld( use = [ 'lib1',
                 'lib2', ] ,
         target = 'lib3' )

会产生输出

lib3 -> lib1
lib3 -> lib2

哪里是注入这种行为的最佳位置?

谢谢!

【问题讨论】:

    标签: c++ dependencies waf


    【解决方案1】:

    您可以在构建步骤中通过add_post_fun 轻松添加类似这样的工具,如下所示:

    from waflib.Errors import WafError
    from waflib import Utils
    
    def filter_uses(ctx, uses):
        filtered = []
        for use in uses:
            try:
                ctx.get_tgen_by_name(use)
                filtered.append(use)
            except WafError:
                pass
        return filtered
    
    @Utils.run_once # print only once, even if used in multiple script
    def make_dot_file(ctx):
        for group in ctx.groups:
            for taskgen in group:
                uses = Utils.to_list(getattr(taskgen, 'use', []))
                uses = filter_uses(ctx, uses) # Optional, only print TaskGens
                try:
                    name = taskgen.name # Sometimes this fails, don't know why
                    print "{} -> {}".format(name, ", ".join(uses))
                except AttributeError:
                    pass
    
    
    def build(bld):
        # Build stuff ...
        bld.add_post_fun(make_dot_file)
    

    注意:要获得真正好的输出,可能需要更多过滤

    【讨论】:

      【解决方案2】:

      我根据自己的需要改进和调整了@CK1 的想法。我的解决方案使用 graphviz 生成一个 DAG,并使用来自 Matthias Eisen 的 article 的辅助函数来显示依赖项和目标。

      代码的主要部分如下所示:

      import functools
      import graphviz as gv
      
      from pathlib import Path
      from waflib import Utils
      
      # Make sure that dot.exe is in your system path. I had to do this as
      # Graphviz (the program, not the package) is installed with conda. I am
      # sure there is a proper way to do this with Waf.
      library_bin = Path(sys.executable).parent / 'Library' / 'bin' / 'graphviz'
      os.environ['PATH'] += str(library_bin) + ';'
      
      
      def make_dot_file(ctx):
          # Create DAG
          dag = digraph()
      
          # Loop over task groups
          for group in ctx.groups:
      
              # Loop over tasks
              for taskgen in group:
                  # Get name and add node for task
                  name = taskgen.get_name()
                  add_nodes(dag, [name])
      
                  # Add nodes for dependencies and edges to task
                  deps = Utils.to_list(getattr(taskgen, 'deps', []))
                  for dep in deps:
                      dep = Path(dep).name
                      add_nodes(dag, [dep])
                      add_edges(dag, [(dep, name)])
      
                  # Add nodes for targets and edges to task
                  targets = Utils.to_list(getattr(taskgen, 'target', []))
                  for target in targets:
                      target = Path(target).name
                      add_nodes(dag, [target])
                      add_edges(dag, [(name, target)])
      
          # Make the DAG pretty
          dag = apply_styles(dag, styles)
      
          # Save DAG
          dag.render(<output path of graphic>)
      
      def build(bld):
          # Build stuff ...
          bld.add_post_fun(make_dot_file)
      

      此示例使用的辅助函数在这里:

      # -------------------- Start helper functions ----------------------------
      graph = functools.partial(gv.Graph, format='png')
      digraph = functools.partial(gv.Digraph, format='png')
      
      styles = {
          'graph': {
              'label': 'Pretty Graph',
              'fontsize': '16',
              'fontcolor': 'white',
              'bgcolor': '#333333',
              'rankdir': 'BT',
          },
          'nodes': {
              'fontname': 'Helvetica',
              'shape': 'hexagon',
              'fontcolor': 'white',
              'color': 'white',
              'style': 'filled',
              'fillcolor': '#006699',
          },
          'edges': {
              'style': 'dashed',
              'color': 'white',
              'arrowhead': 'open',
              'fontname': 'Courier',
              'fontsize': '12',
              'fontcolor': 'white',
          }
      }
      
      
      def apply_styles(graph, styles):
          graph.graph_attr.update(
              ('graph' in styles and styles['graph']) or {}
          )
          graph.node_attr.update(
              ('nodes' in styles and styles['nodes']) or {}
          )
          graph.edge_attr.update(
              ('edges' in styles and styles['edges']) or {}
          )
          return graph
      
      
      def add_nodes(graph, nodes):
          for n in nodes:
              if isinstance(n, tuple):
                  graph.node(n[0], **n[1])
              else:
                  graph.node(n)
          return graph
      
      
      def add_edges(graph, edges):
          for e in edges:
              if isinstance(e[0], tuple):
                  graph.edge(*e[0], **e[1])
              else:
                  graph.edge(*e)
          return graph
      # ----------------------- End helper functions -----------------------------
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-11-03
        • 2011-06-14
        • 1970-01-01
        • 1970-01-01
        • 2022-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多