Dependencies 即使文件已更改,自定义SCons生成器也会将依赖项报告为当前依赖项

Dependencies 即使文件已更改,自定义SCons生成器也会将依赖项报告为当前依赖项,dependencies,build-automation,scons,Dependencies,Build Automation,Scons,我有一个自定义生成器,它使用一个工具来使用源代码和带有标记文档的文本文件构建文档 该工具采用一个配置文件,指定所有输入文件和输出选项 运行时,它会在标记为html的文件夹中生成文档 我的生成器有一个扫描仪来查找所有输入文件 和一个发射器来设置输出目录 扫描仪和发射器可以找到所需的所有文件。但是,当我重建时,它不会检测到输入文件的更改 我已经生成了一个生成器,它复制了问题,并将以下内容放在一个目录中: gen_doc.py import SCons.Builder import os import

我有一个自定义生成器,它使用一个工具来使用源代码和带有标记文档的文本文件构建文档

该工具采用一个配置文件,指定所有输入文件和输出选项

运行时,它会在标记为html的文件夹中生成文档

我的生成器有一个扫描仪来查找所有输入文件

和一个发射器来设置输出目录

扫描仪和发射器可以找到所需的所有文件。但是,当我重建时,它不会检测到输入文件的更改

我已经生成了一个生成器,它复制了问题,并将以下内容放在一个目录中:

gen_doc.py

import SCons.Builder
import os
import ConfigParser

def _doc_build_function(target, source, env):
    #print '***** Builder *****'
    config = ConfigParser.SafeConfigParser()
    try:
        fp = open(str(source[0]), 'r')
        config.readfp(fp)
    finally:
        fp.close()
    output_dir = ''
    if config.has_option('output_options', 'output_dir'):
        output_dir = config.get('output_options', 'output_dir')
    input_files = []
    if config.has_option('input_options', 'input'):
         input_files = config.get('input_options', 'input').split()
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    with open(output_dir + os.sep + 'index.html', 'wb') as out_file:
        for file in input_files:
            try:
                in_file = open(file, 'r')
                out_file.write(in_file.read())
            finally:
                in_file.close()


def _doc_scanner(node, env, path):
    source = []
    config = ConfigParser.SafeConfigParser()
    try:
        fp = open(str(node), 'r')
        config.readfp(fp)
    finally:
        fp.close()
    if config.has_option('input_options', 'input'):
        for i in config.get('input_options', 'input').split():
            source.append(os.path.abspath(i))
    return source

def _doc_emitter(target, source, env):
    target = []
    config = ConfigParser.SafeConfigParser()
    try:
        fp = open(str(source[0]), 'r')
        config.readfp(fp)
    finally:
        fp.close()
    if config.has_option('output_options', 'output_dir'):
        target.append(env.Dir(os.path.abspath(config.get('output_options', 'output_dir'))))
        env.Clean(source, env.Dir(os.path.abspath(config.get('output_options', 'output_dir'))))

    return target, source


def generate(env):
    doc_scanner = env.Scanner(function = _doc_scanner)

    doc_builder = SCons.Builder.Builder(
        action = _doc_build_function,
        emitter = _doc_emitter,
        source_scanner = doc_scanner,
        single_source = 1
    )

    env.Append(BUILDERS = {
        'gen_doc': doc_builder,
    })

def exists(env):
    '''Using internal builder'''
    return True
SConstruct

env = Environment()
env.Tool('gen_doc', toolpath=['.'])
env.gen_doc('config_doc')
config\u doc

[input_options]
input = a.md b.md

[output_options]
output_dir = html
a.md

Hello
 world
b.md

Hello
 world
当我运行它时,它会产生正确的输出

文件夹
html
中的文件,文件名为'index.html'

用单词
Hello world

当我跑的时候

scons -n tree=status html
我得到以下信息

scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: `html' is up to date.
 E         = exists
  R        = exists in repository only
   b       = implicit builder
   B       = explicit builder
    S      = side effect
     P     = precious
      A    = always build
       C   = current
        N  = no clean
         H = no cache

[E B   C  ]+-html
[E     C  ]  +-config_doc
[E     C  ]  +-a.md
[E     C  ]  +-b.md
scons: done building targets.
我进入并修改b.md文件,然后重新运行

scons -n tree=status html
输出是相同的,它仍然将b.md报告为当前,因此不会重建文档

有没有办法让SCON看到扫描程序看到的源文件的更改,并在文件更改时重新生成

更新

我做了一个小游戏,我创建了一个虚拟决策器,看看我是否能找到为什么没有添加这些文件

def foo(dependency, target, prev_ni):
    print 'dependency = %s' % (dependency)
    print 'target = %s' % (target)
    return True
在“生成(环境)”中,我添加了行“环境决策器(foo)”

由_doc_scanner添加到树中的文件没有调用Decider函数,因此永远不会计算MD5哈希

我该怎么做才能让这些文件称为决策者

更新2:

制作帖子时忘记添加发射器的返回值

更新3


修改代码,使其不再调用外部生成器。现在,它调用一个内部生成器函数来模拟生成器。这只是模拟外部生成器的行为。原始生成器操作是
action='cd${SOURCE.dir}&&gen_docs${SOURCE.file}

发射器未返回修改的目标源列表

有关更多信息,请参阅:

emitter函数应返回已修改的目标列表 应建立的目标和将建立目标的来源


这是由我认为sCONS中的设计错误引起的:如果目录存在,目录节点总是被认为是最新的。 以下文件的相关章节:

为什么我的目录只在第一次更新?

与其他所有构建系统一样,SCons认为用作目标的目录是最新的(如果存在)。第一次构建时,目录不在那里,因此SCons运行update命令。此后的每一次,目录都已经存在,因此SCons认为它是最新的

你可以解决这个问题,尽管这有点痛苦。对于要参与依赖关系图的每个目录,需要创建一个“表示”该目录的虚拟文件。无论何时生成目录,都要写入文件。依赖于文件而不是目录

您的代码可以更新以执行此操作,因此:

导入SCons.Builder
导入操作系统
导入配置分析器
导入日期时间
def_清单(目标):
返回os.path.join('.manifest',str(目标))
def_触摸屏(路径):
dirname=os.path.dirname(路径)
如果不存在os.path.exists(目录名):
os.makedirs(dirname)
打开(路径“wt”)作为f:
f、 写入(str(datetime.datetime.now())
定义文档构建功能(目标、源、环境):
#打印“******生成器******”
config=ConfigParser.SafeConfigParser()
尝试:
fp=开放(str(源代码[0]),'r')
config.readfp(fp)
最后:
fp.close()
输出_dir=''
如果config.has_option('output_options','output_dir'):
output\u dir=config.get('output\u options','output\u dir')
输入_文件=[]
如果config.has_option('input_options','input'):
input\u files=config.get('input\u options','input').split()
如果操作系统路径不存在(输出目录):
os.makedirs(输出目录)
打开(output_dir+os.sep+'index.html','wb')作为输出文件:
对于输入_文件中的文件:
尝试:
in_file=open(文件'r')
out\u file.write(in\u file.read())
最后:
in_file.close()中
对于目标中的t:
_触摸(_manifest(t))
def_doc_扫描仪(节点、环境、路径):
来源=[]
config=ConfigParser.SafeConfigParser()
尝试:
fp=打开(str(节点),'r')
config.readfp(fp)
最后:
fp.close()
如果config.has_option('input_options','input'):
对于config.get('input_options','input').split()中的i:
source.append(os.path.abspath(i))
返回源
def_doc_发射器(目标、源、环境):
目标=[]
config=ConfigParser.SafeConfigParser()
尝试:
fp=开放(str(源代码[0]),'r')
config.readfp(fp)
最后:
fp.close()
如果config.has_option('output_options','output_dir'):
append(env.Dir(os.path.abspath(config.get('output\u options','output\u Dir')))
env.Clean(source,env.Dir(os.path.abspath(config.get('output\u options','output\u Dir')))
扩展(映射(_清单,目标))
返回目标、源
def生成(环境):
doc\u scanner=env.scanner(函数=\u doc\u scanner)
doc_builder=SCons.builder.builder(
action=\u doc\u build\u函数,
发射器=_doc_发射器,
source\U scanner=文档\U scanner,
单源=1
)
环境附加(生成器={
“gen_doc”:文档生成器,
})
def存在(环境):
“使用内部生成器”
返回真值

我提供的代码你是对的。这是重新处理问题的错误。(正如我在便条中所说)我的原创