sprout 0.7.153-darwin
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sprout might be problematic. Click here for more details.
- data/MIT-LICENSE +20 -0
- data/TODO +12 -0
- data/bin/sprout +128 -0
- data/doc/Bundle +14 -0
- data/doc/Generator +35 -0
- data/doc/Library +63 -0
- data/doc/Task +21 -0
- data/doc/Tool +20 -0
- data/lib/platform.rb +109 -0
- data/lib/progress_bar.rb +330 -0
- data/lib/sprout.rb +456 -0
- data/lib/sprout/builder.rb +35 -0
- data/lib/sprout/commands/generate.rb +14 -0
- data/lib/sprout/general_tasks.rb +5 -0
- data/lib/sprout/generator.rb +6 -0
- data/lib/sprout/generator/base_mixins.rb +132 -0
- data/lib/sprout/generator/named_base.rb +216 -0
- data/lib/sprout/log.rb +46 -0
- data/lib/sprout/process_runner.rb +46 -0
- data/lib/sprout/project_model.rb +114 -0
- data/lib/sprout/remote_file_loader.rb +233 -0
- data/lib/sprout/remote_file_target.rb +96 -0
- data/lib/sprout/simple_resolver.rb +88 -0
- data/lib/sprout/tasks/gem_wrap_task.rb +192 -0
- data/lib/sprout/tasks/library_task.rb +103 -0
- data/lib/sprout/tasks/sftp_task.rb +245 -0
- data/lib/sprout/tasks/tool_task.rb +541 -0
- data/lib/sprout/tasks/zip_task.rb +158 -0
- data/lib/sprout/template_resolver.rb +207 -0
- data/lib/sprout/user.rb +359 -0
- data/lib/sprout/version.rb +10 -0
- data/lib/sprout/zip_util.rb +61 -0
- data/rakefile.rb +129 -0
- data/samples/gem_wrap/rakefile.rb +17 -0
- metadata +159 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
module Sprout #:nodoc:
|
3
|
+
class ProcessRunnerError < StandardError # :nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
class ProcessRunner #:nodoc:
|
7
|
+
attr_reader :pid,
|
8
|
+
:r,
|
9
|
+
:w,
|
10
|
+
:e
|
11
|
+
|
12
|
+
def initialize(*command)
|
13
|
+
@command = command
|
14
|
+
begin
|
15
|
+
usr = User.new()
|
16
|
+
if(usr.is_a?(WinUser) && !usr.is_a?(CygwinUser))
|
17
|
+
require 'win32/open3'
|
18
|
+
Open3.popen3(*@command) do |w, r, e, pid|
|
19
|
+
@w = w
|
20
|
+
@r = r
|
21
|
+
@e = e
|
22
|
+
@pid = pid
|
23
|
+
end
|
24
|
+
else
|
25
|
+
require 'open4'
|
26
|
+
@pid, @w, @r, @e = open4.popen4(*@command)
|
27
|
+
end
|
28
|
+
rescue Errno::ENOENT => e
|
29
|
+
part = command[0].split(' ').shift
|
30
|
+
raise ProcessRunnerError.new("The expected executable was not found for command [#{part}], please check your system path and/or sprout definition")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def puts(msg)
|
35
|
+
@w.puts(msg)
|
36
|
+
end
|
37
|
+
|
38
|
+
def read
|
39
|
+
return r.read
|
40
|
+
end
|
41
|
+
|
42
|
+
def read_err
|
43
|
+
return e.read
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
|
2
|
+
module Sprout
|
3
|
+
|
4
|
+
# The ProjectModel gives you a place to describe your project so that you
|
5
|
+
# don't need to repeat yourself throughout a rakefile.
|
6
|
+
#
|
7
|
+
# The default set of properties are also used from code generators, library tasks and sometimes tools.
|
8
|
+
#
|
9
|
+
# This class should have some reasonable default values, but can be modified from any rakefile.
|
10
|
+
# If you don't find some properties that you'd like on the ProjectModel, you can simply add
|
11
|
+
# new properties and use them however you wish!
|
12
|
+
class ProjectModel < Hash
|
13
|
+
|
14
|
+
# The real name of the project, usually capitalized like a class name 'SomeProject'
|
15
|
+
attr_accessor :project_name
|
16
|
+
# The relative path to your main source directory. Defaults to 'src'
|
17
|
+
attr_accessor :src_dir
|
18
|
+
# The relative path to your library directory. Defaults to 'lib'
|
19
|
+
#
|
20
|
+
# Any remote .SWC and source libraries that are referenced in your rakefile will be installed
|
21
|
+
# into this directory. Source libraries will be placed in a folder that matches the library name,
|
22
|
+
# while SWCs will be simply placed directly into the lib_dir.
|
23
|
+
attr_accessor :lib_dir
|
24
|
+
# The folder where binary files will be created. Usually this is where any build artifacts like SWF files get placed.
|
25
|
+
attr_accessor :bin_dir
|
26
|
+
# Relative path to the folder that contains your test cases
|
27
|
+
attr_accessor :test_dir
|
28
|
+
# Relative path to the folder where compile time assets will be stored
|
29
|
+
attr_accessor :asset_dir
|
30
|
+
# The folder where compile time skins can be loaded from
|
31
|
+
attr_accessor :skin_dir
|
32
|
+
# The technology language that is being used, right now this is either 'as2' or 'as3'.
|
33
|
+
# Code generators take advantage of this setting to determine which templates to use.
|
34
|
+
attr_accessor :language
|
35
|
+
|
36
|
+
# TODO: Add clean hash interface so that users
|
37
|
+
# can simply add to this object's properties like:
|
38
|
+
# model.foo = 'bar'
|
39
|
+
# model.junk = true
|
40
|
+
# and then just as easily reference those vars from
|
41
|
+
# external generators...
|
42
|
+
def self.instance
|
43
|
+
@@instance ||= ProjectModel.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.destroy # :nodoc:
|
47
|
+
@@instance = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
super
|
52
|
+
@project_name = ''
|
53
|
+
@src_dir = 'src'
|
54
|
+
@lib_dir = 'lib'
|
55
|
+
@bin_dir = 'bin'
|
56
|
+
@test_dir = 'test'
|
57
|
+
@asset_dir = 'assets'
|
58
|
+
@skin_dir = File.join(@asset_dir, 'skins')
|
59
|
+
|
60
|
+
@language = 'as3'
|
61
|
+
|
62
|
+
@model_dir = nil
|
63
|
+
@view_dir = nil
|
64
|
+
@controller_dir = nil
|
65
|
+
end
|
66
|
+
|
67
|
+
# Path to the project directory from which all other paths are created
|
68
|
+
def project_path
|
69
|
+
return Sprout.project_path
|
70
|
+
end
|
71
|
+
|
72
|
+
def model_dir=(dir)
|
73
|
+
@model_dir = dir
|
74
|
+
end
|
75
|
+
|
76
|
+
# Simple MVC helper for project-wide models if your project is called 'SomeProject'
|
77
|
+
# this will default to:
|
78
|
+
# SomeProject/src/someproject/models
|
79
|
+
def model_dir
|
80
|
+
if(@model_dir.nil?)
|
81
|
+
@model_dir = File.join(src_dir, project_name.downcase, 'models')
|
82
|
+
end
|
83
|
+
return @model_dir
|
84
|
+
end
|
85
|
+
|
86
|
+
def view_dir=(dir)
|
87
|
+
@view_dir = dir
|
88
|
+
end
|
89
|
+
|
90
|
+
# Simple MVC helper for project-wide views if your project is called 'SomeProject'
|
91
|
+
# this will default to:
|
92
|
+
# SomeProject/src/someproject/views
|
93
|
+
def view_dir
|
94
|
+
if(@view_dir.nil?)
|
95
|
+
@view_dir = File.join(src_dir, project_name.downcase, 'views')
|
96
|
+
end
|
97
|
+
return @view_dir
|
98
|
+
end
|
99
|
+
|
100
|
+
def controller_dir=(dir)
|
101
|
+
@controller_dir = dir
|
102
|
+
end
|
103
|
+
|
104
|
+
# Simple MVC helper for project-wide views if your project is called 'SomeProject'
|
105
|
+
# this will default to:
|
106
|
+
# SomeProject/src/someproject/controllers
|
107
|
+
def controller_dir
|
108
|
+
if(@controller_dir.nil?)
|
109
|
+
@controller_dir = File.join(src_dir, project_name.downcase, 'controllers')
|
110
|
+
end
|
111
|
+
return @controller_dir
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
|
2
|
+
module Sprout
|
3
|
+
class RemoteFileLoaderError < StandardError #:nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
class RemoteFileLoader #:nodoc:
|
7
|
+
include Archive::Tar
|
8
|
+
|
9
|
+
def get_remote_file(uri, target, force=false, md5=nil)
|
10
|
+
if(force || !File.exists?(target))
|
11
|
+
response = fetch(uri.to_s)
|
12
|
+
if(response_is_valid?(response, md5))
|
13
|
+
write(response, target, force)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def response_is_valid?(response, expected_md5sum=nil)
|
19
|
+
if(expected_md5sum)
|
20
|
+
md5 = Digest::MD5.new
|
21
|
+
md5 << response
|
22
|
+
raise BadCheckSum if (expected_md5sum != md5.hexdigest)
|
23
|
+
end
|
24
|
+
return true
|
25
|
+
end
|
26
|
+
|
27
|
+
def write(response, target, force=false)
|
28
|
+
FileUtils.makedirs(File.dirname(target))
|
29
|
+
if(force && File.exists?(target))
|
30
|
+
File.delete(target)
|
31
|
+
end
|
32
|
+
File.open(target, 'wb') do |f|
|
33
|
+
f.write(response)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def fetch(uri)
|
38
|
+
uri = URI.parse(uri)
|
39
|
+
# Download the file now to the downloads dir
|
40
|
+
# If the file is an archive (zip, gz, tar, tar.gz, dmg), extract to
|
41
|
+
# Sprouts/cache/@type/@name
|
42
|
+
# Check the location again...
|
43
|
+
progress = nil
|
44
|
+
response = nil
|
45
|
+
name = uri.path.split("/").pop
|
46
|
+
|
47
|
+
raise RemoteFileLoaderError.new("The RemoteFileTask failed for #{name}. We can only handle HTTP requests at this time, it seems you were trying: '#{uri.scheme}'") if uri.scheme != 'http'
|
48
|
+
begin
|
49
|
+
open(uri.to_s, :content_length_proc => lambda {|t|
|
50
|
+
if t && t > 0
|
51
|
+
progress = ProgressBar.new(name, t)
|
52
|
+
progress.file_transfer_mode
|
53
|
+
progress.set(0)
|
54
|
+
else
|
55
|
+
progress = ProgressBar.new(name, 0)
|
56
|
+
progress.file_transfer_mode
|
57
|
+
progress.set(0)
|
58
|
+
end
|
59
|
+
},
|
60
|
+
:progress_proc => lambda {|s|
|
61
|
+
progress.set s if progress
|
62
|
+
}) {|f|
|
63
|
+
response = f.read
|
64
|
+
progress.finish
|
65
|
+
}
|
66
|
+
rescue SocketError => e
|
67
|
+
raise RemoteFileLoaderError.new("[ERROR] #{e.to_s}")
|
68
|
+
rescue OpenURI::HTTPError => e
|
69
|
+
raise RemoteFileLoaderError.new("[ERROR] Failed to load file from: '#{uri.to_s}'\n[REMOTE ERROR] #{e.io.read.strip}")
|
70
|
+
rescue Errno::ECONNREFUSED => e
|
71
|
+
raise Errno::ECONNREFUSED.new("[ERROR] Connection refused at: '#{uri.to_s}'")
|
72
|
+
end
|
73
|
+
|
74
|
+
return response
|
75
|
+
end
|
76
|
+
|
77
|
+
def unpack_downloaded_file(file_name, dir)
|
78
|
+
if(!File.exists?(dir))
|
79
|
+
if(is_zip?(file_name))
|
80
|
+
unpack_zip(file_name, dir)
|
81
|
+
elsif(is_targz?(file_name))
|
82
|
+
unpack_targz(file_name, dir)
|
83
|
+
elsif(is_dmg?(file_name))
|
84
|
+
unpack_dmg(file_name, dir)
|
85
|
+
elsif(is_swc?(file_name))
|
86
|
+
# just copy the swc...
|
87
|
+
elsif(is_rb?(file_name))
|
88
|
+
return
|
89
|
+
elsif(is_exe?(file_name))
|
90
|
+
FileUtils.mkdir_p(dir)
|
91
|
+
File.mv(file_name, dir)
|
92
|
+
else
|
93
|
+
raise UsageError.new("RemoteFileTask does not know how to unpack files of type: #{file_name}")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def unpack_zip(zip_file, dir)
|
99
|
+
# Avoid the rubyzip Segmentation Fault bug
|
100
|
+
# at least on os x...
|
101
|
+
if(RUBY_PLATFORM =~ /darwin/)
|
102
|
+
# Unzipping on OS X
|
103
|
+
FileUtils.makedirs(dir)
|
104
|
+
zip_dir = File.expand_path(File.dirname(zip_file))
|
105
|
+
zip_name = File.basename(zip_file)
|
106
|
+
output = File.expand_path(dir)
|
107
|
+
#puts ">> zip_dir: #{zip_dir} zip_name: #{zip_name} output: #{output}"
|
108
|
+
%x(cd #{zip_dir};unzip #{zip_name} -d #{output})
|
109
|
+
else
|
110
|
+
retries = 0
|
111
|
+
begin
|
112
|
+
retries += 1
|
113
|
+
Zip::ZipFile::open(zip_file) do |zf|
|
114
|
+
zf.each do |e|
|
115
|
+
fpath = File.join(dir, e.name)
|
116
|
+
FileUtils.mkdir_p(File.dirname(fpath))
|
117
|
+
# Disgusting, Gross Hack to fix DOS/Ruby Bug
|
118
|
+
# That makes the zip library throw a ZipDestinationFileExistsError
|
119
|
+
# When the zip archive includes two files whose names
|
120
|
+
# differ only by extension.
|
121
|
+
# This bug actually appears in the File.exists? implementation
|
122
|
+
# throwing false positives!
|
123
|
+
# If you're going to use this code, be sure you extract
|
124
|
+
# into a new, empty directory as existing files will be
|
125
|
+
# clobbered...
|
126
|
+
if(File.exists?(fpath) && !File.directory?(fpath))
|
127
|
+
hackpath = fpath + 'hack'
|
128
|
+
zf.extract(e, hackpath)
|
129
|
+
File.copy(hackpath, fpath)
|
130
|
+
File.delete(hackpath)
|
131
|
+
else
|
132
|
+
zf.extract(e, fpath)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
rescue StandardError => e
|
137
|
+
if(retries < 3)
|
138
|
+
puts ">> [ZIP ERROR ENCOUNTERED] trying again with: #{dir}"
|
139
|
+
FileUtils.rm_rf(dir)
|
140
|
+
FileUtils.makedirs(dir)
|
141
|
+
retry
|
142
|
+
end
|
143
|
+
raise e
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def unpack_targz(tgz_file, dir)
|
149
|
+
if(!File.exists?(dir))
|
150
|
+
FileUtils.makedirs(dir)
|
151
|
+
end
|
152
|
+
tar = Zlib::GzipReader.new(File.open(tgz_file, 'rb'))
|
153
|
+
Minitar.unpack(tar, dir)
|
154
|
+
|
155
|
+
# Recurse and unpack gzipped children (Adobe did this double
|
156
|
+
# gzip with the Linux FlashPlayer for some reason)
|
157
|
+
Dir.glob("#{dir}/**/*.tar.gz").each do |child|
|
158
|
+
if(child != tgz_file)
|
159
|
+
unpack_targz(child, File.dirname(child))
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
# This is actually not unpacking the FlashPlayer
|
166
|
+
# Binary file as expected...
|
167
|
+
# OSX is treated the player binary as if it is
|
168
|
+
# a regular text file, but if it is copied manually,
|
169
|
+
# the command works fine!?
|
170
|
+
def unpack_dmg(dmg_file, dir)
|
171
|
+
# 1) Mount the dmg in place
|
172
|
+
# 2) Recursively Copy it's contents to asproject_home
|
173
|
+
# 3) Unmount the dmg
|
174
|
+
if(mounted_path.nil?)
|
175
|
+
raise StandardError.new('DMG file downloaded, but the RemoteFileTask needs a mounted_path in order to mount it')
|
176
|
+
end
|
177
|
+
|
178
|
+
if(!File.exists?(full_mounted_path))
|
179
|
+
system("hdiutil mount #{dmg_file}")
|
180
|
+
end
|
181
|
+
|
182
|
+
begin
|
183
|
+
mounted_target = File.join(full_mounted_path, extracted_file)
|
184
|
+
|
185
|
+
# Copy the DMG contents using system copy rather than ruby utils
|
186
|
+
# Because OS X does something special with .app files that the
|
187
|
+
# Ruby FileUtils and File classes break...
|
188
|
+
from = mounted_target
|
189
|
+
# from = File.join(full_mounted_path, extracted_file)
|
190
|
+
to = File.join(@user.downloads, @name.to_s, extracted_file)
|
191
|
+
FileUtils.makedirs(File.dirname(to))
|
192
|
+
|
193
|
+
if(File.exists?(from))
|
194
|
+
`ditto '#{from}' '#{to}'`
|
195
|
+
end
|
196
|
+
rescue
|
197
|
+
if(File.exists?(full_mounted_path))
|
198
|
+
system("hdiutil unmount -force \"#{full_mounted_path}\"")
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def is_exe?(file)
|
204
|
+
return (file.split('.').pop == 'exe')
|
205
|
+
end
|
206
|
+
|
207
|
+
def is_zip?(file)
|
208
|
+
return (file.split('.').pop == 'zip')
|
209
|
+
end
|
210
|
+
|
211
|
+
def is_targz?(file)
|
212
|
+
parts = file.split('.')
|
213
|
+
part = parts.pop
|
214
|
+
return (part == 'tgz' || part == 'gz' && parts.pop == 'tar')
|
215
|
+
end
|
216
|
+
|
217
|
+
def is_gzip?(file)
|
218
|
+
return (file.split('.').pop == 'gz')
|
219
|
+
end
|
220
|
+
|
221
|
+
def is_swc?(file)
|
222
|
+
return (file.split('.').pop == 'swc')
|
223
|
+
end
|
224
|
+
|
225
|
+
def is_rb?(file)
|
226
|
+
return (file.split('.').pop == 'rb')
|
227
|
+
end
|
228
|
+
|
229
|
+
def is_dmg?(file)
|
230
|
+
return (file.split('.').pop == 'dmg')
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
|
2
|
+
module Sprout
|
3
|
+
class RemoteFileTargetError < StandardError #:nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
class RemoteFileTarget # :nodoc:
|
7
|
+
|
8
|
+
attr_writer :archive_path
|
9
|
+
|
10
|
+
# The user path where this gem will download and install files
|
11
|
+
# This value is set by the Sprout::Builder that creates this RemoteFileTarget
|
12
|
+
attr_accessor :install_path
|
13
|
+
|
14
|
+
# Optional md5 hash, usually set in the sprout.spec for each RemoteFileTarget
|
15
|
+
# If this value is set, the downloaded archive will be hashed, the hashes will
|
16
|
+
# be compared and if they differ, the installation process will break.
|
17
|
+
attr_accessor :md5
|
18
|
+
|
19
|
+
# Used for dmg archives. Absolute path to the mounted dmg (essentially it's name)
|
20
|
+
attr_accessor :mount_path
|
21
|
+
|
22
|
+
# Which platform will this RemoteFileTarget support.
|
23
|
+
# Supported options are:
|
24
|
+
# * universal
|
25
|
+
# * macosx
|
26
|
+
# * win32
|
27
|
+
# * linux
|
28
|
+
attr_accessor :platform
|
29
|
+
|
30
|
+
# URL where Sprouts can go to download the RemoteFileTarget archive
|
31
|
+
attr_accessor :url
|
32
|
+
|
33
|
+
# Relative path within the archive to the executable or binary of interest
|
34
|
+
def archive_path
|
35
|
+
@archive_path ||= ''
|
36
|
+
end
|
37
|
+
|
38
|
+
# Resolve this RemoteFileTarget now. This method is called by the Sprout::Builder
|
39
|
+
# and will download, install and unpack the described archive
|
40
|
+
def resolve(update=false)
|
41
|
+
if(url)
|
42
|
+
@loader = RemoteFileLoader.new
|
43
|
+
if(url && (update || !File.exists?(downloaded_path)))
|
44
|
+
download(url, downloaded_path, update)
|
45
|
+
end
|
46
|
+
|
47
|
+
if(!File.exists?(installed_path) || !File.exists?(File.join(installed_path, archive_path) ))
|
48
|
+
archive_root = File.join(install_path, 'archive')
|
49
|
+
install(downloaded_path, archive_root)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Return the basename of the executable that this RemoteFileTarget refers to
|
55
|
+
def executable
|
56
|
+
return File.basename(archive_path)
|
57
|
+
end
|
58
|
+
|
59
|
+
# The root path to the unpacked archive files. This is the base path that will be added to any
|
60
|
+
# +archive_path+ relative paths
|
61
|
+
def installed_path
|
62
|
+
@installed_path ||= File.join(install_path, 'archive')
|
63
|
+
return @installed_path
|
64
|
+
end
|
65
|
+
|
66
|
+
# Parent directory where archives are downloaded
|
67
|
+
# can be something like: ~/Library/Sprouts/cache/0.7/sprout-somesprout-tool.x.x.x/
|
68
|
+
def downloaded_path
|
69
|
+
@downloaded_path ||= File.join(install_path, file_name)
|
70
|
+
return @downloaded_path
|
71
|
+
end
|
72
|
+
|
73
|
+
# Base file name represented by the provided +url+
|
74
|
+
# Will strip off any ? arguments and trailing slashes. May not play nice with Rails URLS,
|
75
|
+
# We expect archive file name suffixes like, zip, gzip, tar.gz, dmg, etc.
|
76
|
+
def file_name
|
77
|
+
if(url.split('').last == '/')
|
78
|
+
return name
|
79
|
+
end
|
80
|
+
file = url.split('/').pop
|
81
|
+
file = file.split('?').shift
|
82
|
+
return file
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
def download(url, path, update=false)
|
87
|
+
@loader = RemoteFileLoader.new
|
88
|
+
@loader.get_remote_file(url, path, update, md5)
|
89
|
+
end
|
90
|
+
|
91
|
+
def install(from, to)
|
92
|
+
@loader.unpack_downloaded_file(from, to)
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|