1
2
3
4
5
6
| # 打印 Rakefile 所有的任务列表
$ rake -T
# 打印 Rakefile 所有的任务依赖关系
$ rake -P
# 打印调试信息 Rakefile
$ rake -t
|
- 默认情况下,
rake
命令会执行当前目录下的 Rakefile
,也可以指定执行文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
| require 'rake'
require 'rake/clean'
require 'pathname'
Dir.chdir Pathname.getwd()
DIST_PATH=File.join(Pathname.getwd(), 'dist') # set dist path
desc "default task"
task :default => :html # set default task
source_files = Rake::FileList.new("**/*.md", "**/*.markdown") do |fl|
fl.exclude("~*")
fl.exclude(/^scratch\//)
fl.exclude do |f|
`git ls-files #{f}`.empty? # must git add file
end
end
# puts "html in #{source_files.ext(".md")}"
desc "html task"
task :html => source_files.ext(".html")
# multitask :html => source_files.ext(".html") # do multitask
rule ".html" => ".md" do |t|
out_folder=File.join(Pathname.getwd(), 'dist')
if not File.directory?(DIST_PATH)
mkdir_p DIST_PATH
end
sh "file #{t.source}"
out_file=File.join(DIST_PATH, t.name)
msg "do out: #{out_file} in: #{t.source}"
end
desc "clean out folder: outputs"
task :cleanOut do
rm_rf DIST_PATH
end
out_files = Rake::FileList.new("**/*.html", "**/*.mhtml") do |fl|
fl.exclude("~*")
# fl.exclude(/^scratch\//)
# fl.exclude do |f|
# `git ls-files #{f}`.empty?
# end
end
# msg out_files.ext(".html")
CLEAN.include(out_files.ext(".html"))
def msg(text)
puts " -> msg: #{text}"
end
|
1
| $ curl -o Rakefile https://raw.githubusercontent.com/sinlov/rakefile-playground/main/document/Rakefile
|
本教程的实例代码在 https://github.com/sinlov/rakefile-playground
本质上,Rakefile 还是 ruby 脚本,那么肯定包含 ruby 语法,比如打印日志用 puts
Rakefile 在此基础上分几个基本的build规则
- 任务:
task
默认任务为 default,使用 task :[name] do end 来定义一个 name 的任务 - 任务描述:
desc
对任务进行描述 - 命名空间:
namespace
一般用于区分任务组 - 调用任务:
invoke
调用任务
简单实例
1
2
3
4
5
| task default: %w[test]
task :test do
ruby "test/unittest.rb"
end
|
表示为: 默认任务使用 test
任务覆盖,test 任务内容为运行 ruby "test/unittest.rb"
1
2
3
4
5
6
7
| task :default => :html
task :html => %W[ch1.html ch2.html ch3.html]
rule ".html" => ".md" do |t|
sh "file #{t.source}"
sh "pandoc -o #{t.name} #{t.source}"
end
|
表示为: 将对应 markdown 通过 pandoc 编译为 html 文件, 并且如果已经编译过文件则不在编译
Rake::FileList.new()
用来获取目标文件列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| source_files = Rake::FileList.new("**/*.md", "**/*.markdown") do |fl|
fl.exclude("~*")
fl.exclude(/^scratch\//)
fl.exclude do |f|
`git ls-files #{f}`.empty?
end
end
task :html => source_files.ext(".html")
rule ".html" => ".md" do |t|
sh "pandoc -o #{t.name} #{t.source}"
end
rule ".html" => ".markdown" do |t|
sh "pandoc -o #{t.name} #{t.source}"
end
task :default => :html
|
- 扫描当前源码目录中 md 和 markdown 后缀的文件
- 排除
~*
/^scratch\//
, 以及 git ls-files 为空的文件 - 生成对应 html 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| require 'rake'
# open trace rules faster than cli --trace
# Rake.application.options.trace_rules = true
task :default => :html
source_files = Rake::FileList.new("**/*.md") do |fl|
fl.exclude("~*")
fl.exclude(/^scratch\//)
fl.exclude do |f|
`git ls-files #{f}`.empty?
end
end
task :html => source_files.ext(".html")
rule ".html" => ".md" do |t|
sh "pandoc -o #{t.name} #{t.source}"
end
|
- 扫描当前源码目录中 md 和 markdown 后缀的文件
- 排除
~*
/^scratch\//
, 以及 git ls-files 为空的文件 - 生成对应 html 文件
- 特别的,rule
要求生成的每个对应文件,和生成源得一一对应
技巧: 设置 Rake.application.options.trace_rules = true
跟踪构建错误
Rake 修改了 Ruby 的 String 类以便让 FileList 支持特定相同方法
故,Path 有额外的占位符用来快速操作
%p
完整路径%f
没有目录的文件名%n
没有目录和扩展名%d
只有目录%x
只有扩展名%X
排除扩展名
Rakefile 带有常用的文件操作,非常方便操作当前文件
1
2
3
4
5
6
7
8
9
10
11
| # 加入目录
directory [folder]
# 根据 Filemap 父目录路径创建文件夹,常用来生成文件夹
mkdir_p t.name.pathmap("%d")
# 删除目录
rm_rf [folder]
# 执行命令
sh [cmd]
|
执行 rake 时使用 -q
参数来忽略 sh 执行日志
Rake 提供默认文件清理任务 rake clean
此任务使用 CLEAN.include
来导入需要清理的文件列表
1
2
3
4
5
| # 必须引入
require 'rake/clean'
CLEAN.include(SOURCE_FILES.ext(".html"))
CLEAN.include("book.epub")
|
Rake 提供文件清理任务 rake clobber
, 此任务只能导入文件列表到 CLOBBER
这个数组
clobber
任务的清理目标视为最终生产目标,而 clean 任务为情理构建中间产物
1
2
3
4
5
6
7
8
9
| # 必须引入
require 'rake/clean'
file "book.epub"
CLOBBER << "book.epub"
file "book.mobi"
CLOBBER << "book.mobi"
|
当然也可以粗暴的设置任务,删除构建文件夹
1
2
3
| task :cleanOutPuts do
rm_rf "outputs"
end
|
默认情况下,Rake 是单核构建的,如果想用到多核构建,把 task
换成 multitask
比如
1
2
| # task :gen_html => GEN_FILES
multitask :gen_html => GEN_FILES
|
如果不想改构建代码,也想用到多核效果,可以在执行指定CPU核数来开启 -j [cpu]
1
2
3
4
| # single
$ rake
# multi
$ rake -j 4
|
针对不同任务,可以做不同的依赖,最常用 namespace 来分割不同的任务
1
2
3
4
5
6
7
| namespace :group do
namespace :Innergroup do
task :gen do
end
end
task :all => ["Innergroup:gen"]
end
|
通过简单的 =>
来指定依赖关系
打印依赖关系为
1
2
3
4
| $ rake -P
rake group:Innergroup:gen
rake group:all
Innergroup:gen
|
这样就可以减少重复任务编写,提高效率
注意, 使用了 namespace 分配的任务,那么对 CLOBBER 清理来说,直接作用域就会失效,那么得根据实际情况修改清理任务
出现可预测的异常,需要停止构建的使用关键字 abort
, 直接让当前构建退出,并返回错误
问答式构建则复杂一点,需要函数 ask
1
2
3
4
5
6
7
8
9
10
11
12
13
| def ask(message, valid_options)
if valid_options
answer = get_stdin("#{message} #{valid_options.to_s.gsub(/"/, '').gsub(/, /,'/')} ") while !valid_options.include?(answer)
else
answer = get_stdin(message)
end
answer
end
def get_stdin(message)
print message
STDIN.gets.chomp
end
|
下面这个例子就是每当输出文件存在时,交互式问是否输入 n 来 打断构建
1
2
3
4
5
6
7
| GEN_FILES = Rake::FileList.new()
GEN_FILES.each do |t|
if File::exists?(t.pathmap("%p"))
abort("rake aborted! #{t.pathmap("%p")} not overwrite") if ask("#{t.pathmap("%p")} already exists. Do you want to overwrite?", ['y', 'n']) == 'n'
end
sh "pandoc -o #{t.pathmap("%p")} #{RES_FILES[each_code]}"
end
|