ruby rake中order_only先决条件和non-order_only先决条件之间的区别

wmomyfyw  于 2023-01-25  发布在  Ruby
关注(0)|答案(2)|浏览(203)

Rake在13.0中增加了order_only特性,这可能是基于GNU Make的order-only prerequisites
question中进一步阐明了有关仅订购前提条件应如何工作的信息。
然而问题是我不知道它在rake中是如何工作的。例如,如果我运行rake install,然后运行rake build,当build不是它的唯一先决条件时,rake install不会再次运行。也许我只是被误导了,或者可能是其他一些因素,如时间戳和needed?方法如何影响它的行为。但是我对进一步研究rake的源代码有点厌倦了。
在合并提交中,仅限顺序的先决条件似乎只是与prerequisites不同地分配,并且没有演示任何其他内容。
无论如何,我希望有人能够详细完整地描述order_only应该如何在rake中工作;显示例子和引用源代码的rake如果可能的话。这将大大帮助我和其他Rakefile作家。

bzzcjhmw

bzzcjhmw1#

在我看来,有人添加了这个,这样你就可以在Rake中构建一些order_only命令,但没有在内置的文件命令中添加对它的支持。它确实在任务中的一个单独的成员变量中存储了order_only先决条件,所以你可以创建自己版本的FileTask,它将像GNU Make的order-only先决条件一样工作。

class OrderOnlyFileTask < Rake::FileTask
  def out_of_date?(stamp)
    all_regular_prerequisite_tasks.any? { |prereq|
      prereq_task = application[prereq, @scope]
      if prereq_task.instance_of?(Rake::FileTask)
        prereq_task.timestamp > stamp || @application.options.build_all
      else
        prereq_task.timestamp > stamp
      end
    }
  end
  
  def all_regular_prerequisite_tasks
    seen = {}
    collect_regular_prerequisites(seen)
    seen.values
  end
  
  def collect_regular_prerequisites(seen)
    regular_prerequisite_tasks.each do |pre|
      next if seen[pre.name]
      seen[pre.name] = pre
      pre.collect_prerequisites(seen)
    end
  end
  
  def regular_prerequisite_tasks
    prerequisites.map { |pre| lookup_prerequisite(pre) }
  end
end   

file 'foo/created.txt' do
  mkdir 'foo'
  touch 'foo/created.txt'
end

file 'source.txt' do
  touch 'source.txt'
end

OrderOnlyFileTask.define_task('foo/built.txt' => ['source.txt'], order_only: ['foo/created.txt']) do
  touch 'foo/built.txt'
end

task :build => 'foo/built.txt' do
  puts 'build'
end

如果我从头开始运行rake build,我会得到以下输出:

% rake build
touch source.txt
mkdir foo
touch foo/created.txt
touch foo/built.txt
build

如果我再运行一次:

% rake build
build

如果我更新foo/created.txt上的mtime:

% touch foo/created.txt
% rake build
build

如果我修改源文件:

% touch source.txt
% rake build
touch foo/built.txt
build

如果我删除foo目录:

% rm -rf foo
% rake build
mkdir foo
touch foo/created.txt
touch foo/built.txt
build

如果您将OrderOnlyFileTask更改为具有foo/created.txt的常规先决条件,如下所示:

OrderOnlyFileTask.define_task('foo/built.txt' => ['source.txt', 'foo/created.txt']) do
  touch 'foo/built.txt'
end

然后,当点击foo/created.txt时,您会得到不同的行为:

% touch foo/created.txt
% rake build
touch foo/built.txt
build

我在这里将依赖关系设置为一个文件(foo/created.txt),而不是像GNU Make示例中那样设置为一个目录,因为Rake中的目录任务不像make中的目录依赖关系那样检查目录的mtime。
因此,通过任务的order_only选项,您可以构建自己的Rake任务,这些任务在决定是否执行时以不同的方式处理order_only先决条件。您只需在任务中定义needed?,以按照您的意愿处理先决条件。我在OrderOnlyFileTask中放置的所有方法都是更改FileTask中的needed?方法以忽略order_only先决条件。

ftf50wuq

ftf50wuq2#

在某种程度上,rake已经有了订单任务,它们是directory(或者file_create)任务(link):
FileCreationTask是一个文件任务,当且仅当尚未创建该文件时,才需要将其用作依赖项。一旦创建,如果其任何依赖项更新,则不会重新触发它,也不会在更新时触发依赖它的任务的任何重建。
但这是因为它们的时间戳总是小于其他任务的时间戳,而不是因为其他任务可以忽略它(不是因为directory/file_create任务的时间戳在它是一个仅顺序依赖时被忽略)。

directory 'build'
file 'build/a' => ['a.c', 'build'] do
  system('gcc a.c -o build/a', exception: true)
end
$ rake -t build/a
** Invoke build/a (first_time)
** Invoke a.c (first_time, not_needed)
** Invoke build (first_time)
** Execute build
mkdir -p build
** Execute build/a

$ touch build
$ rake -t build/a
** Invoke build/a (first_time, not_needed)
** Invoke a.c (first_time, not_needed)
** Invoke build (first_time, not_needed)
file_create 'b' do
  touch 'b'
end
file 'a' => ['a.c', 'b'] do
  system('gcc a.c -o a', exception: true)
end
$ rake -t a
** Invoke a (first_time)
** Invoke a.c (first_time, not_needed)
** Invoke b (first_time)
** Execute b
touch b
** Execute a

$ touch b
$ rake -t a
** Invoke a (first_time, not_needed)
** Invoke a.c (first_time, not_needed)
** Invoke b (first_time, not_needed)

Peter Haight的OrderOnlyFileTask类证明了只有顺序依赖可以被特殊处理(尽管只有类的直接依赖被这样处理,这没关系,因为这显然是一个概念证明),看起来这个特性还没有发布,或者它是某种私有API的一部分。

相关问题