scons:如何处理动态目标?

时间:2014-01-28 05:36:32

标签: scons imagemagick-convert

我正在尝试使用PDF自动执行将png转换为scons文件的工作。我转换时使用的工具是来自convert的{​​{1}}。

这是原始命令行:

  1. ImageMagick
  2. convert input.pdf temp/temp.png
  3. 第一个命令将为PDF文件中的每个页面生成一个PNG文件,因此第一个命令的 target 是一个动态文件列表。

    这是我正在处理的convert temp/*.png -append output.png文件:

    SConstruct

    代码convert = Builder(action=[ Delete("${TARGET.dir}"), Mkdir("${TARGET.dir}"), "convert $SOURCE $TARGET"]) combine = Builder(action="convert $SOURCE -append $TARGET") env = Environment(BUILDERS={"Convert": convert, "Combine": combine}) pdf = env.PDF("input.tex") pngs = env.Convert("temp/temp.png", pdf) # I don't know how to specify target in this line png = env.Combine('output.png', pngs) Default(png) 实际上是错误的,因为目标是多个文件,我不知道执行pngs = env.Convert("temp/temp.png", pdf)之前有多少文件,因此最终的env.Convert只包含第一页PDF文件。

    任何提示都表示赞赏。

    更新:

    我刚刚发现我可以使用命令output.png来避免两步转换。

    我仍然很好奇如何预先知道中间临时文件列表并且需要动态目标列表时如何处理场景。

3 个答案:

答案 0 :(得分:2)

如果您想知道如何处理您提出的原始(转换和合并)情况,我建议您使用SCons Emitter创建一个构建器。发射器允许您修改源文件和目标文件的列表。这适用于生成的文件,它们不存在干净的构建。

正如您所提到的,转换步骤将生成多个目标,诀窍是您需要能够根据源“计算”发射器中的那些目标。例如,最近我创建了一个wsdl2java构建器,并且能够在发射器中进行一些简单的wsdl解析,以计算要生成的所有目标java文件(源是wsdl)。

以下是构建脚本应该是什么样子的一般概念:

def convert_emitter(source, target, env):
    # both and source and target will be a list of nodes
    # in this case, the target will be empty, and you need
    # to calculate all of the generated targets based on the
    # source pdf file. You will need to open the source file 
    # with standard python code. All of the targets will be
    # removed when cleaned (scons -c)
    target = [] # fill in accordingly
    return (target, source)

# Optionally, you could supply a function for the action
# which would have the same signature as the emitter
convert = env.Builder(emitter=convert_emitter,
                      action=[
                         Delete("temp"),
                         Mkdir("temp"),
                         "convert $SOURCE $TARGET"])
env.Append(BUILDERS={'Convert' : convert})

combine = env.Builder(action=convert_action, emitter=combine_emitter)
env.Append(BUILDERS={'Combine' : combine})

pdf = env.PDF('input.tex')
# You can omit the target in this call, as it will be filled-in by the emitter
pngs = env.Convert(source=pdf)
png = env.Combine(target='output.png', source=pngs)

答案 1 :(得分:1)

根据您的“动态”标准,我相信正确的答案是:不可能

只要运行SCons时,您想要“动态”计算目标集的源,@ Brady的解决方案就可以正常工作。但是,如果有问题的源本身是某个其他命令的目标,它将工作。这是SCons的一个基本限制,因为它假设可以从输入(非中间)源的基本集合静态地确定构建目标集。它在一次扫描中运行并计算构建/目标/依赖关系图,然后在下一次扫描中执行它。它无法运行构建图的某些已知部分,停止内省某些中间目标以动态计算构建图的其余部分,然后继续。在坦克斯的工作中我坦率地喜欢这种能力,但我担心这只是一个基本限制。

您可以做的最好的事情是设置构建,以便在第一次运行时,它在构造PDF时停止(如果在执行构建脚本时不存在PDF目标)。构建PDF后,您可以重新运行构建并进行设置,以便其余构建步骤基于上次运行构建的PDF执行。这或多或少的工作正常......除了一个问题。如果PDF最终更改(并且例如生成一些新页面),您实际上必须重新运行构建两次才能捕获对PDF的更改,因为任何页面计数(等)都将基于旧版本PDF格式。

我喜欢有人在这里证明我的错,但这就是事情的方式。

答案 2 :(得分:0)

看看这个,不需要保留单独的temp / * png - 如果有的话,你不应该将它们放在临时目录中,无论如何你不得不做很多事情如果你想弄清楚要生成哪些页面,那就是工作。

因此,将此作为一步看起来更为明智,这就是你有这样的事情

png = env.Convert('output.png', 'input.pdf')

其中转换的动作函数是某事,如下所示:

Delete('temp'),
Mkdir('temp'),
'convert $SOURCE temp/$TARGET',
'for i in temp/*png; do convert $TARGET temp/$i',
Delete('temp')

虽然坦率地说你可以把整个事情写成一个可调用的脚本来做得更好,以确保你的页面排序正确。