6

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

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

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

会产生输出

lib3 -> lib1
lib3 -> lib2

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

谢谢!

4

2 回答 2

5

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)

注意:为了获得真正好的输出,一些更多的过滤可能会很有用

于 2015-03-27T11:49:22.330 回答
2

我根据自己的需要改进和调整了@CK1 的想法。我的解决方案生成一个 DAG,并使用Matthias Eisen的这篇文章(不再可用)中的graphviz帮助函数来显示依赖项和目标。代码的主要部分如下所示。

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 -----------------------------
于 2018-03-15T09:00:34.583 回答