☰
Current Page
Main Menu
Home
Home
Editing
RakeForMakeUsers
Edit
Preview
h1
h2
h3
Keybinding
default
vim
emacs
Markup
Markdown
Plain Text
Pod
RDoc
reStructuredText
AsciiDoc
BibTeX
Creole
MediaWiki
Org-mode
Textile
Help 1
Help 1
Help 1
Help 2
Help 3
Help 4
Help 5
Help 6
Help 7
Help 8
Autosaved text is available. Click the button to restore it.
Restore Text
--- title: RakeForMakeUsers --- A guide to switching from Make to Rake. If you're just after a Rakefile for building C code, use my [Rake Template][1]. [[_TOC_]] # Syntax ## Variables Immediate evaluation assignment. PROJ := MyDoc DOCS := $(PROJ).html $(PROJ).odt PROJ = "MyDoc" DOCS = [ "#{PROJ}.html", "#{PROJ}.odt" ] Lazy evaluation assignment. TODO ## Targets images: $(IMAGES) desc "Build images" task :images => IMAGES ## Rules Create .html files by combining .rst and .svg: %.html : %.rst %.svg rst2html.py $< $@ rule '.html' => ['.rst','.svg'] do |t| sh "rst2html.py #{t.source} #{t.name}" end ## Globs and Patsubst SRC := $(wildcard *.c) OBJ := $(patsubst %.c,%.o,$(SRC)) SRC = FileList['*.c'] OBJ = SRC.ext('o') ## Output Paths Generate output files with names based on the input files. Support output to a separate build directory. ### Using pathmap To create an OBJ list with an output path in Rake we can use the pathmap syntax %X and substitute known paths %{obj,src}. Pathmap is limited though. Substituting a variable like OBJDIR in the pathmap syntax for FileLists doesn't work. It should work with the '*' symbol allowing one to specify a block to do the required substitution, but FileList breaks this, substituting a literal '*'. The mapping of SRC to OBJ needs to be reversed in the '.o' generation rule. Without using a `proc` this example needs to repeat the output path in multiple places. (Not very DRY.) Note also that the substitution depends on SRC paths starting with './'. DRYer, more flexible mapping is achieved with a `map` and a `proc`. See below. OBJDIR := obj OBJS := ${OBJDIR}/foo.o $(OBJDIR)/%.o : src/%.c @mkdir -p $(dir $@) $(CC) -c -o $@ $< OBJDIR = "obj" OBJS = C_SRCS.pathmap('%{.,path/to/obj/dir}X.o') rule '.o' => ['%{path/to/obj/dir,.}X.c', '%d'] do |t| mkdir_p t.name.pathmap('%d') sh "#{CC} #{t.source} -o #{t.name} #{STDLIB\_CFLAGS} #{STDLIB\_LFLAGS}" end ### Using map and proc Use `map` and `proc` to map source trees to output trees in a flexible way. # Map sources to objects OBJS = C_SRCS.map { |f| f.sub(/^#{SRC_DIR}/, OBJDIR).ext('.o') } # Map objects to sources rule '.o' => [ proc { |tn| tn.sub(/#{OBJ\_DIR}/, SRC\_DIR).ext('.c') }, '%d' ] do |t| sh %Q{#{CC} -c "#{t.source}" -o "#{t.name}"} end An example showing how to use procs to map outputs to a build directory. [RakeExampleBuildDirectory][30] ### Creating directories In GNUMake, use a silent `mkdir -p` and the `dir` built-in. $(OBJDIR)/%.o : src/%.c @mkdir -p $(dir $@) $(CC) -c -o $@ $< In Rake to create output directories to mimic the source tree layout we could use the same method as Make with the `mkdir_p` method but this still calls mkdir every time the rule is applied. A more efficient way is to: * Create a set of `directory` rules based on the OBJS paths * Make the generation rule depend on both the object file and its directory (using pathmap syntax). # declare directory tasks for each object file path OBJS.each do |d| directory d.pathmap('%d') end # Depend on the source file and the output directory rule '.o' => ['%{path/to/obj/dir,.}X.c', '%d'] do |t| sh "#{CC} #{C\_FLAGS} #{C\_DEFINE} #{C_INCLUDE} #{t.source} -o#{t.name}" end The source file must come first for 't.source' to work correctly. Here the pathmap syntax '%d' does the work of extracting the path from the filepath. ## Include Paths and Define lists Mapping an array of include paths to a string passed to the compiler. INCLUDE_DIRS = [ ".", "inc", "/usr/include/blah", ] # Map to the compiler's command line format C\_INCLUDE = INCLUDE\_DIRS.map {|s| "-I "+s }.join(" ") ## Spaces in Filenames ### The Easy Way with Shellwords Require [shellwords][34] require 'shellwords' rule '.out' => [*C_OBJS, '%d'] do |t| sh %Q{#{CC} -o #{t.name.shellescape} #{C_OBJS.shelljoin}} end ### The Manual Way with Monkey-patching If you're not using Bash as your shell you may have to do some manual substitution. In rake, we insert ("monkey patch" or "duck punch") a method in FileList and Array to convert each to a list of quoted strings. # Extension to quote lists (of object files, for example) class Array def to\_quoted\_s(q='"') "#{q}#{self.join("#{q} #{q}")}#{q}" end end class FileList def to\_quoted\_s(q='"') self.to_a.to\_quoted\_s(q) end end Then a rule looks like: rule '.out' => [*C_OBJS, '%d'] do |t| sh %Q{#{CC} #{LD\_FLAGS} #{C\_FLAGS} #{C\_DEFINE} #{C\_INCLUDE} -o "#{t.name}" #{C\_OBJS.to\_quoted_s}} end This could be extended (with a map-join or inject) to escape cases where the quote character appears in the filenames themselves. ## OS Detection ### Make UNAME := $(shell uname) ifeq ($(UNAME), Linux) # do something Linux-y endif ifeq ($(UNAME), Solaris) # do something Solaris-y endif ifdef SystemRoot RM = del /Q FixPath = $(subst /,\,$1) else ifeq ($(shell uname), Linux) RM = rm -f FixPath = $1 endif endif ### Rake def os @os ||= ( host_os = RbConfig::CONFIG['host_os'] case host_os when /mswin|bccwin|wince|emc/ :windows when /cygwin/ :cygwin when /mingw|msys/ :mingw when /darwin|mac os/ :macosx when /linux/ :linux when /solaris|bsd/ :unix else :unknown end ) end ## Invocation and Arguments Make pulls all environment variables into the same namespace as Make variables. Rake accesses arguments as if they were environment variables. ### Make CONFIG ?= Release Invocation: $ make CONFIG=Debug ### Rake CONFIG = ENV["CONFIG"] || "Release" [[$[Get Code]]][39] $ rake CONFIG=Debug ## PHONY * <http://blog.zenspider.com/blog/2012/01/simulating-phony-in-rake.html> ### Make .PHONY: clean ### rake As of 0.9.3 rake has phony built in: require 'rake/phony' task :clean => :phony But it can be simulated with: def (task(:phony)).timestamp Time.at end task :clean => :phony * * * # Examples ## Single build tree [RakeExampleBuildDirectory][30] ## Make and Rake An example project which takes a ReStructuredText document and graphviz/dot files and generates a single html or odt document. The html document has SVG images, the odt document has png images. ### GNUmake # # GNUmakefile for dot and reStructuredText # ##### Inputs ###### PROJ := MyDoc DOCS := $(PROJ).html $(PROJ).odt IMAGES := $(PROJ).svg $(PROJ).png ##### Targets ###### .PHONY: all clean images all: docs docs: $(DOCS) images: $(IMAGES) clean: @-$(RM) $(DOCS) $(IMAGES) ##### Rules ###### %.svg : %.dot dot -Tsvg $< -o $@ %.png : %.dot dot -Tpng $< -o $@ %.html : %.rst %.svg rst2html.py $< $@ %.odt : %.rst %.png sed 's/\.svg/\.png/g' $< | rst2odt.py > $@ ### Rake # # Rakefile for dot and reStructuredText # require 'rake/clean' ##### Inputs ###### PROJ = "MyDoc" DOCS = [ "#{PROJ}.html", "#{PROJ}.odt" ] IMAGES = [ "#{PROJ}.svg", "#{PROJ}.png" ] ##### Targets ###### CLEAN.include DOCS, IMAGES desc "Build documents" task :docs => DOCS desc "Build images" task :images => IMAGES desc "Build all" task :default => 'docs' ##### Rules ###### rule '.svg' => ['.dot'] do |t| sh "dot -Tsvg #{t.source} -o #{t.name}" end rule '.png' => ['.dot'] do |t| sh "dot -Tpng #{t.source} -o #{t.name}" end rule '.html' => ['.rst','.svg'] do |t| sh "rst2html.py #{t.source} #{t.name}" end rule '.odt' => ['.rst','.png'] do |t| sh "sed 's/\.svg/\.png/g' #{t.source} | rst2odt.py > #{t.name}" end ## Putting it all together My [rakefile template][1] for building C projects. # Extras ## Task iteration From [stackoverflow #1290119][45]. Ruby tasks, when invoked, are marked as complete. Repeated calls (e.g. in a loop) will invoke only once. The solution is either to: * convert the task to a method * reenable the task after each call ### As A Method task :build => [:some\_other\_tasks] do build end task :build_all do [:debug, :release].each { |t| build t } end def build(type = :debug) # ... end ### Reenabling tasks Rake::Task[':build'].reenable Rake::Task[':build'].invoke # Links * <http://blog.fortified-bikesheds.com/2012/02/rake-yet-another-reinvention-of-make.html> \---| [1]: RakeTemplate [21]: RakeForMakeUsers?action=sourceblock&num=1 [22]: RakeForMakeUsers?action=sourceblock&num=2 [23]: RakeForMakeUsers?action=sourceblock&num=3 [24]: RakeForMakeUsers?action=sourceblock&num=4 [28]: RakeForMakeUsers?action=sourceblock&num=5 [29]: RakeForMakeUsers?action=sourceblock&num=6 [30]: RakeExampleBuildDirectory [32]: RakeForMakeUsers?action=sourceblock&num=7 [33]: RakeForMakeUsers?action=sourceblock&num=8 [34]: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/shellwords/rdoc/Shellwords.html [35]: RakeForMakeUsers?action=sourceblock&num=9 [36]: RakeForMakeUsers?action=sourceblock&num=10 [37]: RakeForMakeUsers?action=sourceblock&num=11 [38]: RakeForMakeUsers?action=sourceblock&num=12 [39]: RakeForMakeUsers?action=sourceblock&num=13 [40]: RakeForMakeUsers?action=sourceblock&num=14 [41]: RakeForMakeUsers?action=sourceblock&num=15 [44]: RakeForMakeUsers?action=sourceblock&num=16 [45]: http://stackoverflow.com/questions/577944/how-to-run-rake-tasks-from-within-rake-tasks/1290119#1290119 [46]: RakeForMakeUsers?action=sourceblock&num=17 [47]: RakeForMakeUsers?action=sourceblock&num=18
Uploading file...
Sidebar
# SideBar * [Home][1] * [Projects][2] * * * <!-- --> * [Code][3] * [Tech][4] * [Network][5] * [MediaCentre][6] * [UAV][7] * * * <!-- --> * [Travel][8] * [Music][9] * [Horse Riding][10] * [Study][11] * [Games][12] * [Other Activities][13] * * * <!-- --> * [Car][14] * [House][15] * [Watch][16] * [Clothing][17] * [Miscellany][18] * * * [1]: /Home [2]: /Projects [3]: /Code/Code [4]: /Tech/Tech [5]: /Network/Network [6]: /MediaCentre/MediaCentre [7]: /UAV/UAV [8]: /Travel/Travel [9]: /Music/Music [10]: /HorseRiding/HorseRiding [11]: /Study/Study [12]: /Games/Games [13]: /Do/Do [14]: /Car/Car [15]: /House/House [16]: /Watch/Watch [17]: /Clothing/Clothing [18]: /Miscellany/Miscellany
Edit message:
Cancel