purplepkg 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/install_ruby +49 -0
- data/bin/purple +141 -0
- data/lib/purple/getter.rb +34 -0
- data/lib/purple/getter_cp.rb +22 -0
- data/lib/purple/getter_wget.rb +105 -0
- data/lib/purple/makefile.rb +59 -0
- data/lib/purple/matches.rb +20 -0
- data/lib/purple/osx.rb +117 -0
- data/lib/purple/pkg_actions.rb +95 -0
- data/lib/purple/platform.rb +15 -0
- data/lib/purple/script.rb +172 -0
- data/lib/purple.rb +221 -0
- metadata +49 -0
data/bin/install_ruby
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
if [ -z "$1" ]; then
|
4
|
+
echo "Usage: $0 ruby-<version>.tar.gz"
|
5
|
+
exit 1
|
6
|
+
fi
|
7
|
+
|
8
|
+
if [ ! -f "$1" ]; then
|
9
|
+
echo "File does not exist: $1"
|
10
|
+
exit 1
|
11
|
+
fi
|
12
|
+
|
13
|
+
if [ ! -f 'purple.gemspec' ]; then
|
14
|
+
echo "Please only run this script from purple source directory."
|
15
|
+
exit 1
|
16
|
+
fi
|
17
|
+
|
18
|
+
rm -f install_ruby.log
|
19
|
+
export TARBALL=$1
|
20
|
+
export PURPLE_TOPDIR=`pwd`
|
21
|
+
|
22
|
+
function system() {
|
23
|
+
echo -n "$1" >&2
|
24
|
+
shift
|
25
|
+
$* >> $PURPLE_TOPDIR/install_ruby.log 2>&1
|
26
|
+
STATUS=$?
|
27
|
+
echo >&2
|
28
|
+
if [ 0 -ne $STATUS ]; then
|
29
|
+
echo "Error ocurred. Please check install_ruby.log."
|
30
|
+
fi
|
31
|
+
}
|
32
|
+
|
33
|
+
# Determine ruby topdir
|
34
|
+
export RUBY_TOPDIR=/tmp/`tar tzf "$TARBALL" | head -1`
|
35
|
+
|
36
|
+
# Extract source
|
37
|
+
system "Extracting source..." tar -C /tmp xzf $TARBALL
|
38
|
+
|
39
|
+
cd $RUBY_TOPDIR
|
40
|
+
|
41
|
+
# Compile ruby
|
42
|
+
system "Configuring..." ./configure
|
43
|
+
system "Building..." make
|
44
|
+
|
45
|
+
# Run purple to stage ruby package
|
46
|
+
$RUBY_TOPDIR/miniruby -I lib -I "$RUBY_TOPDIR"/lib bin/purple -i $TARBALL
|
47
|
+
|
48
|
+
# Clean /tmp
|
49
|
+
rm -fr "$RUBY_TOPDIR"
|
data/bin/purple
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'uri/http'
|
5
|
+
require 'getoptlong'
|
6
|
+
|
7
|
+
require 'purple'
|
8
|
+
|
9
|
+
def usage
|
10
|
+
puts <<-EOS
|
11
|
+
Purple Packager, Copyright (c) 2004, Simon Conrad-Armes <curne@curnomatic.dk>
|
12
|
+
Rights granted, under GNU GPL (version 2 or later), see file LICENSE
|
13
|
+
|
14
|
+
Usage: #{File.basename $0} [options] <package>
|
15
|
+
|
16
|
+
Options:
|
17
|
+
--make Make a package from the staged install.
|
18
|
+
-m
|
19
|
+
--deploy Deploy the built package. Implies --make.
|
20
|
+
-i
|
21
|
+
--skip-to=step Skip to the given step [prepare].
|
22
|
+
--stop-at=step Stop after given step is complete [build].
|
23
|
+
--manual Print the manual page for Purple Package.
|
24
|
+
|
25
|
+
The arguments given to --skip-to and --stop-at must be one of
|
26
|
+
'prepare', 'build', 'stage', or 'deps'.
|
27
|
+
|
28
|
+
<package>
|
29
|
+
The package argument may be a url or path to a local file path. The file
|
30
|
+
ending determines how it is handled:
|
31
|
+
.tar.gz Source Tarball assumed. A purple spec is created
|
32
|
+
inferring the name and version from the filename.
|
33
|
+
.purple Purple spec script. Contains references to source and
|
34
|
+
build instructions.
|
35
|
+
.purple-src Purple source package. Contains source files and build
|
36
|
+
instructions.
|
37
|
+
.purple-bin Purple binary package.
|
38
|
+
|
39
|
+
EOS
|
40
|
+
exit 1
|
41
|
+
end
|
42
|
+
|
43
|
+
def filetype url
|
44
|
+
filename = File.basename(URI.parse(url).path)
|
45
|
+
return case filename
|
46
|
+
when /\.tar\.(gz|bz2)|\.tgz$/
|
47
|
+
:tarball
|
48
|
+
when /\.purple$/
|
49
|
+
:script
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
ALL_STEPS = { :prepare => 0, :build => 2, :stage => 4, :deps => 6 }
|
54
|
+
|
55
|
+
|
56
|
+
usage if 0 == ARGV.size
|
57
|
+
|
58
|
+
opts = GetoptLong.new
|
59
|
+
opts.set_options(
|
60
|
+
[ '--help', GetoptLong::OPTIONAL_ARGUMENT],
|
61
|
+
[ '--make', '-m', GetoptLong::NO_ARGUMENT],
|
62
|
+
[ '--deploy', '-i', GetoptLong::NO_ARGUMENT],
|
63
|
+
[ '--skip-to', GetoptLong::REQUIRED_ARGUMENT],
|
64
|
+
[ '--stop-at', GetoptLong::REQUIRED_ARGUMENT],
|
65
|
+
[ '--manual', GetoptLong::REQUIRED_ARGUMENT]
|
66
|
+
)
|
67
|
+
|
68
|
+
OPTIONS = Hash.new
|
69
|
+
opts.each_option { |name,arg| OPTIONS[name.gsub(/^--/, '')] = arg }
|
70
|
+
p OPTIONS
|
71
|
+
|
72
|
+
usage if OPTIONS.has_key? 'help'
|
73
|
+
|
74
|
+
steps = [:prepare, :config, :build, :stage]
|
75
|
+
|
76
|
+
if OPTIONS.has_key? 'make' or 'deps' == OPTIONS['stop-at']
|
77
|
+
steps << :deps
|
78
|
+
end
|
79
|
+
|
80
|
+
if OPTIONS.has_key? 'skip-to'
|
81
|
+
if 'make' == OPTIONS['skip-to']
|
82
|
+
first = steps.size
|
83
|
+
else
|
84
|
+
first = steps.index OPTIONS['skip-to'].intern
|
85
|
+
end
|
86
|
+
else
|
87
|
+
first = 0
|
88
|
+
end
|
89
|
+
|
90
|
+
if OPTIONS.has_key? 'stop-at'
|
91
|
+
last = steps.index OPTIONS['stop-at'].intern
|
92
|
+
else
|
93
|
+
last = steps.size - 1
|
94
|
+
end
|
95
|
+
|
96
|
+
steps = steps[first..last]
|
97
|
+
|
98
|
+
|
99
|
+
# Convert local paths to urls
|
100
|
+
arg_ref = ARGV[0]
|
101
|
+
arg_uri = URI.parse(arg_ref)
|
102
|
+
if not arg_uri.scheme
|
103
|
+
arg_uri.scheme = 'file'
|
104
|
+
arg_uri.path = File.join(Dir.pwd, arg_uri.path) if arg_uri.path !~ %r"^/"
|
105
|
+
end
|
106
|
+
arg_ref = arg_uri.to_s
|
107
|
+
|
108
|
+
# Decide what to do with the argument
|
109
|
+
puts "DEBUG main: #{filetype(arg_ref).inspect}"
|
110
|
+
case filetype(arg_ref)
|
111
|
+
when :tarball
|
112
|
+
@cabinet = Purple.cabinet_from_url arg_ref
|
113
|
+
when :script
|
114
|
+
@cabinet = Purple::Script.open URI.parse(arg_ref).path
|
115
|
+
else
|
116
|
+
raise "Unknown file type of argument '#{arg_ref}'"
|
117
|
+
end
|
118
|
+
p @cabinet
|
119
|
+
|
120
|
+
error = catch :stop do
|
121
|
+
@cabinet.setup
|
122
|
+
|
123
|
+
@cabinet.run_steps steps
|
124
|
+
|
125
|
+
if OPTIONS.has_key? 'make' or OPTIONS.has_key? 'deploy'
|
126
|
+
Purple::Platform.detect
|
127
|
+
@cabinet.make
|
128
|
+
end
|
129
|
+
|
130
|
+
if OPTIONS.has_key? 'deploy'
|
131
|
+
puts "DEBUG: Doing deploy"
|
132
|
+
@cabinet.deploy
|
133
|
+
end
|
134
|
+
|
135
|
+
:normal_complete
|
136
|
+
end
|
137
|
+
|
138
|
+
if :normal_complete != error
|
139
|
+
STDERR.puts "An error condition was encountered and forced a stop."
|
140
|
+
STDERR.puts "Reason: #{error}" if error
|
141
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Purple
|
4
|
+
module Getter
|
5
|
+
def self.new url, destdir
|
6
|
+
puts "DEBUG Getter##new: url=#{url}"
|
7
|
+
@uri = URI.parse url
|
8
|
+
plugin = case @uri.scheme
|
9
|
+
when /http|ftp/
|
10
|
+
'wget'
|
11
|
+
when 'file'
|
12
|
+
'cp'
|
13
|
+
else
|
14
|
+
raise StandardError.new("Unable to handle scheme #{@uri.scheme}")
|
15
|
+
end
|
16
|
+
require "purple/getter_#{plugin}"
|
17
|
+
(eval "Purple::Getter::Getter_#{plugin}").new @uri, destdir
|
18
|
+
end
|
19
|
+
|
20
|
+
class Generic
|
21
|
+
# -> anIO
|
22
|
+
def initialize uri, destdir
|
23
|
+
self.uri = uri
|
24
|
+
@status = 'Getting'
|
25
|
+
@destdir = destdir
|
26
|
+
end
|
27
|
+
attr_accessor :uri, :length, :content_type
|
28
|
+
attr_reader :thread
|
29
|
+
|
30
|
+
# def get -> nil
|
31
|
+
# def get_status -> anArray [status, %done]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module Purple
|
4
|
+
module Getter
|
5
|
+
class Getter_cp < Generic
|
6
|
+
def get
|
7
|
+
if system "ln '#{@uri.path}' '#{@destdir}/'"
|
8
|
+
@done = '100'
|
9
|
+
@status = 'Done'
|
10
|
+
else
|
11
|
+
@done = '100'
|
12
|
+
@status = 'Error'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_status
|
17
|
+
[@status, @done]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module Purple
|
4
|
+
module Getter
|
5
|
+
class Getter_wget < Generic
|
6
|
+
def initialize *args
|
7
|
+
super
|
8
|
+
@done = "0"
|
9
|
+
end
|
10
|
+
|
11
|
+
def get
|
12
|
+
@thread = Thread.new do
|
13
|
+
filename = File.basename uri.path
|
14
|
+
Open3.popen3 "wget --progress dot -O '#{@destdir}/#{filename}' #{@uri.to_s} 2>&1" do
|
15
|
+
|pw, pr, pe|
|
16
|
+
parse_input pw, pr, pe
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_status
|
22
|
+
[@status, @done]
|
23
|
+
end
|
24
|
+
|
25
|
+
# wget specific methods
|
26
|
+
|
27
|
+
def parse_input pw, pr, pe
|
28
|
+
#puts "DEBUG Getter_wget#parse_input: Start"
|
29
|
+
|
30
|
+
loop do
|
31
|
+
# Start line
|
32
|
+
2.times { pr.gets }
|
33
|
+
|
34
|
+
# Host lookup
|
35
|
+
#puts "DEBUG Getter_wget#parse_input: Host lookup"
|
36
|
+
line = pr.gets
|
37
|
+
#puts "->#{line}"
|
38
|
+
if line =~ /failed:/
|
39
|
+
@status = 'Host lookup failed'
|
40
|
+
return
|
41
|
+
end
|
42
|
+
|
43
|
+
# Connecting
|
44
|
+
#puts "DEBUG Getter_wget#parse_input: Connecting"
|
45
|
+
line = pr.gets
|
46
|
+
#puts "->#{line}"
|
47
|
+
md = /^Connecting to (.*?)... (.*?)$/.match line
|
48
|
+
#puts "DEBUG Getter_wget#parse_input: md[2]=#{md[2].inspect}"
|
49
|
+
|
50
|
+
unless 'connected.' == md[2]
|
51
|
+
@status = 'Connection failed'
|
52
|
+
return
|
53
|
+
end
|
54
|
+
|
55
|
+
# Request
|
56
|
+
#puts "DEBUG Getter_wget#parse_input: Sending request"
|
57
|
+
line = pr.gets
|
58
|
+
#puts "->#{line}"
|
59
|
+
md = /HTTP request sent, awaiting response... (.*)/.match line
|
60
|
+
case md[1]
|
61
|
+
when '200 OK'
|
62
|
+
# Okay. Continue
|
63
|
+
when /^3\d{2}/
|
64
|
+
# Redirection
|
65
|
+
pr.gets # Location line
|
66
|
+
redo
|
67
|
+
else
|
68
|
+
@status = md[1]
|
69
|
+
return
|
70
|
+
end
|
71
|
+
|
72
|
+
break
|
73
|
+
end
|
74
|
+
|
75
|
+
# Content details
|
76
|
+
line = pr.gets
|
77
|
+
#puts "->#{line}"
|
78
|
+
md = /Length: (.*?) \[(.*?)\]/.match line
|
79
|
+
@length = md[1].gsub(',', '').to_i
|
80
|
+
@content_type = md[2]
|
81
|
+
|
82
|
+
# Empty line
|
83
|
+
pr.gets
|
84
|
+
|
85
|
+
# Data incoming
|
86
|
+
pr.each do |line|
|
87
|
+
#puts "->#{line}"
|
88
|
+
|
89
|
+
md = / *([0-9]+)K[ \.]+([0-9]+)% +([0-9\.,]+ KB\/s)/.match line
|
90
|
+
#puts (md ? "DEBUG #{md.to_a.inspect}" : "DEBUG nil")
|
91
|
+
if md
|
92
|
+
@done = md[2]
|
93
|
+
@rate = md[3]
|
94
|
+
next
|
95
|
+
end
|
96
|
+
|
97
|
+
if "100" == @done
|
98
|
+
@status = "Done"
|
99
|
+
end
|
100
|
+
pr.read
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
|
2
|
+
module Purple
|
3
|
+
|
4
|
+
# Makefile aware predicate container. Initialize with path
|
5
|
+
# to source directory.
|
6
|
+
class MakefileMaven
|
7
|
+
def initialize srcdir
|
8
|
+
@srcdir = srcdir
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_makefile?
|
12
|
+
makefilename && true
|
13
|
+
end
|
14
|
+
|
15
|
+
def makefilename
|
16
|
+
return @makefilename if defined? @makefilename
|
17
|
+
@makefilename = ['GNUmakefile', 'makefile', 'Makefile'].find do |name|
|
18
|
+
puts "DEBUG MakefileMaven#makefilename: testing #{File.join(@srcdir, name)}"
|
19
|
+
FileTest.exist? File.join(@srcdir, name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def uses_prefix?
|
24
|
+
return true if makefile_rules.grep(/^prefix/).size > 0
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def uses_destdir?
|
29
|
+
return true if makefile_rules.grep(/DESTDIR/).size > 0
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def makefile_rules
|
34
|
+
return @rules if defined? @rules
|
35
|
+
@rules = `make -pn -C #{@srcdir}`
|
36
|
+
end
|
37
|
+
|
38
|
+
def makefile_contents
|
39
|
+
return @contents if defined? @contents
|
40
|
+
|
41
|
+
@contents = ''
|
42
|
+
files = [makefilename];
|
43
|
+
while files.size > 0
|
44
|
+
filename = File.join(@srcdir, files.shift)
|
45
|
+
if FileTest.exist? filename
|
46
|
+
puts "DEBUG MakefileMaven#makefile_contents: loop->if"
|
47
|
+
text = File.open(filename) { |f| f.read }
|
48
|
+
@contents << text
|
49
|
+
text.grep(/^include /).each do |s|
|
50
|
+
md = /^include (.*?)\n$/.match s
|
51
|
+
files << md[1]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
puts "DEBUG MakefileMaven#makefile_contents: #{@contents.to_a.size} lines"
|
56
|
+
@contents
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
module Purple
|
3
|
+
|
4
|
+
module Matches
|
5
|
+
EXTENSIONS = '(\.tar\.gz|\.tar\.bz2|\.tgz|\.zip)'
|
6
|
+
REGEXPS = [
|
7
|
+
/^(.+)-((\d+)(\.\d+)*(-?\w+)?)#{EXTENSIONS}$/,
|
8
|
+
/^(.+)\.([^\.]+?)#{EXTENSIONS}$/,
|
9
|
+
];
|
10
|
+
|
11
|
+
def self.match_filename filename
|
12
|
+
md = nil
|
13
|
+
REGEXPS.find { |r| md = r.match filename }
|
14
|
+
#puts "DEBUG Matches#match_filename: filename=#{filename.inspect} md=#{md.to_a.inspect}"
|
15
|
+
raise 'filename does not match pattern' if not (md and not md.to_a[0..1].include? nil)
|
16
|
+
|
17
|
+
{ :name => md[1], :version => md[2] }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/purple/osx.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
|
2
|
+
module Purple
|
3
|
+
|
4
|
+
PACKAGE_DESCRIPTION_TEMPLATE = proc do |pkg| <<EOS
|
5
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
6
|
+
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
7
|
+
<plist version="1.0">
|
8
|
+
<dict>
|
9
|
+
<key>IFPkgDescriptionTitle</key>
|
10
|
+
<string>#{pkg.name}</string>
|
11
|
+
<key>IFPkgDescriptionVersion</key>
|
12
|
+
<string>#{pkg.version}</string>
|
13
|
+
</dict>
|
14
|
+
</plist>
|
15
|
+
EOS
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
PACKAGE_INFO_TEMPLATE = proc do |pkg| <<EOS
|
20
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
21
|
+
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
22
|
+
<plist version="1.0">
|
23
|
+
<dict>
|
24
|
+
<!--
|
25
|
+
<key></key>
|
26
|
+
<string></string>
|
27
|
+
***
|
28
|
+
-->
|
29
|
+
<key>CFBundleGetInfoString</key>
|
30
|
+
<string>#{pkg.info_string}</string>
|
31
|
+
<key>CFBundleIdentifier</key>
|
32
|
+
<string>#{pkg.identifier}</string>
|
33
|
+
<key>CFBundleName</key>
|
34
|
+
<string>#{pkg.name}</string>
|
35
|
+
<key>CFBundleShortVersionString</key>
|
36
|
+
<string>#{pkg.version}</string>
|
37
|
+
<key>IFMajorVersion</key>
|
38
|
+
<integer>#{pkg.major}</integer>
|
39
|
+
<key>IFMinorVersion</key>
|
40
|
+
<integer>#{pkg.minor}</integer>
|
41
|
+
<key>IFPkgFlagAllowBackRev</key>
|
42
|
+
<false/>
|
43
|
+
<key>IFPkgFlagAuthorizationAction</key>
|
44
|
+
<string>AdminAuthorization</string>
|
45
|
+
<key>IFPkgFlagDefaultLocation</key>
|
46
|
+
<string>/</string>
|
47
|
+
<key>IFPkgFlagInstallFat</key>
|
48
|
+
<false/>
|
49
|
+
<key>IFPkgFlagIsRequired</key>
|
50
|
+
<false/>
|
51
|
+
<key>IFPkgFlagOverwritePermissions</key>
|
52
|
+
<true/>
|
53
|
+
<key>IFPkgFlagRelocatable</key>
|
54
|
+
<false/>
|
55
|
+
<key>IFPkgFlagRestartAction</key>
|
56
|
+
<string>NoRestart</string>
|
57
|
+
<key>IFPkgFlagRootVolumeOnly</key>
|
58
|
+
<true/>
|
59
|
+
<key>IFPkgFlagUpdateInstalledLanguages</key>
|
60
|
+
<false/>
|
61
|
+
<key>IFPkgFlagUseUserMask</key>
|
62
|
+
<false/>
|
63
|
+
<key>IFPkgFormatVersion</key>
|
64
|
+
<real>0.10000000149011612</real>
|
65
|
+
</dict>
|
66
|
+
</plist>
|
67
|
+
EOS
|
68
|
+
end
|
69
|
+
|
70
|
+
class PackageInfo
|
71
|
+
def info_string; long_name; end
|
72
|
+
end
|
73
|
+
|
74
|
+
class PurpleCabinet
|
75
|
+
def osx_stage
|
76
|
+
assert_dir "#{@cabinet_dir}"
|
77
|
+
assert_dir "#{@cabinet_dir}/OsxStage"
|
78
|
+
assert_dir File.join(@cabinet_dir, 'OsxStage')
|
79
|
+
end
|
80
|
+
|
81
|
+
def make
|
82
|
+
packages.each do |pkg|
|
83
|
+
system "rm -r '#{osx_stage}'"
|
84
|
+
File.open "#{osx_stage}/Description.plist", "w" do |f|
|
85
|
+
f.puts pkg.description
|
86
|
+
end
|
87
|
+
|
88
|
+
Dir.mkdir "#{osx_stage}/Resources"
|
89
|
+
File.open "#{osx_stage}/Resources/Welcome.txt", "w" do |f|
|
90
|
+
f.print <<-EOS
|
91
|
+
Hello, World!
|
92
|
+
EOS
|
93
|
+
end
|
94
|
+
|
95
|
+
File.open "#{osx_stage}/Info.plist", "w" do |f|
|
96
|
+
f.print PACKAGE_INFO_TEMPLATE.call(pkg)
|
97
|
+
end
|
98
|
+
|
99
|
+
File.open "#{osx_stage}/Description.plist", "w" do |f|
|
100
|
+
f.print PACKAGE_DESCRIPTION_TEMPLATE.call(pkg)
|
101
|
+
end
|
102
|
+
|
103
|
+
system "/Developer/Applications/Utilities/PackageMaker.app/Contents/MacOS/PackageMaker " +
|
104
|
+
"-build -p '#{packagedir}/#{pkg.name}.pkg' -f '#{pkg.stage_root}' -r '#{osx_stage}/Resources' " +
|
105
|
+
"-d '#{osx_stage}/Description.plist' -i '#{osx_stage}/Info.plist'"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
def deploy
|
111
|
+
packages.each do |pkg|
|
112
|
+
system "open '#{File.join packagedir, pkg.name}.pkg'"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
|
2
|
+
module Purple
|
3
|
+
module PackageActions
|
4
|
+
|
5
|
+
attr_accessor :cabinet
|
6
|
+
|
7
|
+
def self.package_method name
|
8
|
+
prg = <<-EOS
|
9
|
+
def #{name}
|
10
|
+
if defined? @method_#{name}_package and @method_#{name}_package
|
11
|
+
return @method_#{name}_package.call(self)
|
12
|
+
else
|
13
|
+
m = self.method "default_#{name}"
|
14
|
+
return(m.call) if m
|
15
|
+
end
|
16
|
+
end
|
17
|
+
def #{name}= prc
|
18
|
+
@method_#{name}_package = prc
|
19
|
+
end
|
20
|
+
EOS
|
21
|
+
#puts prg
|
22
|
+
module_eval prg
|
23
|
+
end
|
24
|
+
|
25
|
+
def srcdir
|
26
|
+
return @srcdir if defined? @srcdir
|
27
|
+
dirs = Dir.new(cabinet.srcdir).reject { |s| s =~ /^\.{1,2}$/ }
|
28
|
+
if 1 == dirs.size
|
29
|
+
@srcdir = "#{cabinet.srcdir}/#{dirs[0]}"
|
30
|
+
else
|
31
|
+
@srcdir = Dir.new(cabinet.srcdir).find { |s| s =~ Regexp.new('^' + Regexp.escape(name)) }
|
32
|
+
end
|
33
|
+
throw :stop, "Unable to determine source directory for package #{name}" if not @srcdir
|
34
|
+
puts "Choosing #{@srcdir} as source code folder"
|
35
|
+
@srcdir
|
36
|
+
end
|
37
|
+
|
38
|
+
def stage_root
|
39
|
+
File.join cabinet.stage_root, long_name
|
40
|
+
end
|
41
|
+
|
42
|
+
package_method :prepare
|
43
|
+
package_method :config
|
44
|
+
package_method :build
|
45
|
+
package_method :stage
|
46
|
+
package_method :make
|
47
|
+
|
48
|
+
def default_prepare
|
49
|
+
puts "DEBUG PackageActions#default_prepare"
|
50
|
+
files.each do |f|
|
51
|
+
unpack = case f
|
52
|
+
when /gz$/; '-z'
|
53
|
+
when /bz2$/; '-j'
|
54
|
+
else ''
|
55
|
+
end
|
56
|
+
if not Purple.sys "tar x #{unpack} -C '#{cabinet.srcdir}' -f #{cabinet.filesdir}/#{f}"
|
57
|
+
throw :stop
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
def default_config
|
64
|
+
throw :stop unless system "cd #{srcdir}; ./configure --prefix=/usr"
|
65
|
+
end
|
66
|
+
|
67
|
+
def default_build
|
68
|
+
throw :stop unless system "make -C '#{srcdir}'"
|
69
|
+
end
|
70
|
+
|
71
|
+
def default_stage
|
72
|
+
system "rm -fr '#{stage_root}/*'"
|
73
|
+
|
74
|
+
require 'purple/makefile'
|
75
|
+
|
76
|
+
puts "DEBUG PackageActions#default_stage: srcdir=#{srcdir}"
|
77
|
+
mkm = MakefileMaven.new srcdir
|
78
|
+
if mkm.uses_prefix?
|
79
|
+
if mkm.uses_destdir?
|
80
|
+
make_args = "DESTDIR='#{stage_root}'"
|
81
|
+
else
|
82
|
+
make_args = "prefix='#{stage_root}/usr'"
|
83
|
+
end
|
84
|
+
else
|
85
|
+
if mkm.uses_destdir?
|
86
|
+
make_args = "DESTDIR='#{stage_root}'"
|
87
|
+
else
|
88
|
+
raise 'Unable to find a way to stage! Package neither defines prefix or DESTDIR'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
throw :stop if not system "make -C '#{srcdir}' #{make_args} install"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
module Purple
|
3
|
+
module Platform
|
4
|
+
def self.detect
|
5
|
+
case true
|
6
|
+
when FileTest.exist?('/etc/debian_version')
|
7
|
+
system_brand = :debian
|
8
|
+
when (FileTest.directory?('/System/Library/') and FileTest.file?('/mach_kernel'))
|
9
|
+
system_brand = :osx
|
10
|
+
end
|
11
|
+
|
12
|
+
require "purple/#{system_brand}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'uri'
|
3
|
+
require 'purple'
|
4
|
+
|
5
|
+
|
6
|
+
PURPLE_SCRIPT_TEMPLATE = proc do |cabinet| <<EOS
|
7
|
+
# Autogenerated Purple Packaging script.
|
8
|
+
# Cabinet name: '#{cabinet.name}'
|
9
|
+
|
10
|
+
name #{cabinet.name.inspect}
|
11
|
+
long_name #{cabinet.long_name.inspect}
|
12
|
+
|
13
|
+
#{ cabinet.urls.collect { |k,v| "url #{v.inspect}\n" } }
|
14
|
+
|
15
|
+
#{
|
16
|
+
cabinet.packages.collect { |pkg| PURPLE_PACKAGE_TEMPLATE.call pkg }
|
17
|
+
}
|
18
|
+
EOS
|
19
|
+
end
|
20
|
+
|
21
|
+
PURPLE_PACKAGE_TEMPLATE = proc do |pkg| <<EOS
|
22
|
+
package(#{pkg.name.inspect}) { |p|
|
23
|
+
#{pkg.files.collect{ |s| " p.file #{s.inspect}" }.join("\n") }
|
24
|
+
p.long_name #{pkg.long_name.inspect}
|
25
|
+
p.identifier #{pkg.identifier.inspect}
|
26
|
+
p.version #{pkg.version.inspect}
|
27
|
+
p.major #{pkg.major}
|
28
|
+
p.minor #{pkg.minor}
|
29
|
+
|
30
|
+
# Uncomment and modify for special configuration
|
31
|
+
#p.config proc { |pkg|
|
32
|
+
# throw :stop unless system "cd \#{pkg.srcdir}; ./configure --prefix=/usr"
|
33
|
+
#}
|
34
|
+
|
35
|
+
}
|
36
|
+
EOS
|
37
|
+
end
|
38
|
+
|
39
|
+
module Purple
|
40
|
+
class Script
|
41
|
+
def self.open filename
|
42
|
+
parse File.open(filename).read
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.parse string
|
46
|
+
script = self.new
|
47
|
+
script.instance_eval string
|
48
|
+
puts "DEBUG Purple::Script##parse: cabinet=#{script.cabinet}"
|
49
|
+
script.cabinet
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize
|
53
|
+
@cabinet = PurpleCabinet.new
|
54
|
+
@package_scripts = Array.new
|
55
|
+
end
|
56
|
+
attr_reader :cabinet
|
57
|
+
|
58
|
+
def name name
|
59
|
+
@cabinet.name = name
|
60
|
+
end
|
61
|
+
|
62
|
+
def long_name name
|
63
|
+
@cabinet.long_name = name
|
64
|
+
end
|
65
|
+
|
66
|
+
def url url
|
67
|
+
@cabinet.add_url url
|
68
|
+
end
|
69
|
+
|
70
|
+
def package name=nil
|
71
|
+
puts "> #package(#{name.inspect})"
|
72
|
+
package = PackageScript.new @cabinet
|
73
|
+
@package_scripts << package
|
74
|
+
package.name name
|
75
|
+
yield package if block_given?
|
76
|
+
end
|
77
|
+
|
78
|
+
def infer
|
79
|
+
if not @cabinet.long_name
|
80
|
+
@cabinet.long_name = @cabinet.packages[0].long_name
|
81
|
+
end
|
82
|
+
if not @cabinet.name
|
83
|
+
@cabinet.name = @cabinet.packages[0].name
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def infer_all
|
88
|
+
@package_scripts.each { |pkg|
|
89
|
+
puts "DEBUG PurpleScript#infer_all"
|
90
|
+
pkg.infer
|
91
|
+
}
|
92
|
+
puts "DEBUG PurpleScript#infer_all: b4end"
|
93
|
+
infer
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.write cabinet, filename
|
97
|
+
File.open filename, 'w' do |f|
|
98
|
+
f.write dump(cabinet)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
def self.dump cabinet
|
102
|
+
PURPLE_SCRIPT_TEMPLATE.call cabinet
|
103
|
+
end
|
104
|
+
|
105
|
+
class PackageScript
|
106
|
+
def self.callthrough (*ss)
|
107
|
+
ss.each do |s|
|
108
|
+
module_eval <<-EOS
|
109
|
+
def #{s} value
|
110
|
+
@package.#{s} = value
|
111
|
+
end
|
112
|
+
EOS
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def initialize cabinet
|
117
|
+
@package = cabinet.create_package
|
118
|
+
@cabinet = cabinet
|
119
|
+
puts "DEBUG PackageScript#initialize: @package=#{@package}"
|
120
|
+
end
|
121
|
+
|
122
|
+
callthrough :long_name, :name, :identifier, :version, :major, :minor
|
123
|
+
callthrough :prepare, :config, :build, :stage, :make
|
124
|
+
|
125
|
+
def pkg
|
126
|
+
@package
|
127
|
+
end
|
128
|
+
|
129
|
+
def file filename
|
130
|
+
pkg.files << filename
|
131
|
+
end
|
132
|
+
|
133
|
+
def infer
|
134
|
+
puts "DEBUG #infer"
|
135
|
+
puts "DEBUG PackageScript#infer: pkg=#{pkg}"
|
136
|
+
# Take a guess at the filename
|
137
|
+
if 0 == pkg.files.size
|
138
|
+
files = @cabinet.files
|
139
|
+
puts "DEBUG #infer: cabinet_files=#{files.inspect}"
|
140
|
+
if 0 == files.size
|
141
|
+
warn 'Cabinet files section is empty. Did you not specify any urls?'
|
142
|
+
else
|
143
|
+
if pkg.long_name
|
144
|
+
# See if any filenames match the long name
|
145
|
+
rs = files.grep Regexp.new(Regexp.escape(pkg.long_name))
|
146
|
+
file rs[0] if 1 == rs.size
|
147
|
+
else
|
148
|
+
# Else just guess that the first file is the right one
|
149
|
+
file files[0]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
#puts "DEBUG #infer: pkg.files=#{pkg.files.inspect}"
|
155
|
+
#puts "DEBUG #infer: long_name=#{pkg.long_name}"
|
156
|
+
# Take a guess at properties long_name, name, version, major, and minor
|
157
|
+
md = Matches.match_filename pkg.files[0]
|
158
|
+
puts "DEBUG #infer: filename match result #{md.inspect}"
|
159
|
+
|
160
|
+
pkg.long_name = "#{md[:name]}-#{md[:version]}" if not pkg.long_name
|
161
|
+
pkg.name = md[:name] if not pkg.name
|
162
|
+
pkg.version = md[:version] if not pkg.version
|
163
|
+
|
164
|
+
md = /^([0-9]+)\.([0-9]+).*/.match pkg.version
|
165
|
+
pkg.major = md[1] if md and not pkg.major
|
166
|
+
pkg.minor = md[2] if md and not pkg.minor
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
end
|
data/lib/purple.rb
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Purple
|
5
|
+
autoload :PackageActions, 'purple/pkg_actions'
|
6
|
+
autoload :Getter, 'purple/getter'
|
7
|
+
autoload :Script, 'purple/script'
|
8
|
+
autoload :Matches, 'purple/matches'
|
9
|
+
autoload :Platform, 'purple/platform'
|
10
|
+
PURPLE_FOLDER = "#{ENV['HOME']}/purple"
|
11
|
+
|
12
|
+
|
13
|
+
class PurpleCabinet
|
14
|
+
def initialize
|
15
|
+
@packages = Array.new
|
16
|
+
@urls = Hash.new
|
17
|
+
@files = Array.new
|
18
|
+
end
|
19
|
+
attr_accessor :name, :long_name
|
20
|
+
attr_reader :urls, :files, :packages, :cabinet_dir
|
21
|
+
|
22
|
+
# -> aString # Get source dir
|
23
|
+
def filesdir
|
24
|
+
assert_dir File.join(@cabinet_dir, 'Files')
|
25
|
+
end
|
26
|
+
|
27
|
+
def srcdir
|
28
|
+
assert_dir File.join(@cabinet_dir, 'Source')
|
29
|
+
end
|
30
|
+
|
31
|
+
def stage_root
|
32
|
+
assert_dir File.join(@cabinet_dir, 'StageRoot')
|
33
|
+
end
|
34
|
+
|
35
|
+
def packagedir
|
36
|
+
assert_dir File.join(@cabinet_dir, 'Package')
|
37
|
+
end
|
38
|
+
|
39
|
+
def fetch_all
|
40
|
+
urls.each do |url|
|
41
|
+
Getter.get url, filesdir
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_url url
|
46
|
+
filename = File.basename URI.parse(url).path
|
47
|
+
@urls[filename] = url
|
48
|
+
@files << filename
|
49
|
+
end
|
50
|
+
|
51
|
+
def setup
|
52
|
+
raise 'cabinet long_name not properly set' if not long_name
|
53
|
+
@cabinet_dir = File.join(PURPLE_FOLDER, long_name)
|
54
|
+
assert_dir @cabinet_dir
|
55
|
+
end
|
56
|
+
|
57
|
+
def fetch filename
|
58
|
+
if not FileTest.exist? "#{filesdir}/#{filename}"
|
59
|
+
g = Getter.new urls[filename], filesdir
|
60
|
+
g.get
|
61
|
+
status = 'Getting'
|
62
|
+
loop do
|
63
|
+
status, done = *(g.get_status)
|
64
|
+
printf "\015\033[MFetching %-40s %3d%% %s", filename, done, status
|
65
|
+
$defout.flush
|
66
|
+
sleep 1
|
67
|
+
break if 'Getting' != status
|
68
|
+
end
|
69
|
+
puts
|
70
|
+
throw :stop, "There was an error while fetching file #{filename}" unless 'Done' == status
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def prepare
|
75
|
+
@packages.each do |p|
|
76
|
+
p.files.each { |f| fetch f }
|
77
|
+
p.prepare
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def config
|
82
|
+
@packages.each { |p|
|
83
|
+
return false if not p.config
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def build
|
88
|
+
@packages.each { |p|
|
89
|
+
return false if not p.build
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
def stage
|
94
|
+
@packages.each { |p|
|
95
|
+
return false if not p.stage
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
def deps
|
100
|
+
true
|
101
|
+
end
|
102
|
+
|
103
|
+
def run_steps steps=[:prepare, :build, :deps, :make]
|
104
|
+
steps.each do |step|
|
105
|
+
self.method(step).call
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
## ##
|
110
|
+
|
111
|
+
def create_package
|
112
|
+
p = PackageInfo.new
|
113
|
+
class <<p
|
114
|
+
include PackageActions
|
115
|
+
end
|
116
|
+
p.cabinet = self
|
117
|
+
@packages << p
|
118
|
+
@packages.last
|
119
|
+
end
|
120
|
+
|
121
|
+
## ##
|
122
|
+
|
123
|
+
def assert_dir path
|
124
|
+
unless FileTest.exist? path
|
125
|
+
puts ">- Creating folder #{path}"
|
126
|
+
Dir.mkdir path
|
127
|
+
end
|
128
|
+
(FileTest.directory? path) ? path : nil
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
class PackageInfo
|
134
|
+
def initialize
|
135
|
+
@files = []
|
136
|
+
end
|
137
|
+
attr_accessor :long_name, :name, :version, :major, :minor, :identifier, :description
|
138
|
+
attr_reader :files
|
139
|
+
end
|
140
|
+
|
141
|
+
class PurpleFile
|
142
|
+
def self.parse str
|
143
|
+
pkg = PackageInfo.new
|
144
|
+
receiver_stack = ['main']
|
145
|
+
str.split(/(\r\n|\r|\n)+/).each do |line|
|
146
|
+
r = self.method("__#{receiver_stack.last}").call line.strip, pkg
|
147
|
+
case r
|
148
|
+
when -1
|
149
|
+
receiver_stack.pop
|
150
|
+
when nil
|
151
|
+
else
|
152
|
+
receiver_stack.push r
|
153
|
+
end
|
154
|
+
end
|
155
|
+
pkg
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.__main line, pkg
|
159
|
+
case line
|
160
|
+
when /^package/
|
161
|
+
pkg.name = line.gsub(/^package\s+/, '')
|
162
|
+
return 'package'
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
PROPS = 'longname|version|major|minor|identifier|description|url'
|
167
|
+
def self.__package line, pkg
|
168
|
+
if (md = Regexp.new("^(#{PROPS})\s+(.*)$").match line)
|
169
|
+
pkg.method("#{md[1]}=").call md[2]
|
170
|
+
else
|
171
|
+
self.infer_properties pkg if line == 'infer'
|
172
|
+
end
|
173
|
+
nil
|
174
|
+
end
|
175
|
+
|
176
|
+
def self.infer_properties pkg
|
177
|
+
if pkg.url
|
178
|
+
if pkg.url =~ /prdownloads.sourceforge.net/
|
179
|
+
md = Regexp.new 'http://prdownloads.sourceforge.net/([^/])/(.*?)tar.(gz|bz2)?download'
|
180
|
+
pkg.long_name = md[2]
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
pkg.long_name = File.basename Dir.pwd if not pkg.long_name
|
185
|
+
|
186
|
+
md = /([a-z\-]+)-([0-9\-\.]+.*)/.match pkg.long_name
|
187
|
+
pkg.version = md[2] if not pkg.version
|
188
|
+
|
189
|
+
md = /^([0-9]+)\.([0-9]+)/.match pkg.version
|
190
|
+
pkg.major = md[1] if not pkg.major
|
191
|
+
pkg.minor = md[2] if not pkg.minor
|
192
|
+
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def self.cabinet_from_url url
|
197
|
+
|
198
|
+
cabinet = Purple::Script.parse(<<-EOS
|
199
|
+
url #{url.inspect}
|
200
|
+
package # {|pkg| pkg.infer }
|
201
|
+
infer_all
|
202
|
+
EOS
|
203
|
+
)
|
204
|
+
cabinet.setup
|
205
|
+
Purple::Script.write cabinet, File.join(cabinet.cabinet_dir, cabinet.name + '.purple')
|
206
|
+
cabinet
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.sys cmd
|
210
|
+
system cmd
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
module Kernel
|
216
|
+
alias_method :_system_, :system
|
217
|
+
def system(*args)
|
218
|
+
puts "> #{args.join " "}"
|
219
|
+
_system_(*args)
|
220
|
+
end
|
221
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.1
|
3
|
+
specification_version: 1
|
4
|
+
name: purplepkg
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.3
|
7
|
+
date: 2004-11-12
|
8
|
+
summary: A simple pre-packing tool with meta-package plugin support.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
author: Simon Conrad-Armes
|
12
|
+
email: curne@curnomatic.dk
|
13
|
+
homepage: purplepkg.rubyforge.org
|
14
|
+
rubyforge_project: purple
|
15
|
+
description:
|
16
|
+
autorequire:
|
17
|
+
default_executable:
|
18
|
+
bindir: bin
|
19
|
+
has_rdoc: false
|
20
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
21
|
+
requirements:
|
22
|
+
-
|
23
|
+
- ">"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 0.0.0
|
26
|
+
version:
|
27
|
+
platform: ruby
|
28
|
+
files:
|
29
|
+
- lib/purple
|
30
|
+
- lib/purple.rb
|
31
|
+
- lib/purple/matches.rb
|
32
|
+
- lib/purple/getter_cp.rb
|
33
|
+
- lib/purple/getter_wget.rb
|
34
|
+
- lib/purple/osx.rb
|
35
|
+
- lib/purple/pkg_actions.rb
|
36
|
+
- lib/purple/script.rb
|
37
|
+
- lib/purple/makefile.rb
|
38
|
+
- lib/purple/getter.rb
|
39
|
+
- lib/purple/platform.rb
|
40
|
+
- bin/purple
|
41
|
+
- bin/install_ruby
|
42
|
+
test_files: []
|
43
|
+
rdoc_options: []
|
44
|
+
extra_rdoc_files: []
|
45
|
+
executables:
|
46
|
+
- purple
|
47
|
+
extensions: []
|
48
|
+
requirements: []
|
49
|
+
dependencies: []
|