如何获取为给定目标计算的所有源依赖项 SCons?

How can I get all the source dependencies SCons computed for a given target?

我想在构建给定目标后立即以编程方式执行此操作,在 SCons 构建期间 运行,而不是使用 --tree 或任何其他 scons 命令。我有一个目标节点。它可能有一些明确的依赖关系、使用过的扫描仪、基于文件扩展名的扫描仪,以及 SCons 计算的任何其他东西。所以喜欢:

all_source_nodes = tgt_node.get_all_sources(...)

我搜索了文档和 API。只在 FS 节点上看到 get_stored_implicit。我得到 None 以及 .prerequisites.implicit 节点成员。

我还发现 .sources 节点成员显示了传递到构建器中的直接源。当然,这也不够,因为我基本上需要依赖子树的所有节点,这要多得多。

在处理 SConstruct/SConscript 时,您不会获得大部分信息。

之后依赖关系图被完全填充。

您可能可以使用 sconsign。 它会在 SCons 完成后读取构建数据库,您可以查询特定目标。

尤里卡! :) 构建信息数据和递归扫描器应用程序的组合为我提供了所有 scons --tree=prune 报告。下面是完整代码,以及一些信息性消息。

__FULL_DEPS = {}    # all dependencies for the node and its children recursively
__DIRECT_DEPS = {}  # only the dependencies obtained from SCons for the node itself (direct and scanned)

# Find scons deps for this target - these are all in the subtree (same as __FULL_DEPS[tgt])
scons_deps = find_deps(tgt, None, env)
tgt_deps = set(map(str, scons_deps))    # Some nodes refer to the same paths

def find_deps(node, children_func, env):
    """Recursively traverses children dependencies from build info and by applying 
children_func (scanner) to collect the full set of all dependencies 
starting with 'node' as the dependency tree root.
:Parameters:
    - node          the current target we check (root node of the sub-tree)
    - children_func the function called to get the children of a node. May be None.
    - env           the current environment
:Return:
    The set of all source nodes in the dependency sub-tree
    While the node objects are unique, the underlying files/dirs may repeat on rare occasions.
    Collects the global __DIRECT_DEPS and __FULL_DEPS caches
    """
    if node in __FULL_DEPS:
        print("_find_deps '{}' deps - from CACHE: {}."
                            .format(str(node), len(__FULL_DEPS[node])))
        return __FULL_DEPS[node]
    children = set(node.all_children())  # children may repeat
    
    # Apply children func, if available and merge in the results
    scanned_deps = set()
    if children_func:
        scanned_deps = set(children_func(node))
        children |= scanned_deps
    __DIRECT_DEPS[node] = children

    # Iterate all the unvisited children and recursively call find_deps with children_func 
    # specific to each child.
    # FYI: Scanner use code is based on SCons code in Node.render_include_tree()
    subtree_deps = set()
    for child in sorted(children):
        scanner = node.get_source_scanner(child)
        # Note: get_build_scanner_path fails on nodes without executor
        scanner_path = node.get_build_scanner_path(scanner) if (scanner and node.get_executor()) else None
        def children_func(node, env=env, scanner=scanner, path=None):
            return node.get_found_includes(env, scanner, path)
    
        subtree_deps |= find_deps(child, children_func, env)
        __FULL_DEPS[node] = children | subtree_deps

    print("_find_deps '{}' deps: {} children ({} scanned) + {} from sub-tree"
        .format(str(node), len(children), len(scanned_deps), len(subtree_deps)))

return __FULL_DEPS[node]

在高性能机器上大约一秒钟内获得 700 多个依赖项。这应该在 post-action 中调用,即在构建节点之后。在构建节点之前调用时可能仍然可以正常工作。