【问题标题】:scons: How to deal with dynamic targets?scons:如何处理动态目标?
【发布时间】:2014-01-28 05:36:31
【问题描述】:

我正在尝试使用sconsPDF 转换为png 文件的工作自动化。我用于转换的工具是convert from ImageMagick

这是原始命令行:

  1. convert input.pdf temp/temp.png
  2. convert temp/*.png -append output.png

第一个命令会为PDF文件的每一页生成一个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执行之前我不知道有多少,所以最后的output.png只包含PDF文件的第一页。

感谢任何提示。

更新:

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

我仍然很好奇如何处理中间临时文件列表事先未知并且需要动态目标列表的情况。

【问题讨论】:

    标签: scons imagemagick-convert


    【解决方案1】:

    如果您想知道如何处理您提出的原始(转换和组合)情况,我建议创建一个带有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)
    

    【讨论】:

    • 你从不在这里定义combine_emitter。你介意把它画出来吗?
    • 另外......更重要的是......我正在尝试类似的东西,但无法让它工作,因为当它到达发射器时,(相当于) pdf 目标尚不存在,因此它无法从该文件中计算任何内容。我认为这种技术的重点是它以某种方式推迟发射器函数的执行,直到所有其他已知输入源都已构建好,以便您可以根据所述输入动态计算目标?据我所知,您所写的内容不能“动态地”表现...如果我遗漏了什么,请告诉我。
    【解决方案2】:

    根据您认为“动态”的条件,我相信正确的答案是:不可能

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

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

    我希望有人在这里证明我错了,但事情就是这样。

    【讨论】:

      【解决方案3】:

      看这个,没有要求保留单个 temp/*png - 如果有,您不应该将它们放在临时目录中,无论如何您都必须做很多事情如果您想确定要生成哪些页面,工作量。

      因此,将其作为一个步骤执行看起来更明智,因此您将拥有类似的东西

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

      convert 的动作函数是 something 像这样:

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

      虽然坦率地说,您可能会更好地将整个内容编写为单个可调用脚本,以确保您的页面排序正确。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多