devball 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README +14 -0
- data/bin/devball +9 -0
- data/bin/devball-build +118 -0
- data/bin/devball-pull +66 -0
- data/bin/devball-push +46 -0
- data/lib/devball/initialization.rb +35 -0
- data/lib/devball/pkgspec/base.rb +342 -0
- data/lib/devball/pkgspec/djb.rb +73 -0
- data/lib/devball/pkgspec/gem.rb +98 -0
- data/lib/devball/pkgspec/pecl.rb +35 -0
- data/lib/devball/pkgspec/simple_dir.rb +9 -0
- data/lib/devball/pkgspec/tarball.rb +33 -0
- data/lib/devball/pkgspec/test.rb +0 -0
- metadata +67 -0
data/README
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
DevBall Builder
|
2
|
+
---
|
3
|
+
This is a tool for building a devball. A devball is a self-contained set of packages that can be portably be moved from one binary-compatible machine to another.
|
4
|
+
|
5
|
+
Usage
|
6
|
+
---
|
7
|
+
- Create a directory with pkgspecs and the accompanying tarballs/gem files/etc.
|
8
|
+
- Run "devball build {path}" to build the packages and setup the devball directory
|
9
|
+
- Use "devball push -p {rsync target} -n {build name}" to push your devball into an rsync repository
|
10
|
+
- Use "devball pull -p {rsync target} -n {build name}" to pull a devball down from an rsync repository
|
11
|
+
|
12
|
+
Examples
|
13
|
+
---
|
14
|
+
(To be added)
|
data/bin/devball
ADDED
data/bin/devball-build
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Builds our package environment based on the .pkgspec files in packages
|
3
|
+
|
4
|
+
require 'devball/initialization'
|
5
|
+
|
6
|
+
module DevBall
|
7
|
+
should_clean = false
|
8
|
+
clean_build_dirs = true
|
9
|
+
explicit_packages = nil
|
10
|
+
build = true
|
11
|
+
force = false
|
12
|
+
only = false
|
13
|
+
|
14
|
+
$build_dir = "/tmp/devball-build"
|
15
|
+
|
16
|
+
configure do |opts|
|
17
|
+
opts.on("-b", "--build-dir BUILD_DIR", "Specify where to use as a scratch directory for building. Defaults to /tmp/devball-build") do |build_dir|
|
18
|
+
$build_dir = build_dir
|
19
|
+
end
|
20
|
+
opts.on("-c", "--clean-install", "Remove built devball directory before starting.") do
|
21
|
+
should_clean = true
|
22
|
+
end
|
23
|
+
opts.on("-L", "--leave-build-dirs", "Leave intermediate build directories alone after installing (for debugging)") do
|
24
|
+
clean_build_dirs = false
|
25
|
+
end
|
26
|
+
opts.on("-N", "--no-build", "Don't actually build the devball, just list what would be built.") do
|
27
|
+
build = false
|
28
|
+
end
|
29
|
+
opts.on("-o", "--only PKGNAME,PKGNAME,PKGNAME...", "Only build the packages specified on the command line (but not their deps)") do |pkgnames|
|
30
|
+
only = true
|
31
|
+
explicit_packages = pkgnames.collect do |pkgname|
|
32
|
+
if (regex_match = /^\/(.*)\/$/.match(pkgname))
|
33
|
+
Regexp.new(regex_match[1])
|
34
|
+
else
|
35
|
+
pkgname
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
opts.on("-f", "--force", "Build ALL packages, even if they're already installed in the devball.") do
|
40
|
+
force = true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
packages = PkgSpec::Base.load_packages($package_dir, explicit_packages, only);
|
45
|
+
if (!force)
|
46
|
+
packages.delete_if {|pkg| File.directory?("#{$install_base}/packages/#{pkg}") }
|
47
|
+
end
|
48
|
+
puts("Installing packages: #{packages.join(', ')}")
|
49
|
+
puts("Into #{$install_base}")
|
50
|
+
|
51
|
+
exit(0) if (!build)
|
52
|
+
|
53
|
+
if (!system("mkdir", "-p", $build_dir))
|
54
|
+
puts("Could not create build directory.")
|
55
|
+
exit(150)
|
56
|
+
end
|
57
|
+
|
58
|
+
if (File.exists?($install_base) && should_clean)
|
59
|
+
system("rm", "-rf", "#{$install_base}/*")
|
60
|
+
end
|
61
|
+
|
62
|
+
if (!system("mkdir", "-p", $install_base))
|
63
|
+
puts("Could not create target directory.")
|
64
|
+
puts("You will probably need to create the directory and set permissions yourself by running:")
|
65
|
+
puts("sudo mkdir -p #{$install_base}")
|
66
|
+
puts("sudo chown #{ENV['USER']}: #{$install_base}")
|
67
|
+
exit(250)
|
68
|
+
end
|
69
|
+
|
70
|
+
# commonly needed environment variables.
|
71
|
+
ENV['CFLAGS'] = (ENV['CFLAGS'] || "") + " -I#{$install_base}/include -fPIC"
|
72
|
+
ENV['CXXFLAGS'] = (ENV['CXXFLAGS'] || "") + " -I#{$install_base}/include -fPIC"
|
73
|
+
ENV['LDFLAGS'] = (ENV['LDFLAGS'] || "") + " -L#{$install_base}/lib -fPIC"
|
74
|
+
ENV['PATH'] = "#{$install_base}/bin:" + (ENV['PATH'] || "")
|
75
|
+
ENV['RUBYOPT'] = '' # clear this out. It blows stuff up.
|
76
|
+
|
77
|
+
packages.each {|package|
|
78
|
+
package.remove_build()
|
79
|
+
related_pkgs = []
|
80
|
+
begin
|
81
|
+
puts("Extracting #{package}")
|
82
|
+
package.step_extract()
|
83
|
+
puts("Patching #{package}")
|
84
|
+
package.step_patch()
|
85
|
+
puts("Setting environment for #{package}")
|
86
|
+
package.step_environment_setup()
|
87
|
+
puts("Configuring #{package}")
|
88
|
+
package.step_configure()
|
89
|
+
puts("Building #{package}")
|
90
|
+
package.step_build()
|
91
|
+
puts("Installing #{package}")
|
92
|
+
package.remove_install()
|
93
|
+
begin
|
94
|
+
FileUtils.mkdir_p(package.package_install_dir) # precreate the package directory so it always exists.
|
95
|
+
related_pkgs = package.step_install()
|
96
|
+
rescue Exception
|
97
|
+
puts("Error installing #{package}. Cleaning up the install dir.")
|
98
|
+
package.remove_install()
|
99
|
+
system("rm", "-rf", package.package_install_dir)
|
100
|
+
raise
|
101
|
+
end
|
102
|
+
ensure
|
103
|
+
if (clean_build_dirs)
|
104
|
+
puts("Removing build directory for #{package}")
|
105
|
+
package.remove_build()
|
106
|
+
end
|
107
|
+
end
|
108
|
+
puts("Setting up links for #{package}")
|
109
|
+
package.step_setup_links()
|
110
|
+
if (related_pkgs.kind_of?(Array))
|
111
|
+
related_pkgs.each {|related|
|
112
|
+
PkgSpec::Base.find(related).step_setup_links()
|
113
|
+
}
|
114
|
+
end
|
115
|
+
}
|
116
|
+
|
117
|
+
puts("Done installing packages #{packages.join(', ')}.")
|
118
|
+
end
|
data/bin/devball-pull
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Pushed a built devball to an rsync target for later fetching by devball-pull
|
3
|
+
|
4
|
+
require 'devball/initialization'
|
5
|
+
|
6
|
+
module DevBall
|
7
|
+
config = {
|
8
|
+
# :path => nil,
|
9
|
+
# :username => nil,
|
10
|
+
# :build_name => nil,
|
11
|
+
# :no_push => nil,
|
12
|
+
}
|
13
|
+
|
14
|
+
configure do |opts|
|
15
|
+
opts.banner += "\n Will pull from {TARGET_PATH}{BUILD_NAME}."
|
16
|
+
|
17
|
+
opts.on("-n", "--name BUILD_NAME", "Name the build for pushing to rsync server.") do |build_name|
|
18
|
+
config[:build_name] = build_name
|
19
|
+
end
|
20
|
+
opts.on("-p", "--path TARGET_PATH", "Push to the rsync path specified with the build name specified in -n.") do |rsync_server|
|
21
|
+
config[:path] = rsync_server
|
22
|
+
end
|
23
|
+
opts.on("-u", "--username USERNAME", "Username to use when connecting to rsync.") do |username|
|
24
|
+
config[:username] = username
|
25
|
+
end
|
26
|
+
opts.on("-N", "--no-pull", "Don't actually pull, just tell me where it'll be pushing to.") do
|
27
|
+
config[:no_pull] = true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
config = $config[:rsync][:pull].merge(config) if ($config[:rsync] && $config[:rsync][:pull])
|
32
|
+
|
33
|
+
if (!config[:build_name] || !config[:path])
|
34
|
+
puts("You must specify a build name and rsync path to push to.")
|
35
|
+
exit(1)
|
36
|
+
end
|
37
|
+
|
38
|
+
if (config[:username])
|
39
|
+
config[:path] = config[:path].gsub(%r{^rsync://}, "rsync://#{config[:username]}@")
|
40
|
+
end
|
41
|
+
|
42
|
+
puts("Pulling devball from rsync path #{config[:path]}#{config[:build_name]}")
|
43
|
+
exit(0) if config[:no_pull]
|
44
|
+
|
45
|
+
if (!system("mkdir", "-p", $install_base))
|
46
|
+
puts("Could not create target directory.")
|
47
|
+
puts("You will probably need to create the directory and set permissions yourself by running:")
|
48
|
+
puts("sudo mkdir -p #{$install_base}")
|
49
|
+
puts("sudo chown #{ENV['USER']}: #{$install_base}")
|
50
|
+
exit(250)
|
51
|
+
end
|
52
|
+
|
53
|
+
system("rsync", "-a", "--delete", "#{config[:path]}#{config[:build_name]}", "#{$install_base}")
|
54
|
+
|
55
|
+
# now take the list of packages we pulled down, load their package objects, and setup links.
|
56
|
+
packages = PkgSpec::Base.load_packages($package_dir, nil, false);
|
57
|
+
Dir["#{$install_base}/packages/*"].each {|pkg_name|
|
58
|
+
pkg_name = %r{/([^/]+)$}.match(pkg_name)[1]
|
59
|
+
package = PkgSpec::Base.find(pkg_name)
|
60
|
+
if (!package)
|
61
|
+
raise "Unknown package #{pkg_name} pulled from devball server."
|
62
|
+
end
|
63
|
+
puts("Setting up links for #{pkg_name}")
|
64
|
+
package.step_setup_links()
|
65
|
+
}
|
66
|
+
end
|
data/bin/devball-push
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Pushed a built devball to an rsync target for later fetching by devball-pull
|
3
|
+
|
4
|
+
require 'devball/initialization'
|
5
|
+
|
6
|
+
module DevBall
|
7
|
+
config = {
|
8
|
+
# :path => nil,
|
9
|
+
# :username => nil,
|
10
|
+
# :build_name => nil,
|
11
|
+
# :no_push => nil,
|
12
|
+
}
|
13
|
+
|
14
|
+
configure do |opts|
|
15
|
+
opts.banner += "\n Will push to {TARGET_PATH}{BUILD_NAME}.\n Must have a built devball to push anything."
|
16
|
+
|
17
|
+
opts.on("-n", "--name BUILD_NAME", "Name the build for pushing to rsync server.") do |build_name|
|
18
|
+
config[:build_name] = build_name
|
19
|
+
end
|
20
|
+
opts.on("-p", "--path TARGET_PATH", "Push to the rsync path specified with the build name specified in -n.") do |rsync_server|
|
21
|
+
config[:path] = rsync_server
|
22
|
+
end
|
23
|
+
opts.on("-u", "--username USERNAME", "Username to use when connecting to rsync.") do |username|
|
24
|
+
config[:username] = username
|
25
|
+
end
|
26
|
+
opts.on("-N", "--no-push", "Don't actually push, just tell me where it'll be pushing to.") do
|
27
|
+
config[:no_push] = true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
config = $config[:rsync][:push].merge(config) if ($config[:rsync] && $config[:rsync][:push])
|
32
|
+
|
33
|
+
if (!config[:build_name] || !config[:path])
|
34
|
+
puts("You must specify a build name and rsync path to push to.")
|
35
|
+
exit(1)
|
36
|
+
end
|
37
|
+
|
38
|
+
if (config[:username])
|
39
|
+
config[:path] = config[:path].gsub(%r{^rsync://}, "rsync://#{config[:username]}@")
|
40
|
+
end
|
41
|
+
|
42
|
+
puts("Pushing devball to rsync path #{config[:path]}#{config[:build_name]}")
|
43
|
+
exit(0) if config[:no_push]
|
44
|
+
|
45
|
+
system("rsync", "-a", "--delete", "#{$install_base}/packages", "#{config[:path]}#{config[:build_name]}")
|
46
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'yaml'
|
3
|
+
require 'devball/pkgspec/base'
|
4
|
+
|
5
|
+
module DevBall
|
6
|
+
# Loads all the config options relevant to all devball commands. Yields
|
7
|
+
# an optparse object for the specific command to add options to.
|
8
|
+
def self.configure(argv = ARGV)
|
9
|
+
$install_base = nil
|
10
|
+
|
11
|
+
opts = OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: #{$0} [options] package_dir"
|
13
|
+
|
14
|
+
opts.on_tail("-h", "--help", "Show help screen") do
|
15
|
+
puts opts
|
16
|
+
exit(0)
|
17
|
+
end
|
18
|
+
|
19
|
+
yield opts
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.parse! argv
|
23
|
+
|
24
|
+
$package_dir = ARGV.shift || raise(ArgumentError, "No package bundle directory named.")
|
25
|
+
$package_name = File.basename($package_dir)
|
26
|
+
|
27
|
+
$config = {}
|
28
|
+
if (File.exists? "#{$package_dir}/config.yaml")
|
29
|
+
$config = YAML.load_file("#{$package_dir}/config.yaml")
|
30
|
+
$package_name = $config[:package_name] || $package_name
|
31
|
+
end
|
32
|
+
|
33
|
+
$install_base = "/#{$package_name}"
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,342 @@
|
|
1
|
+
# defines a bunch of helper functions as well as the main steps for building a package
|
2
|
+
# These steps are:
|
3
|
+
# - step_extract
|
4
|
+
# - step_patch
|
5
|
+
# - step_environment_setup
|
6
|
+
# - step_configure
|
7
|
+
# - step_build
|
8
|
+
# - step_install
|
9
|
+
# - step_setup_links
|
10
|
+
# There are also two that are involved in removing the resulting files.
|
11
|
+
# - remove_build
|
12
|
+
# - remove_install
|
13
|
+
# Derived classes should also describe their dependency tree through the
|
14
|
+
# PkgSpec::Base.depends_on function.
|
15
|
+
require 'fileutils'
|
16
|
+
module DevBall
|
17
|
+
module PkgSpec
|
18
|
+
class Error < Exception; end
|
19
|
+
class Undefined < Error; end
|
20
|
+
class PackageLoadError < Error; end
|
21
|
+
class ExtractFailed < Error; end
|
22
|
+
class ConfigureFailed < Error; end
|
23
|
+
class BuildFailed < Error; end
|
24
|
+
class InstallFailed < Error; end
|
25
|
+
class RemoveFailed < Error; end
|
26
|
+
|
27
|
+
# Autoload arbitrary devball pkgspec-types through const_missing
|
28
|
+
def self.const_missing(name)
|
29
|
+
autoloading = (Thread.current[:autoloading] ||= {})
|
30
|
+
if (autoloading[name])
|
31
|
+
raise NameError, "uninitialized constant DevBall::PkgSpec::#{name} (autoload already tried to find it)"
|
32
|
+
end
|
33
|
+
autoloading[name] = true
|
34
|
+
|
35
|
+
begin
|
36
|
+
filename = name.to_s.gsub(/^[A-Z]/) {|a| a.downcase }.gsub(/[A-Z]/) {|a| "_#{a.downcase}" }
|
37
|
+
require 'devball/pkgspec/' + filename
|
38
|
+
if (!const_defined?(name))
|
39
|
+
raise NameError, "uninitialized constant DevBall::PkgSpec::#{name} (autoload found file but file did not define class)"
|
40
|
+
end
|
41
|
+
return const_get(name)
|
42
|
+
rescue LoadError
|
43
|
+
raise NameError, "uninitialized constant DevBall::PkgSpec::#{name} (autoload failed to find matching file)"
|
44
|
+
ensure
|
45
|
+
autoloading[name] = false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Base
|
50
|
+
class <<self
|
51
|
+
def load_packages(dir, explicit, only_explicit = false)
|
52
|
+
# load all the packagespecs
|
53
|
+
@packages ||= {}
|
54
|
+
@install_packages ||= {}
|
55
|
+
Dir["#{dir}/*.pkgspec"].each {|f|
|
56
|
+
load "#{Dir.getwd}/#{f}"
|
57
|
+
}
|
58
|
+
|
59
|
+
def explicit.include?(str)
|
60
|
+
each {|i|
|
61
|
+
if (i === str)
|
62
|
+
return true
|
63
|
+
end
|
64
|
+
}
|
65
|
+
return false
|
66
|
+
end
|
67
|
+
|
68
|
+
@packages_inorder = @install_packages.collect {|name, pkg|
|
69
|
+
if (deps = pkg.recursive_depends_on)
|
70
|
+
[deps, name].flatten
|
71
|
+
else
|
72
|
+
name
|
73
|
+
end
|
74
|
+
}.flatten.uniq.collect {|pkgname|
|
75
|
+
if (!only_explicit || explicit.include?(pkgname))
|
76
|
+
@packages[pkgname] || raise(Error, "Unknown package #{pkgname} in package list (#{@packages.keys.join(",")})")
|
77
|
+
else
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
}.compact
|
81
|
+
return @packages_inorder
|
82
|
+
end
|
83
|
+
attr :packages_inorder
|
84
|
+
|
85
|
+
def find(name)
|
86
|
+
@packages ||= {}
|
87
|
+
|
88
|
+
return @packages[name]
|
89
|
+
end
|
90
|
+
|
91
|
+
def depends_on(*deps)
|
92
|
+
@deps ||= []
|
93
|
+
@deps.push(*deps) if (deps.length > 0)
|
94
|
+
|
95
|
+
deps = []
|
96
|
+
if (superclass && superclass.respond_to?(:depends_on))
|
97
|
+
deps.push(*superclass.depends_on)
|
98
|
+
end
|
99
|
+
deps.push(*@deps)
|
100
|
+
deps
|
101
|
+
end
|
102
|
+
|
103
|
+
def register_package(klass, name, required)
|
104
|
+
@packages ||= {}
|
105
|
+
@packages[name] = klass
|
106
|
+
if (required)
|
107
|
+
@install_packages ||= {}
|
108
|
+
@install_packages[name] = klass
|
109
|
+
end
|
110
|
+
end
|
111
|
+
attr :ball
|
112
|
+
def set_ball(name, required = true)
|
113
|
+
@ball = name
|
114
|
+
@instance = self.new
|
115
|
+
PkgSpec::Base.register_package(@instance, @instance.package_name, required)
|
116
|
+
end
|
117
|
+
# defines it as a library that is only required if somethind depended on it.
|
118
|
+
def set_lib_ball(name)
|
119
|
+
set_ball(name, false)
|
120
|
+
end
|
121
|
+
|
122
|
+
attr :patches
|
123
|
+
def set_patch(*patches)
|
124
|
+
@patches ||= []
|
125
|
+
@patches += patches
|
126
|
+
end
|
127
|
+
end
|
128
|
+
def ball
|
129
|
+
return self.class.ball
|
130
|
+
end
|
131
|
+
def depends_on
|
132
|
+
return self.class.depends_on
|
133
|
+
end
|
134
|
+
def recursive_depends_on
|
135
|
+
return self.class.depends_on.collect {|dep|
|
136
|
+
pkg = PkgSpec::Base.find(dep) || raise(PackageLoadError, "Package #{to_s} depends on #{dep} which doesn't exist.")
|
137
|
+
[pkg.recursive_depends_on, dep]
|
138
|
+
}.flatten
|
139
|
+
end
|
140
|
+
|
141
|
+
def ball_file_name()
|
142
|
+
return "#{$package_dir}/#{ball}"
|
143
|
+
end
|
144
|
+
# extracts the ball to the correct place in the builddir
|
145
|
+
def step_extract()
|
146
|
+
raise Undefined, "Behaviour undefined for extracting the ball"
|
147
|
+
end
|
148
|
+
|
149
|
+
def step_patch()
|
150
|
+
if (File.directory?(build_dir_name) && self.class.patches && self.class.patches.length)
|
151
|
+
orig = Dir.getwd
|
152
|
+
Dir.chdir(build_dir_name) {|dir|
|
153
|
+
self.class.patches.each {|patch|
|
154
|
+
system("patch -p1 < #{orig}/#{$package_dir}/#{patch}") || raise(PatchFailed, "Patch #{patch} failed to apply. Errno #{$?}")
|
155
|
+
}
|
156
|
+
}
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# get the name of the ball without extention
|
161
|
+
def ball_version()
|
162
|
+
ball().gsub(/^(.*)\.(gem|tar\.gz|tgz|tar\.bz2)$/, '\1')
|
163
|
+
end
|
164
|
+
|
165
|
+
# get the name of the ball without version. Cuts off after the first hyphen
|
166
|
+
def ball_name()
|
167
|
+
ball_version().gsub(/^([^\-]+)$/, '\1')
|
168
|
+
end
|
169
|
+
|
170
|
+
# get the name of the subdirectory the ball was extracted to
|
171
|
+
# provides a default implementation that uses ball() and takes off known extensions
|
172
|
+
# on the assumption that its name will be derived from the ball name that way.
|
173
|
+
# If that's not true, it should be overriden
|
174
|
+
def build_dir_name()
|
175
|
+
return "#{$build_dir}/#{ball_version}"
|
176
|
+
end
|
177
|
+
|
178
|
+
# get the name of the package without any version information. Default
|
179
|
+
# implementation bases it on the class name.
|
180
|
+
def package_name()
|
181
|
+
package_name = self.class.name.to_s
|
182
|
+
package_name = package_name.gsub(/^(.+?)(Package)?$/, '\1')
|
183
|
+
return package_name
|
184
|
+
end
|
185
|
+
def to_s()
|
186
|
+
package_name()
|
187
|
+
end
|
188
|
+
|
189
|
+
# Returns the directory the package will be installed in.
|
190
|
+
def package_install_dir()
|
191
|
+
return "#{$install_base}/packages/#{package_name}"
|
192
|
+
end
|
193
|
+
|
194
|
+
# configure options to pass to ./configure. The default step_configure function
|
195
|
+
# uses this, but derived versions don't have to, but whatever they do should
|
196
|
+
# have the same effect as the prefix set by the hash this returns.
|
197
|
+
# if this is overloaded, you should merge your options with super()
|
198
|
+
def configure_params()
|
199
|
+
return {:prefix=>package_install_dir}
|
200
|
+
end
|
201
|
+
|
202
|
+
def step_environment_setup()
|
203
|
+
end
|
204
|
+
|
205
|
+
def configure_script_name()
|
206
|
+
"./configure"
|
207
|
+
end
|
208
|
+
|
209
|
+
# do the configure step. The only requirement is that this end up installing things
|
210
|
+
# in /$install_base/packages/#{package_name}. The default implementation runs ./configure in
|
211
|
+
# the build dir with the results of configure_params.
|
212
|
+
def step_configure()
|
213
|
+
Dir.chdir(build_dir_name) {|dir|
|
214
|
+
params = configure_params.collect {|key, val|
|
215
|
+
if (val)
|
216
|
+
"--#{key}=#{val}"
|
217
|
+
else
|
218
|
+
"--#{key}"
|
219
|
+
end
|
220
|
+
}
|
221
|
+
if (!system(configure_script_name, *params))
|
222
|
+
raise ConfigureFailed, "Configuration failed with error: #{$?}"
|
223
|
+
end
|
224
|
+
}
|
225
|
+
end
|
226
|
+
|
227
|
+
# targets for the build call to make to use
|
228
|
+
def build_targets()
|
229
|
+
return ["all"]
|
230
|
+
end
|
231
|
+
|
232
|
+
def build_concurrent?()
|
233
|
+
return true
|
234
|
+
end
|
235
|
+
|
236
|
+
# do the build step. Default implementation is to simply run make in the build dir.
|
237
|
+
def step_build()
|
238
|
+
Dir.chdir(build_dir_name) {|dir|
|
239
|
+
args = []
|
240
|
+
args.push("-j4") if build_concurrent?
|
241
|
+
args += build_targets
|
242
|
+
if (!system("make", *args))
|
243
|
+
raise BuildFailed, "Build failed with error: #{$?}"
|
244
|
+
end
|
245
|
+
}
|
246
|
+
end
|
247
|
+
|
248
|
+
def install_targets()
|
249
|
+
return ["install"]
|
250
|
+
end
|
251
|
+
|
252
|
+
# do the install step. Default implementation is to simply run "make install" in the build dir
|
253
|
+
def step_install()
|
254
|
+
Dir.chdir(build_dir_name) {|dir|
|
255
|
+
if (!system("make", *install_targets))
|
256
|
+
raise InstallFailed, "Install failed with error: #{$?}"
|
257
|
+
end
|
258
|
+
}
|
259
|
+
return []
|
260
|
+
end
|
261
|
+
|
262
|
+
# Returns any explicit filename translations that should be performed on a file when linking
|
263
|
+
# it into the right place in the tree.
|
264
|
+
def link_mappings()
|
265
|
+
return {}
|
266
|
+
end
|
267
|
+
|
268
|
+
def install_service_links(base_from)
|
269
|
+
Dir[File.join(base_from, "*")].each {|service|
|
270
|
+
supervise_dir = File.join("/var#{$install_base}/supervise", File.basename(service))
|
271
|
+
begin
|
272
|
+
File.delete(File.join(service, "supervise"))
|
273
|
+
rescue; end
|
274
|
+
File.symlink(supervise_dir, File.join(service, "supervise"))
|
275
|
+
if (File.directory?(File.join(service, "log")))
|
276
|
+
begin
|
277
|
+
File.delete(File.join(service, "log", "supervise"))
|
278
|
+
rescue; end
|
279
|
+
File.symlink("#{supervise_dir}-log", File.join(service, "log", "supervise"))
|
280
|
+
end
|
281
|
+
}
|
282
|
+
end
|
283
|
+
|
284
|
+
# internal implementation of setting the links up for a given directory.
|
285
|
+
# pass nil into subdir to suppress recursion.
|
286
|
+
def install_links(base_from, base_into, subdir)
|
287
|
+
recurse = subdir
|
288
|
+
subdir = "" if !subdir
|
289
|
+
Dir[File.join(base_from, subdir, "*")].each {|file|
|
290
|
+
stat = File.lstat(file)
|
291
|
+
if (stat.directory? && recurse)
|
292
|
+
FileUtils.mkdir_p(File.join(base_into, subdir, File.basename(file)))
|
293
|
+
install_links(base_from, base_into, File.join(subdir, File.basename(file)))
|
294
|
+
end
|
295
|
+
if (!stat.directory?)
|
296
|
+
target = File.join(base_into, subdir, File.basename(file))
|
297
|
+
begin
|
298
|
+
File.delete(target)
|
299
|
+
rescue
|
300
|
+
end
|
301
|
+
File.symlink(file, target)
|
302
|
+
end
|
303
|
+
}
|
304
|
+
end
|
305
|
+
|
306
|
+
# Build the appropriate links for the package so it can be run properly from /nexopia
|
307
|
+
def step_setup_links()
|
308
|
+
install_links(package_install_dir, "#{$install_base}", nil)
|
309
|
+
FileUtils.mkdir_p("#{$install_base}/bin")
|
310
|
+
install_links(File.join(package_install_dir, "bin"), "#{$install_base}/bin", "")
|
311
|
+
install_links(File.join(package_install_dir, "sbin"), "#{$install_base}/bin", "")
|
312
|
+
install_links(File.join(package_install_dir, "libexec"), "#{$install_base}/bin", "")
|
313
|
+
FileUtils.mkdir_p("#{$install_base}/lib")
|
314
|
+
install_links(File.join(package_install_dir, "lib"), "#{$install_base}/lib", "")
|
315
|
+
FileUtils.mkdir_p("#{$install_base}/include")
|
316
|
+
install_links(File.join(package_install_dir, "include"), "#{$install_base}/include", "")
|
317
|
+
FileUtils.mkdir_p("#{$install_base}/man")
|
318
|
+
install_links(File.join(package_install_dir, "man"), "#{$install_base}/man", "")
|
319
|
+
FileUtils.mkdir_p("#{$install_base}/etc")
|
320
|
+
install_links(File.join(package_install_dir, "etc"), "#{$install_base}/etc", "")
|
321
|
+
|
322
|
+
FileUtils.mkdir_p("#{$install_base}/service-privileged")
|
323
|
+
install_service_links(File.join(package_install_dir, "service-privileged"))
|
324
|
+
install_links(File.join(package_install_dir, "service-privileged"), "#{$install_base}/service-privileged", "")
|
325
|
+
FileUtils.mkdir_p("#{$install_base}/service-required")
|
326
|
+
install_service_links(File.join(package_install_dir, "service-required"))
|
327
|
+
install_links(File.join(package_install_dir, "service-required"), "#{$install_base}/service-required", "")
|
328
|
+
FileUtils.mkdir_p("#{$install_base}/service-optional")
|
329
|
+
install_service_links(File.join(package_install_dir, "service-optional"))
|
330
|
+
install_links(File.join(package_install_dir, "service-optional"), "#{$install_base}/service-optional", "")
|
331
|
+
end
|
332
|
+
|
333
|
+
def remove_build()
|
334
|
+
system("rm", "-rf", build_dir_name) || raise(RemoveFailed, "Could not delete existing build directory.")
|
335
|
+
end
|
336
|
+
|
337
|
+
def remove_install()
|
338
|
+
system("rm", "-rf", package_install_dir) || raise(RemoveFailed, "Could not delete installed directory.")
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'ftools'
|
2
|
+
|
3
|
+
module DevBall
|
4
|
+
module PkgSpec
|
5
|
+
class Djb < Tarball
|
6
|
+
def extract_dir_name()
|
7
|
+
return build_dir_name
|
8
|
+
end
|
9
|
+
def package_build_dir_name()
|
10
|
+
return extract_dir_name + "/#{type}/#{ball_version}"
|
11
|
+
end
|
12
|
+
|
13
|
+
# use this to set the type of the package in djb's world (ie. admin)
|
14
|
+
def self.set_type(type)
|
15
|
+
@type = type
|
16
|
+
end
|
17
|
+
def self.type()
|
18
|
+
return @type
|
19
|
+
end
|
20
|
+
def type()
|
21
|
+
return self.class.type
|
22
|
+
end
|
23
|
+
|
24
|
+
def step_configure()
|
25
|
+
# look for makefiles and turn off -static flags in them. (ugh)
|
26
|
+
if (RUBY_PLATFORM =~ /darwin/)
|
27
|
+
Dir[package_build_dir_name() + "/**/Makefile"].each {|makefile|
|
28
|
+
File.rename(makefile, makefile + ".old")
|
29
|
+
File.open(makefile + ".old", "r") {|fin|
|
30
|
+
File.open(makefile, "w") {|fout|
|
31
|
+
fin.each {|line|
|
32
|
+
fout.puts(line.gsub(/\-static/, ''))
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def step_build()
|
41
|
+
Dir.chdir(package_build_dir_name()) {|dir|
|
42
|
+
# build source
|
43
|
+
system("package/compile") || raise(BuildFailed, "Build of djb package failed: #{$?}")
|
44
|
+
# build man pages
|
45
|
+
Dir.chdir("man") {|dir|
|
46
|
+
Dir["*.?"].each {|manfile|
|
47
|
+
system("gzip", manfile)
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def step_install()
|
54
|
+
# get commands from command/ and put them in the package install dir's bin directory
|
55
|
+
Dir.chdir(package_build_dir_name) {|dir|
|
56
|
+
# install the commands as specified by package/commands.
|
57
|
+
commands = IO.readlines("package/commands")
|
58
|
+
FileUtils.mkdir_p(package_install_dir + "/bin")
|
59
|
+
commands.each {|command|
|
60
|
+
FileUtils.copy("compile/" + command.chomp, package_install_dir + "/bin")
|
61
|
+
}
|
62
|
+
# install the man pages
|
63
|
+
Dir["man/*.?.gz"].each {|manpage|
|
64
|
+
mantype = /(.+)\.([0-8])\.gz/.match(manpage)
|
65
|
+
FileUtils.mkdir_p(package_install_dir + "/man/man#{mantype[2]}")
|
66
|
+
FileUtils.copy(manpage, package_install_dir + "/man/man#{mantype[2]}")
|
67
|
+
}
|
68
|
+
}
|
69
|
+
return []
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module DevBall
|
2
|
+
module PkgSpec
|
3
|
+
class Gem < Base
|
4
|
+
if ENV['RUBINIUS']
|
5
|
+
depends_on "Rubinius"
|
6
|
+
elsif ENV['REE']
|
7
|
+
depends_on "Ruby"
|
8
|
+
else
|
9
|
+
depends_on "RubyGems"
|
10
|
+
end
|
11
|
+
|
12
|
+
def step_extract()
|
13
|
+
# whether the gem is a local file or fetched from rubyforge, we don't extract it.
|
14
|
+
end
|
15
|
+
|
16
|
+
def step_configure()
|
17
|
+
# gems are all three steps in one at install time.
|
18
|
+
end
|
19
|
+
|
20
|
+
def step_build()
|
21
|
+
# gems are all three steps in one at install time.
|
22
|
+
end
|
23
|
+
|
24
|
+
def configure_params()
|
25
|
+
return {} # no need for prefix
|
26
|
+
end
|
27
|
+
|
28
|
+
# override to specify a different gem repository. Default returns nil and indicates to use the standard rubyforge repo.
|
29
|
+
def gem_repository()
|
30
|
+
return nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def step_install()
|
34
|
+
Dir.chdir($package_dir) {|dir|
|
35
|
+
params = configure_params.collect {|key, val|
|
36
|
+
if (val)
|
37
|
+
"--#{key}=#{val}"
|
38
|
+
else
|
39
|
+
"--#{key}"
|
40
|
+
end
|
41
|
+
}
|
42
|
+
|
43
|
+
if (params.length)
|
44
|
+
params.unshift("--")
|
45
|
+
end
|
46
|
+
|
47
|
+
if (gem_repository)
|
48
|
+
params.unshift(gem_repository)
|
49
|
+
params.unshift("--source")
|
50
|
+
end
|
51
|
+
|
52
|
+
if (ENV['RUBINIUS'])
|
53
|
+
# params = ["rbx", "gem", "install", "--install-dir=/nexopia/packages/Rubinius/lib/rubinius/gems/1.8", ball, *params].collect {|i| %Q{"#{i}"} }
|
54
|
+
params = ["rbx", "gem", "install", ball, *params].collect {|i| %Q{"#{i}"} }
|
55
|
+
else
|
56
|
+
params = ["gem", "install", ball, *params].collect {|i| %Q{"#{i}"} }
|
57
|
+
end
|
58
|
+
# and here this gets complicated, since gems don't return an error code if they fail. They just talk about it on their output.
|
59
|
+
success = false
|
60
|
+
errors = []
|
61
|
+
open("|#{params.join(' ')} 2>&1", "r") {|io|
|
62
|
+
io.each {|line|
|
63
|
+
puts(line)
|
64
|
+
if (match = /ERROR:\s+(.+)$/.match(line))
|
65
|
+
errors.push(match[1])
|
66
|
+
end
|
67
|
+
if (match = /^[0-9]+ gems? installed/.match(line))
|
68
|
+
success = true
|
69
|
+
end
|
70
|
+
if (match = /^Successfully installed/.match(line))
|
71
|
+
success = true
|
72
|
+
end
|
73
|
+
}
|
74
|
+
}
|
75
|
+
if (!success)
|
76
|
+
raise(InstallFailed, "Could not install gem: #{errors.join(', ')}")
|
77
|
+
end
|
78
|
+
}
|
79
|
+
if (ENV['JRUBY'])
|
80
|
+
return "JRuby"
|
81
|
+
elsif (ENV['Rubinius'])
|
82
|
+
return "Rubinius"
|
83
|
+
else
|
84
|
+
return "Ruby"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def step_uninstall()
|
89
|
+
if (ENV['RUBINIUS'])
|
90
|
+
system("rbx", "gem", "uninstall", ball())
|
91
|
+
else
|
92
|
+
system("gem", "uninstall", ball())
|
93
|
+
end
|
94
|
+
super()
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module DevBall
|
2
|
+
module PkgSpec
|
3
|
+
class Pecl < Base
|
4
|
+
depends_on "Php"
|
5
|
+
|
6
|
+
def step_extract()
|
7
|
+
end
|
8
|
+
|
9
|
+
def step_configure()
|
10
|
+
# pecl is all three steps in one at install time.
|
11
|
+
end
|
12
|
+
|
13
|
+
def step_build()
|
14
|
+
# gems is all three steps in one at install time.
|
15
|
+
end
|
16
|
+
|
17
|
+
def step_install()
|
18
|
+
Dir.chdir($package_dir) {|dir|
|
19
|
+
if(!system("pecl", "install", ball()))
|
20
|
+
raise(InstallFailed, "Could not install pecl package: #{$?}")
|
21
|
+
end
|
22
|
+
if(!system("pecl", "list-files", "apc")) # pecl install doesn't seem to always return an error code if it fails, so double check
|
23
|
+
raise(InstallFailed, "Could not install pecl package: #{$?}")
|
24
|
+
end
|
25
|
+
}
|
26
|
+
return ["Php"]
|
27
|
+
end
|
28
|
+
|
29
|
+
def step_uninstall()
|
30
|
+
system("pecl", "uninstall", ball())
|
31
|
+
super()
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module DevBall
|
2
|
+
module PkgSpec
|
3
|
+
class Tarball < Base
|
4
|
+
# overload if this package is known to extract in a funny way (ie. no top level directory included).
|
5
|
+
# default is to extract directly in the build directory.
|
6
|
+
def extract_dir_name()
|
7
|
+
File.dirname(build_dir_name)
|
8
|
+
end
|
9
|
+
|
10
|
+
def step_extract()
|
11
|
+
# figure out what type of ball it is:
|
12
|
+
ext = ball_file_name.match(/(\.tar|\.tar\.gz|\.tgz|\.tar\.bz2)$/)
|
13
|
+
if (!ext)
|
14
|
+
raise ExtractFailed, "Could not determine type of tarball."
|
15
|
+
end
|
16
|
+
ext = ext[1]
|
17
|
+
args = "xvf"
|
18
|
+
case ext
|
19
|
+
when ".tar.gz", ".tgz":
|
20
|
+
args = "z" + args
|
21
|
+
when ".tar.bz2"
|
22
|
+
args = "j" + args
|
23
|
+
end
|
24
|
+
|
25
|
+
wd = Dir.getwd
|
26
|
+
FileUtils.mkdir_p(extract_dir_name)
|
27
|
+
Dir.chdir(extract_dir_name) {|dir|
|
28
|
+
system("tar", "-#{args}", "#{wd}/#{ball_file_name}") || raise(ExtractFailed, "Could not extract tarball, error #{$?}")
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
File without changes
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: devball
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.3"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Graham Batty
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-04 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: graham at nexopia dot com
|
18
|
+
executables:
|
19
|
+
- devball-build
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
files:
|
25
|
+
- bin/devball
|
26
|
+
- bin/devball-build
|
27
|
+
- bin/devball-pull
|
28
|
+
- bin/devball-push
|
29
|
+
- lib/devball
|
30
|
+
- lib/devball/initialization.rb
|
31
|
+
- lib/devball/pkgspec
|
32
|
+
- lib/devball/pkgspec/base.rb
|
33
|
+
- lib/devball/pkgspec/djb.rb
|
34
|
+
- lib/devball/pkgspec/gem.rb
|
35
|
+
- lib/devball/pkgspec/pecl.rb
|
36
|
+
- lib/devball/pkgspec/simple_dir.rb
|
37
|
+
- lib/devball/pkgspec/tarball.rb
|
38
|
+
- lib/devball/pkgspec/test.rb
|
39
|
+
- README
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://github.com/nexopia/devball
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.3.1
|
63
|
+
signing_key:
|
64
|
+
specification_version: 2
|
65
|
+
summary: A tool for building a self-contained set of packages that can be portably be moved from one binary-compatible machine to another.
|
66
|
+
test_files: []
|
67
|
+
|