purplepkg 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|