Posts Tagged ‘Rakefile’

h1

Rakefile basic

May 20, 2008

Rakefile은 Makefile과 비슷한 역할을 하는, Ruby script입니다. 따라서 Ruby라는 언어의 강력한 기능들을 그대로 가져다 쓸 수 있다는 장점이 있습니다. 단, Ruby를 알아야 제대로 사용할 수 있겠죠. Makefile을 make라는 명령어로 실행하듯이, Rakefile은 rake라는 명령어로 실행합니다. Rakefile 작성법을 Makefile 작성법과 비교하며 살펴보도록 하겠습니다. Makefile의 기본적인 작성법은
Target: Dependency list
[Tab] Command

였죠. Rakefile도 유사합니다. 단, Ruby syntax를 사용하죠. 기본적인 작성법은 다음과 같습니다.
task :name = [:prereq1, :prereq2] do
    Command
end

Makefile에서 Target에 해당하는 것이 Rakefile의 task입니다. 잘 살펴보면 task라는 함수명과 Hash, Block 두 개의 argument로 이루어진 구조라는 것을 알 수 있습니다. Hash의 key는 target이 되고 value는 prerequisites (dependency list)가 됩니다. Block은 실행해야 할 명령들로 이루어집니다. 특별히 compile하는 경우와 같이 파일을 작성하는 task의 경우에는
file "name" = ["prereq1", "prereq2"] do
    Command
end

와 같이 file task를 사용합니다. Command 부분에서 ‘name’ 또는 dependency list (prereq1, prereq2, … )를 사용하고 싶을 때는
file "name" = ["prereq1", "prereq2"] do |t|
sh “f77 -o #{t.name} #{t.prerequisites.join(’ ‘)}”
end

과 같이 사용하여 f77 -o name prereq1 prereq2와 같은 결과를 얻을 수도 있습니다.

그럼 앞에서 만들었던 Makefile과 같은 기능을 하는 Rakefile을 만들어 비교해 보겠습니다. 앞에서 만들었던 Makefile은 다음과 같고,

01: # target: dependency list
02: # [tab] command
03: F77=gfortran
04:
05: all: main
06:
07: main: main.o sub1.o sub2.o
08:         $(F77) -O2 -o main main.o sub1.o sub2.o
09: main.o: main.f
10:         $(F77) -O2 -c main.f
11: sub1.o: sub1.f
12:         $(F77) -O2 -c sub1.f
13: sub2.o: sub2.f
14:         $(F77) -O2 -c sub2.f
15: clean:
16:         rm main main.o sub1.o sub2.o

이에 해당하는 Rakefile은 다음과 같습니다.


f90='gfortran'

task :default => ['main.e']
file 'main.e' => ['main.o','sub1.o','sub2.o'] do |t|
    sh "#{f90} -o #{t.name} main.o sub1.o sub2.o"
end

file 'main.o' => ['main.f'] do
    sh "#{f90} -c main.f"
end
file 'sub1.o' => ['sub1.f'] do
    sh "#{f90} -c sub1.f"
end
file 'sub2.o' => ['sub2.f'] do
    sh "#{f90} -c sub2.f"
end

require 'rake/clean'
CLEAN.include('*.o')
CLOBBER.include('main.e')

task :default 부분은 Makefile에서 all 이라는 target을 지정해서 사용했던 것과 같은 역할을 합니다. 단, Rakefile에서는 default task가 맨 처음에 나올 필요가 없습니다. 파일 내 아무데나 나와도 잘 인식합니다. 중간 부분은 Makefile과 매우 유사하므로 특별한 설명이 필요 없겠죠? 뒤에 있는 clean task는 rake에 이미 지정되어 있는 task입니다. 사용하기 위해서는 ‘rake/clean’을 불러옵니다. rake clean을 실행하면 CLEAN에 포함된 파일들을 지워주고 rake clobber를 실행하면 CLOBBER와 CLEAN에 지정된 파일들을 모두 지워줍니다. 위에서 볼 수 있는 것처럼, 최종 결과 파일만 CLOBBER에 포함시키고 중간에 생성되는 파일들은 CLEAN에 포함시키면 편리하게 사용할 수 있습니다.

Makefile에서는 확장자법칙을 이용해 편리하게 compile할 수 있었죠? Rakefile에도 같은 기능이 있습니다. 비교해볼까요?

01: # $^ : dependency list
02: # $@ : target
03:
04: F77=ifort
05: FFLAG=-assume byterecl -O2
06: TARGET=main
07: OBJECTS=main.o sub1.o sub2.o
08:
09: all: $(TARGET)
10:
11: $(TARGET): $(OBJECTS)
12:         $(F77) -o $@ $^
13:
14: .SUFFIXES: .o .f
15: %.o: %.f
16:         $(F77) ${FFLAG} -c $^
17:
18: clean:
19:         rm $(TARGET) $(OBJECTS)

F90='ifort'
FFLAG='-assume byterecl -O2'
TARGET='main.e'
SRC=FileList['*.f']
OBJ=SRC.ext('o')

task :default => TARGET
file TARGET => OBJ do
    sh "#{F90} -o #{TARGET} #{OBJ}"
end
rule '.o' => '.f' do |t|
    sh "#{F90} #{FFLAG} -c #{t.source}"
end

require 'rake/clean'
CLEAN.include('*.o')
CLOBBER.include('main.e')

Rakefile에서는 rule이라는 함수가 Makefile의 확장자법칙과 같은 역할을 합니다. FileList 명령은 glob pattern (여기서는 ‘*.f’)을 받아들여서 해당하는 파일들의 목록을 만들어주고, FileList 객체의 ext method는 목록에 있는 파일들의 확장자를 원하는 확장자로 바꿔서 새로운 FileList를 만들어줍니다. 앞에서 dependency list를 불러올 때 t.prerequisites.join(' ')이라고 사용했었는데 여기서는 t.source라고 사용했습니다. 앞의 방법은 전체 dependency list를 문자열로 만들어주고(’ ‘을 이용하여 각각을 합치죠), 뒤의 방법은 dependency list의 첫 번 째 항목만 문자열로 만들어줍니다. 위의 예에서는 dependency list에 ‘.o’에 해당하는 ‘.f’ 파일 하나만 있으니까 t.source라고 사용해도 무관하겠죠?

Makefile에 없고 Rakefile에만 있는 기능 중 하나로, task에 설명을 달 수 있는 기능이 있습니다. task 또는 file task 바로 윗 줄에
desc "description"
이라고 설명을 추가해주면 rake -T라고 실행했을 때 설명과 함께 task 목록을 보여줍니다. Rakefile을 직접 보지 않고도 안에 무슨 task가 있는지 확인할 수 있는 유용한 기능이죠^^

더 자세한 내용은 다음의 site들을 참고하세요.
http://rake.rubyforge.org/
http://docs.rubyrake.org/

h1

Custom Rake Applications

June 3, 2006

Reference

Rake를 Rakefile을 이용하여 실행하지 않고 library로 사용하는 방법입니다.


#!/usr/bin/env ruby

#gem 'rake', '>= 0.7.3'
require 'rake'

Rake.application.init('server')

### (write a Rakefile here)

Rake.application.top_level