【问题标题】:Is there a build tool based on inotify-like mechanism有没有基于类似inotify机制的构建工具
【发布时间】:2011-02-24 20:35:31
【问题描述】:

在使用普通旧 make 的相对较大的项目中,即使在没有任何变化的情况下构建项目也需要几十秒。尤其是对make -C 的多次执行,这会产生新的进程开销。

这个问题的明显解决方案是基于操作系统的inotify-like 功能的构建工具。它会查看某个文件何时更改,并根据该列表单独编译该文件。

那里有这样的机器吗?开源项目的奖励积分。

【问题讨论】:

    标签: build makefile inotify


    【解决方案1】:

    你的意思是Tup:

    从主页:

    “Tup 是一个基于文件的构建系统 - 它输入文件更改列表和有向无环图 (DAG),然后处理 DAG 以执行更新依赖文件所需的适当命令。DAG 存储在SQLite 数据库。默认情况下,文件更改列表是通过扫描文件系统生成的。或者,可以通过运行包含的文件监视器守护进程预先提供该列表。"

    【讨论】:

    • 我想知道,当你编辑一个文件并保存它的不完整版本时会发生什么。应该是每次保存都会触发Tup编译吧?
    • Tup 仅在您询问时编译,但它始终监视文件系统的更改。
    • tup monitor -a - 到目前为止,看起来不错 - 相关文件已编译并且程序已准备就绪,我什至还没来得及切换到终端:-D
    【解决方案2】:

    我只是想知道是不是 stat()ing 需要这么长时间的文件。要检查这里,我编写了一个小的 systemtap 脚本来测量 stat() 文件所需的时间:

    # call-counts.stp
    
    global calls, times
    
    probe kernel.function(@1) {
        times[probefunc()] = gettimeofday_ns()
    }
    
    probe kernel.function(@1).return {
        now = gettimeofday_ns()
        delta = now - times[probefunc()]
        calls[probefunc()] <<< delta
    }
    

    然后像这样使用它:

    $ stap -c "make -rC ~/src/prj -j8 -k" ~/tmp/count-calls.stp sys_newstat
    make: Entering directory `/home/user/src/prj'
    make: Nothing to be done for `all'.
    make: Leaving directory `/home/user/src/prj'
    calls["sys_newstat"] @count=8318 @min=684 @max=910667 @sum=26952500 @avg=3240
    

    我运行它的项目有 4593 个源文件,make 需要大约 27 毫秒(以上为 26952500 纳秒)来统计所有文件以及相应的 .d 文件。不过我使用的是非递归make。

    【讨论】:

    • 您可能是正确的,这可能是一个过早的优化。顺便说一句,缓存很热吗?看一个有 100,000 的项目很有趣,看看你的统计时间是否按比例缩放。请注意,对于真正的大型项目,非递归 make 很多时候不是一个选项。 +1 花时间进行分析。
    • 文件系统缓存很热(我编译的工作站有 12Gb 的 RAM)。它位于本地 ext3 文件系统上。不过,在 NFS 上构建时,stat() 文件需要更长的时间。
    • tup 的作者,用几秒钟的时间来制作一个包含 100k 个文件的未更改项目gittup.org/tup/make_vs_tup.html
    • 有趣。我想知道测试中的文件系统是否挂载了noatime
    • ninja 的作者同意inotify 不值得麻烦。 “inotify。我原本打算让 Ninja 驻留在内存中,并使用 inotify 来始终保持构建状态热。但是在编写代码时,我发现它足够快,每次都可以从头开始运行。也许是一台较慢的计算机仍然会从 inotify 中受益;数据结构的设置使得使用 inotify 应该不难。” martine.github.com/ninja/manual.html
    【解决方案3】:

    如果你使用的是 OSX,你可以使用fswatch

    https://github.com/alandipert/fswatch

    以下是如何使用 fswatch 对文件进行更改,然后在检测到任何内容时运行 make

    fswatch -o anyFile | xargs -n1 -I{} make
    

    您可以像这样从 makefile 中运行 fswatch:

    watch: $(FILE)
      fswatch -o $^ | xargs -n1 -I{} make
    

    (当然,$(FILE) 是在 makefile 中定义的。) make 现在可以像这样监视文件中的更改:

    > make watch
    

    你可以像这样观看另一个文件:

    > make watch anotherFile
    

    【讨论】:

      【解决方案4】:

      安装inotify-tools 并写几行bash 以在某些目录更新时调用make

      附带说明一下,递归 make 的扩展性很差并且容易出错。首选non-recursive make

      【讨论】:

      • 嗯?调用 make 会触及项目中的每个文件。我不想那样做!最重要的一点是永远不要触及所有文件,即使是为了获得它们的修改时间。
      • @Elazar Leibovich:make 不会触及任何文件。它统计源文件以查看是否需要重建目标。
      • Maxim,我不知道你怎么样,但我认为stating 一个文件,触摸它。而且,如果您需要在每次执行单个 make 时统计 100,000 个文件,则可能需要一段时间(其中 99.9% 的文件对您来说并不有趣,您已经将它们编译为 libsomething.so,这就是您所需要的全部)。
      • 哦,你指的是文件访问时间吗?这个功能是完全有缺陷的,对于每次读取,您都在安装文件系统时使用-noatime。请参阅thunk.org/tytso/blog/2009/03/01/… 了解更多信息。
      • @Maxim,这是一个不错的技巧,但我的方法保证将开销减少到 99.9%(访问一个更改的文件,而不是其他 100,000 个文件,inotify-make 将忽略它们) .
      【解决方案5】:

      您描述的变更依赖已经是 Make 的一部分,但是 Make 足够灵活,可以以低效的方式使用。如果缓慢确实是由递归(make -C 命令)引起的——它可能是——那么你应该减少递归。 (您可以尝试放入自己的条件逻辑来决定是否执行make -C,但这将是一个非常不雅的解决方案。)

      粗略地说,如果你的 makefile 看起来像这样

      # main makefile
      
      foo:
          make -C bar baz
      

      还有这个

      # makefile in bar/
      
      baz: quartz
          do something
      

      你可以把它们改成这样:

      # main makefile
      
      foo: bar/quartz
          cd bar && do something
      

      有很多细节需要修改,但现在如果bar/quartz 没有更改,foo 规则将不会运行。

      【讨论】:

      • 这是一种妥协。采用你的方法会给我一个巨大的、臃肿的makefile,它更快。我想要我的蛋糕,也想吃。这可以通过 inotify 魔法实现。切勿触摸任何未修改的文件,即使是在修改日期也不行。
      猜你喜欢
      • 1970-01-01
      • 2013-01-16
      • 2018-03-16
      • 2011-04-02
      • 2014-01-30
      • 1970-01-01
      • 1970-01-01
      • 2011-05-07
      • 1970-01-01
      相关资源
      最近更新 更多