rakeとは
Rake は Make によく似た機能を持つ Ruby で書かれたシンプルなビルドツールです。
世間では、ビルドツールというとMakeやApache Antが有名で、よく使われている。 Rakeは、これらのいいとこ取りをした上で、特有のフィーチャーを追加した新しいビルドツールであり、複雑なビルドを柔軟に書きこなすことができる。その秘密は内部DSLという仕組みにあり、このおかげでビルドの記述にRubyの強力な文法をそのまま使うことができる。この自由度の高さは、ビルドの記述に独自の言語の使用を選択したMakeとAntには無い強みだ。
rakeとはrubyで書いたビルドツールのこと。
makeでいうMakefileの代わりに、Rakefileというファイルを作成して実行することができる
RakefileはRubyのDSLで書くことができるのでrubyさえわかれば記述が可能なので楽
rakeの用語についてのまとめ
用語 | 意味 |
---|---|
task | Rakeファイルの基本単位。 |
ファイルタスク | ファイル生成に特化したタスク |
アクション | taskのブロック内の実行されるコードのこと |
invoke | タスクを実行する。実行されたことのあるアクションは実行しない。依存しているタスクも呼び出だす |
execute | タスクを実行する。常にアクションを実行する。依存関係にあるタスクは実行しない |
invokeとexcuteについてはこちらの記事が非常にわかりやすい。
rakeタスク内で別のタスクを呼び出す
http://qiita.com/paty-fakename/items/5df189681c92ce1e8004
Rakefileを作成してみる
Rakefile
という名称でファイルを作成し、下記を記述する
task :hello do
puts 'hello world'
end
rakeコマンドを入力してタスクを実行すると結果が返ってくる
$ rake hello
#=> do task hello!
依存関係を作って実行してみる
今度は以下のコードをRakefileに書いてください
task "dist" => ["init", "compile"]
task "init"
task "compile"
以下で実行。
-t
オプションをつけると実行内容が表示される。
$ rake -t dist
** Invoke dist (first_time)
** Invoke init (first_time)
** Execute init
** Invoke compile (first_time)
** Execute compile
** Execute dist
dist
を実行すると、initとcompileが順番に実行されたのがわかると思います
taskはハッシュを引数にすると、task同士の依存を書くことができます
ちなみにrake
というコマンドを入力した場合は、default
という名前のタスクが実行されるようになっている
task "default" => "dist"
task "dist" => ["init", "compile"] do
puts "dist"
end
task "init" do
puts "init"
end
task "compile" do
puts "compile"
end
なので上記のようにRakefileを書き換えてrake
を実行するとdefaultのタスクが実行されて依存関係のあるdistが実行される
$ rake
#=> init
#=> compile
#=> dist
fileタスク
先ほどまでtaskブロックで実行してきたが、rakeにはファイル作成に特化したタスクもある
fileタスクの第一引数には実行するファイルのパスを指定する
taskと結びつけることによって、rake実行時にrake test_txt
として名称を指定して実行できる
# fileタスクをtaskと結びつける
task test_txt: './test.txt'
desc 'test.txtの中身を読み込んで、read.txtを作成する'
file './test.txt' do
puts 'write read.txt'
File.open('./test.txt', 'w') do |f|
f << "success"
end
end
desc
というのが新たに出てきているが、ここにはタスクの説明を書くのに使用する
実行すると、test_dir/test.txtが作成される
$ rake test_txt
#=> write test.txt
fileタスクの利点
このfileタスクは下記の場合のみ実行される
- 指定したファイルパスにファイルがない場合
- ファイルが作成されている場合、依存先のファイルの更新日時を調べそれが目的ファイルより新しくなっている場合
つまり、ファイルを新規に作成しなければならないときか、ファイルが変更されたときのみ実行できる。なので、ファイル操作の条件判定などを自分で書く必要がなくなる
なので先ほどのtest_txtを2回やっても、test.txtが作成済みなので何も実行されない。
$ rake test_txt
#=> (何も実行されない)
依存関係のあるfileタスクを書く
fileタスクの引数をハッシュ形式にすると依存が書ける
'実行予定のファイルパス' => '依存しているファイルパス'
という形式で書く
先ほどのファイルを少し改変して、test_dir/test.txtを作成したあと、それを利用してread.txtというファイルを作成する
task read: './read.txt'
desc 'test.txtの中身を読み込んで、read.txtを作成する'
file './read.txt' => './test.txt' do
puts 'write read.txt'
File.open('./read.txt', 'w') do |f|
# test.txtの中身を読みこんでread.txtに書き込む
f << File.read('./test.txt')
end
end
desc 'test.txtを作成する'
file './test.txt' do
puts 'create test.txt'
File.open('./test.txt', 'w') do |f|
f << "success"
end
end
test.txtがないときにこれおを実行すると、test.txtとread.txtが作成される
$ rake read
#=> write test.txt
#=> create read.txt
上記で書いたようにfileタスクはファイルがない場合か、依存ファイルが変更された場合のみ実行される。なのでもう一度実行してもタスクは実行されない
$ rake read
#=> (何も実行されない)
試しに依存ファイルのtest_dir/test.txtの中身を変更してみる
success
success2 # この行を追加
するとread.txtに関するタスクだけが実行されるように表示される。
もちろんread.txtの中身も書き換わっている
rake read
#=> write read.txt
fileタスクを利用してc言語のソースをコンパイルしてみる
冒頭でMakeに似たビルドツールという風に書いたように、C言語のソースをコンパイルするタスクも書くことができる。下記のようにhello worldを実行するだけのC言語のファイルがあったとする
#include <stdio.h>
int main(int argc, const char * argv[]) {
printf("hello world!\n");
return 0;
}
先ほどの依存関係の実行と同じように下記のように書く
CC = "gcc"
task :default => "hello"
# helloというファイルを作成するためにはhello.oが必要
file "hello" => "hello.o" do
sh "#{CC} -o hello hello.o"
end
# hello.oというオブジェクトファイルをコンパイルするためにはhello.cというファイルが必要
file "hello.o" => "hello.c" do
sh "#{CC} -c hello.c"
end
実行するとhelloというファイルができる
taskにdefaultを指定しているので、rakeだけで実行できる
$ rake
ファイルが作成されたので実行して世界にあいさつ
$ ./hello
# => hello world!
RailsのRakeタスクはどう動作するのか
Rakefileがあるとrakeコマンドが実行できることはわかったと思うが、Railsでタスクを作成するときはlib/tasks
に作成するのが基本である。
RailsのRakfileは以下のようになっている。
require_relative "config/application"
Rails.application.load_tasks
このload_tasksは以下で定義されている
def load_tasks(app = self)
require "rake"
run_tasks_blocks(app)
self
end
# ~省略~
def run_tasks_blocks(*) #:nodoc:
super
paths["lib/tasks"].existent.sort.each { |ext| load(ext) }
end
paths
にlib/tasks
があるかどうかexistent
で判断して、あればloadするという流れになっている。
参考
rubyリファレンス rake
https://docs.ruby-lang.org/ja/2.7.0/library/rake.html
RubyによるビルドツールRakeの覚え書き
http://www2s.biglobe.ne.jp/~idesaku/sss/tech/rake/