rake-plus 0.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,13 @@
1
+ Rake ext
2
+ ========
3
+
4
+ A couple of rake extensions that are useful for building stuff.
5
+
6
+ I always end up needing these things so here they are, neatly packaged
7
+ together.
8
+
9
+ One of the differenciating approach here is that new kind of tasks are
10
+ implemented a new "types" of tasks instead as being exposed like
11
+ class derivatives.
12
+
13
+ Check out the example to see what I mean.
@@ -0,0 +1,49 @@
1
+ require 'rake-plus/sudo'
2
+ require 'rake/task'
3
+
4
+ module Rake
5
+ # TODO: abstract dependencies from the package system
6
+ class BuildDependency < Rake::Task
7
+ include Rake::DSL
8
+ ON_OSX = (`uname` == "Darwin\n")
9
+
10
+ def needed?
11
+ return if ON_OSX
12
+ # dpkg-query also returns old packages, so '^ii ' guarantees only installed
13
+ sh("dpkg-query -l \"#{basename}\" | grep -e '^ii ' > /dev/null 2>&1") do |ok, res|
14
+ return :package_missing unless ok
15
+ end
16
+ return
17
+ end
18
+
19
+ def timestamp
20
+ Rake::EARLY
21
+ end
22
+
23
+ def execute(args=nil)
24
+ sudo "apt-get install -qy \"#{basename}\""
25
+ super
26
+ end
27
+
28
+ def basename
29
+ name.sub(':bdep:','')
30
+ end
31
+
32
+ class << self
33
+ # Apply the scope to the task name according to the rules for this kind
34
+ # of task. File based tasks ignore the scope when creating the name.
35
+ def scope_name(scope, task_name)
36
+ ":bdep:#{task_name}"
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ def bdep(args, &block)
43
+ t = Rake::BuildDependency.define_task(args, &block)
44
+ Rake::Task["bdeps"].prerequisites.push(t.name).uniq!
45
+ t
46
+ end
47
+
48
+ desc "Installs all bdeps"
49
+ task "bdeps"
@@ -0,0 +1,97 @@
1
+ require 'uri'
2
+
3
+ module Rake
4
+ class GitDependency < Rake::Task
5
+ include Rake::DSL if defined? Rake::DSL
6
+ attr_accessor :branch, :commit_id
7
+
8
+ def branch(val = nil)
9
+ @branch = val if val
10
+ @branch
11
+ end
12
+
13
+ def commit_id(val = nil)
14
+ @commit_id = val if val
15
+ @commit_id
16
+ end
17
+
18
+ def commit_or_branch
19
+ @commit_id || @branch
20
+ end
21
+
22
+ def needed?
23
+ return :checkout_missing unless File.directory?(local_path)
24
+ # check if that revision exists
25
+ if commit_or_branch
26
+ Dir.chdir(local_path) do
27
+ sh("git log #{commit_or_branch} -- >/dev/null 2>&1") do |ok, res|
28
+ return "commit_missing: #{commit_or_branch}" unless ok
29
+ end
30
+ end
31
+ end
32
+ return
33
+ end
34
+
35
+ def timestamp
36
+ if File.exist?(local_path / 'HEAD')
37
+ File.mtime(local_path / 'HEAD')
38
+ else
39
+ Rake::EARLY
40
+ end
41
+ end
42
+
43
+ def local_path
44
+ RakePlus.cache_dir / "git" / basename
45
+ end
46
+
47
+ def basename
48
+ base = File.basename(name)
49
+ unless File.extname(base) == ".git"
50
+ base + ".git"
51
+ else
52
+ base
53
+ end
54
+ end
55
+
56
+ def execute(args=nil)
57
+ if File.directory?(local_path)
58
+ Dir.chdir(local_path) do
59
+ sh "git remote update"
60
+ end
61
+ else
62
+ FileUtils.mkdir_p File.dirname(local_path)
63
+ sh "git clone --mirror #{name} #{local_path}"
64
+ end
65
+ super
66
+ end
67
+
68
+ # Utility
69
+ def clone_to(dir, branch=nil)
70
+ sh "git clone #{local_path} #{dir}"
71
+ if commit_or_branch
72
+ Dir.chdir(dir) do
73
+ sh "git checkout #{commit_or_branch}"
74
+ end
75
+ end
76
+ end
77
+
78
+ # Like .tar archives, creates a folder beneath it
79
+ def unpack_to(dir)
80
+ clone_to(File.join(dir, basename))
81
+ end
82
+
83
+ class << self
84
+ # Git based tasks ignore the scope when creating the name.
85
+ def scope_name(scope, task_name); task_name end
86
+ end
87
+ end
88
+ end
89
+
90
+ # Usage: git("git@github.com:zimbatm/direnv.git")
91
+ #
92
+
93
+
94
+ def git(*args, &block)
95
+ Rake::GitDependency.define_task(*args, &block)
96
+ end
97
+ alias git_dep git
@@ -0,0 +1,10 @@
1
+ require 'time'
2
+
3
+ def gitver(branch_or_file=nil)
4
+ ret = `git log -n 1 --oneline #{branch_or_file}`
5
+ raise ArgumentError, "could not find #{branch_or_file}" if $?.exitstatus > 0
6
+ raise "parse error" unless (ret =~ /^([^\s]+)/)
7
+ commit_id = $1
8
+ commit_num = `git log --oneline #{branch_or_file} | wc -l`.chomp.to_i
9
+ "#{commit_num}-#{commit_id}"
10
+ end
@@ -0,0 +1,90 @@
1
+ require 'pathname'
2
+
3
+ # Patching Pathame to return self.class.new objects instead of Pathname.new
4
+ class Pathname
5
+ def +(other)
6
+ other = self.class.new(other) unless other.kind_of?(Pathname)
7
+ self.class.new(plus(@path, other.to_s))
8
+ end
9
+
10
+ def join(*args)
11
+ args.unshift self
12
+ result = args.pop
13
+ result = self.class.new(result) unless result.kind_of?(Pathname)
14
+ return result if result.absolute?
15
+ args.reverse_each {|arg|
16
+ arg = self.class.new(arg) unless arg.kind_of?(Pathname)
17
+ result = arg + result
18
+ return result if result.absolute?
19
+ }
20
+ result
21
+ end
22
+
23
+ def relative_path_from(base_directory)
24
+ dest_directory = self.cleanpath.to_s
25
+ base_directory = base_directory.cleanpath.to_s
26
+ dest_prefix = dest_directory
27
+ dest_names = []
28
+ while r = chop_basename(dest_prefix)
29
+ dest_prefix, basename = r
30
+ dest_names.unshift basename if basename != '.'
31
+ end
32
+ base_prefix = base_directory
33
+ base_names = []
34
+ while r = chop_basename(base_prefix)
35
+ base_prefix, basename = r
36
+ base_names.unshift basename if basename != '.'
37
+ end
38
+ unless SAME_PATHS[dest_prefix, base_prefix]
39
+ raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}"
40
+ end
41
+ while !dest_names.empty? &&
42
+ !base_names.empty? &&
43
+ SAME_PATHS[dest_names.first, base_names.first]
44
+ dest_names.shift
45
+ base_names.shift
46
+ end
47
+ if base_names.include? '..'
48
+ raise ArgumentError, "base_directory has ..: #{base_directory.inspect}"
49
+ end
50
+ base_names.fill('..')
51
+ relpath_names = base_names + dest_names
52
+ if relpath_names.empty?
53
+ self.class.new('.')
54
+ else
55
+ self.class.new(File.join(*relpath_names))
56
+ end
57
+ end
58
+ end
59
+
60
+ # Like a Pathname but behaves more like a string
61
+ class Path < Pathname
62
+ alias / +
63
+ def =~(regex); to_s =~ regex end
64
+ def <=>(other); to_s <=> other.to_s end
65
+ def length; to_s.length end
66
+ alias size length
67
+
68
+ def method_missing(m, *a, &b)
69
+ s = to_s
70
+ if s.respond_to? m
71
+ s.send(m, *a, &b)
72
+ else
73
+ super
74
+ end
75
+ end
76
+
77
+ def respond_to?(m)
78
+ super || to_s.respond_to?(m)
79
+ end
80
+
81
+ alias expand expand_path
82
+
83
+ def self.[](*args)
84
+ new File.join(*args.map(&:to_s))
85
+ end
86
+ end
87
+
88
+ def path(*args)
89
+ Path[ *args ]
90
+ end
@@ -0,0 +1,12 @@
1
+ module Rake
2
+ class Task
3
+ # Are there any prerequisites with a later time than the given time stamp?
4
+ def out_of_date?(stamp)
5
+ @prerequisites.any? { |n| application[n, @scope].timestamp > stamp}
6
+ end
7
+
8
+ def file_missing?(path)
9
+ File.exist?(path) ? nil : :file_missing
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,221 @@
1
+ require 'rake-plus'
2
+ require 'rake-plus/rake_ext'
3
+
4
+ class Recipe < Rake::Task
5
+ include Rake::DSL if defined? Rake::DSL
6
+ BUILD_SYSTEM = `uname -sm`.strip.gsub(' ', '-').downcase
7
+
8
+ @@defaults = {}
9
+
10
+ class << self
11
+ def var(name, &default)
12
+ if block_given?
13
+ @@defaults[name.to_s] = default
14
+
15
+ class_eval <<-CODE
16
+ def #{name}(val=nil)
17
+ if !val.nil?
18
+ @#{name} = val
19
+ elsif @#{name}.nil?
20
+ @#{name} = instance_eval(&@@defaults["#{name}"])
21
+ end
22
+ @#{name}
23
+ end
24
+ CODE
25
+ else
26
+ class_eval <<-CODE
27
+ def #{name}(val=nil)
28
+ @#{name} = val unless val.nil?
29
+ @#{name}
30
+ end
31
+ CODE
32
+ end
33
+ self
34
+ end
35
+ end
36
+
37
+ # Variables for the DSL
38
+ def url_or_git
39
+ @url || @git || @svn
40
+ end
41
+
42
+ var :unpack_dir do
43
+ url_or_git.basename.sub('.tar.gz','').sub('.tar.bz2','')
44
+ end
45
+ var :build_dir do
46
+ # : causes trouble in PATH
47
+ sub = name.split(':')
48
+ sub[0] = sub[0] + '-' + BUILD_SYSTEM
49
+ RakePlus.build_dir / sub.join('/')
50
+ end
51
+ var :src_dir do
52
+ build_dir / 'src'
53
+ end
54
+ var :dep_dir do
55
+ build_dir / 'dep'
56
+ end
57
+ var :prefix do
58
+ build_dir / 'install'
59
+ end
60
+ def bdeps(val=nil)
61
+ unless val.nil?
62
+ val = [val] unless val.kind_of? Array
63
+ @bdeps = val.map{|x| x.kind_of?(Recipe) ? x : recipe(x)}
64
+ end
65
+ @bdeps || []
66
+ end
67
+ var :static_install
68
+ def install(&block)
69
+ @install ||= block
70
+ @install ||= proc do
71
+ mkdir_p(prefix)
72
+ sh "./configure --prefix=#{prefix}#{static_install ? " --enable-static --disable-shared" : ''}"
73
+ sh "make"
74
+ sh "make install"
75
+ end
76
+ @install
77
+ end
78
+ def url(val=nil)
79
+ unless val.nil?
80
+ @url = remote_package(val)
81
+ end
82
+ @url
83
+ end
84
+ def git(val=nil)
85
+ unless val.nil?
86
+ @git = git_dep(val)
87
+ end
88
+ @git
89
+ end
90
+
91
+ def svn(val=nil)
92
+ unless val.nil?
93
+ @svn = svn_dep(val)
94
+ end
95
+ @svn
96
+ end
97
+
98
+ def patches
99
+ @patches ||= []
100
+ end
101
+ def patch(val)
102
+ patches.push(val)
103
+ end
104
+
105
+ def install_to(dir)
106
+ sh "cp -r #{prefix}/* #{dir}"
107
+ end
108
+
109
+ def needed?
110
+ file_missing?(build_dir / "install.done") || out_of_date?(timestamp)
111
+ end
112
+
113
+ def timestamp
114
+ if File.exist?(build_dir / "install.done")
115
+ File.mtime(build_dir / "install.done")
116
+ else
117
+ Rake::EARLY
118
+ end
119
+ end
120
+
121
+ def opts_to_string(opts)
122
+ ary = []
123
+ opts.each_pair do |k,v|
124
+ ary << "--#{k}=#{v}"
125
+ end
126
+ ary.join(' ')
127
+ end
128
+
129
+ def flags_to_features(*flags)
130
+ flags.flatten.map do |x|
131
+ case x
132
+ when /^\+/
133
+ x.gsub(/^\+/,'--enable-')
134
+ when /^-/
135
+ x.gsub(/^-/,'--disable-')
136
+ when /^\/\//, /^\s*$/
137
+ nil
138
+ else
139
+ raise ArgumentError, "unknown flag: #{x}"
140
+ end
141
+ end.compact.join(' ')
142
+ end
143
+
144
+ def define(&block)
145
+ raise "Already defined" if @defined
146
+ instance_eval(&block)
147
+ raise "Missing @url or @git" unless url_or_git
148
+
149
+ directory build_dir
150
+ directory src_dir
151
+
152
+ patches.each{|p| file(p) }
153
+
154
+
155
+ file(build_dir / "src.done" => [url_or_git, build_dir, src_dir] + patches) do |t|
156
+ sh "rm -rf #{src_dir}/*"
157
+ url_or_git.unpack_to src_dir
158
+
159
+ # Apply patches
160
+ Dir.chdir(src_dir / unpack_dir) do
161
+ patches.each do |p|
162
+ sh "patch -b -f -N -p0 < #{RakePlus.top / p}"
163
+ end
164
+ end
165
+
166
+ touch t.name
167
+ end
168
+
169
+ self.class.define_task(self.name.sub(/.*:/,'') => bdeps + [build_dir / "src.done"] + Dir[src_dir/'**/*'] )
170
+
171
+ @defined = true
172
+ self
173
+ end
174
+
175
+ def execute(args=nil)
176
+ raise "Not defined" unless @defined
177
+
178
+ # Make build-dependencies available
179
+ if bdeps.any?
180
+ rm_rf dep_dir
181
+ mkdir_p dep_dir
182
+ bdeps.each{|dep| recipe(dep).install_to(dep_dir) }
183
+ end
184
+
185
+ with_env(
186
+ :LDFLAGS => "-L#{dep_dir}/lib",
187
+ :CFLAGS => "-I#{dep_dir}/include",
188
+ :PKG_CONFIG_PATH => "#{dep_dir}/lib/pkgconfig",
189
+ :PATH => "#{dep_dir}/bin:#{ENV['PATH']}"
190
+ ) do
191
+ Dir.chdir(src_dir / unpack_dir, &install)
192
+ end
193
+
194
+ # remove unused stuff
195
+ rm_rf prefix / "man"
196
+ rm_rf prefix / "share" / "man"
197
+ sh "find #{prefix} -type d -empty -delete"
198
+
199
+ # done
200
+ touch build_dir / "install.done"
201
+ super
202
+ end
203
+
204
+ protected
205
+
206
+ def with_env(new_env={}, &block)
207
+ bak = new_env.keys.inject({}) do |h,k|
208
+ h[k.to_s] = ENV[k.to_s]
209
+ h
210
+ end
211
+ new_env.each{|k,v| ENV[k.to_s] = v.to_s }
212
+ yield
213
+ ensure
214
+ bak.each{|k,v| ENV[k] = v }
215
+ end
216
+
217
+ end
218
+
219
+ def recipe(*args, &block)
220
+ Recipe.define_task(*args, &block)
221
+ end
@@ -0,0 +1,79 @@
1
+ module Rake
2
+ class RemotePackage < Rake::Task
3
+ include Rake::DSL if defined? Rake::DSL
4
+
5
+ # Example: https://github.com/vivienschilis/segmenter/zipball/v0.0.1
6
+ GITHUB_MATCH = %r[^https?://github\.com/([^/]+)/([^/]+)/(zip|tar)ball/(.*)]
7
+ SF_MATCH = %r[^https?://sourceforge.net/.*/([^/]+)/download$]
8
+
9
+ def needed?
10
+ :package_missing unless File.exist?(local_path)
11
+ end
12
+
13
+ def local_path
14
+ RakePlus.cache_dir / "pkg" / basename
15
+ end
16
+
17
+ def basename
18
+ if name =~ GITHUB_MATCH
19
+ "#{$1}-#{$2}-#{$4}#{$3 == "zip" ? ".zip" : ".tar.gz"}"
20
+ elsif name =~ SF_MATCH
21
+ $1
22
+ else
23
+ File.basename(name.sub(/\?.*/,''))
24
+ end
25
+ end
26
+
27
+ def execute(args=nil)
28
+ if needed?
29
+ FileUtils.mkdir_p File.dirname(local_path)
30
+ sh "curl -L -o \"#{local_path}.tmp\" \"#{name}\""
31
+ mv "#{local_path}.tmp", local_path
32
+ end
33
+ super
34
+ end
35
+
36
+ # Utility
37
+ def unpack_to(dir)
38
+ abs_local_path = RakePlus.top_dir.expand(local_path)
39
+ Dir.chdir(dir) do
40
+ case abs_local_path
41
+ when /\.tar\.gz$/, /\.tgz$/
42
+ sh "tar xzvf \"#{abs_local_path}\""
43
+ when /\.tar\.bz2$/
44
+ sh "tar xjvf \"#{abs_local_path}\""
45
+ when /\.tar$/
46
+ sh "tar xvf \"#{abs_local_path}\""
47
+ else
48
+ raise "Unsupported file extensions of #{basename}"
49
+ end
50
+ end
51
+ end
52
+
53
+ # Time stamp for file task.
54
+ def timestamp
55
+ if File.exist?(local_path)
56
+ File.mtime(local_path)
57
+ else
58
+ Rake::EARLY
59
+ end
60
+ end
61
+
62
+ class << self
63
+ # Apply the scope to the task name according to the rules for this kind
64
+ # of task. File based tasks ignore the scope when creating the name.
65
+ def scope_name(scope, task_name)
66
+ task_name
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ def remote_package(*args, &block)
73
+ t = Rake::RemotePackage.define_task(*args, &block)
74
+ Rake::Task["remote_packages"].prerequisites.push(t.name).uniq!
75
+ t
76
+ end
77
+
78
+ desc "Downloads all remote packages"
79
+ task :remote_packages
@@ -0,0 +1,8 @@
1
+ def sudo(*command)
2
+ raise ArgumentError, "command missing" if command.empty?
3
+ if command.length == 1
4
+ sh "sudo #{command.first}"
5
+ else
6
+ sh "sudo", *command
7
+ end
8
+ end
@@ -0,0 +1,63 @@
1
+ require 'uri'
2
+
3
+ module Rake
4
+ class SvnDependency < Rake::Task
5
+ include Rake::DSL if defined? Rake::DSL
6
+
7
+ def branch(val = nil)
8
+ @branch = val if val
9
+ @branch || "trunk"
10
+ end
11
+
12
+ def needed?
13
+ return :checkout_missing unless File.directory?(local_path)
14
+ end
15
+
16
+ def timestamp
17
+ if File.exist?(local_path / 'svn')
18
+ File.mtime(local_path / 'svn')
19
+ else
20
+ Rake::EARLY
21
+ end
22
+ end
23
+
24
+ def local_path
25
+ RakePlus.cache_dir / "svn" / basename
26
+ end
27
+
28
+ def basename
29
+ base = File.basename(name)
30
+ end
31
+
32
+ def execute(args=nil)
33
+ if File.directory?(local_path)
34
+ Dir.chdir(local_path) do
35
+ sh "svn update"
36
+ end
37
+ else
38
+ FileUtils.mkdir_p File.dirname(local_path)
39
+ sh "svn checkout #{name}/#{branch} #{local_path}"
40
+ end
41
+ super
42
+ end
43
+
44
+ # Like .tar archives, creates a folder beneath it
45
+ def unpack_to(dir)
46
+ sh "cp -R #{local_path} #{dir}"
47
+ end
48
+
49
+ class << self
50
+ # Git based tasks ignore the scope when creating the name.
51
+ def scope_name(scope, task_name); task_name end
52
+ end
53
+ end
54
+ end
55
+
56
+ # Usage: git("git@github.com:zimbatm/direnv.git")
57
+ #
58
+
59
+
60
+ def svn(*args, &block)
61
+ Rake::SvnDependency.define_task(*args, &block)
62
+ end
63
+ alias svn_dep svn
@@ -0,0 +1,11 @@
1
+ require 'ostruct'
2
+ require 'erb'
3
+
4
+ def tpl(name, target, variables={})
5
+ ctx = OpenStruct.new(variables)
6
+ tpl_str = File.read(RakePlus.template_dir / "#{name}.erb")
7
+ tpl = ERB.new(tpl_str)
8
+ out = tpl.result ctx.send(:binding)
9
+ FileUtils.mkdir_p File.dirname(target)
10
+ File.open(target, 'w') do |f| f.write out end
11
+ end
@@ -0,0 +1,4 @@
1
+ # Tired of re-running your rake task with -t because it throwed an exception ?
2
+ #
3
+ # This will turn on the trace by default so that you don't have to do it again.
4
+ Rake.application.options.trace = true
data/lib/rake-plus.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'rake-plus/trace'
2
+ require 'rake-plus/path'
3
+
4
+ module RakePlus
5
+ @top = path(Dir.pwd)
6
+ @cache_dir = @top / '.cache'
7
+ @build_dir = @top / 'build'
8
+ @template_dir = @top / 'tpl'
9
+
10
+ class << self
11
+ attr_reader :top
12
+ attr_accessor :cache_dir
13
+ attr_accessor :build_dir
14
+ attr_accessor :template_dir
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rake-plus
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.5'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jonas Pfenniger
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: A collection of rake extensions that are useful all around
31
+ email: jonas@pfenniger.name
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - README.md
37
+ - lib/rake-plus/bdep.rb
38
+ - lib/rake-plus/git_dep.rb
39
+ - lib/rake-plus/gitver.rb
40
+ - lib/rake-plus/path.rb
41
+ - lib/rake-plus/rake_ext.rb
42
+ - lib/rake-plus/recipe.rb
43
+ - lib/rake-plus/remote_package.rb
44
+ - lib/rake-plus/sudo.rb
45
+ - lib/rake-plus/svn_dep.rb
46
+ - lib/rake-plus/tpl.rb
47
+ - lib/rake-plus/trace.rb
48
+ - lib/rake-plus.rb
49
+ homepage: https://github.com/zimbatm/rake-plus
50
+ licenses: []
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 1.8.19
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: A shiny rake extensions collection
73
+ test_files: []