smc-get 0.2.0.beta1 → 0.3.0.beta1

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.
@@ -1,329 +1,297 @@
1
- #Encoding: UTF-8
2
- ################################################################################
3
- # This file is part of smc-get.
4
- # Copyright (C) 2010-2011 Entertaining Software, Inc.
5
- # Copyright (C) 2011 Marvin Gülker
6
- #
7
- # This program is free software: you can redistribute it and/or modify
8
- # it under the terms of the GNU General Public License as published by
9
- # the Free Software Foundation, either version 3 of the License, or
10
- # (at your option) any later version.
11
- #
12
- # This program is distributed in the hope that it will be useful,
13
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
- # GNU General Public License for more details.
16
- #
17
- # You should have received a copy of the GNU General Public License
18
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
- ################################################################################
20
-
21
- module SmcGet
22
-
23
- module CUICommands
24
-
25
- class BuildCommand < Command
26
-
27
- def self.help
28
- <<-EOF
29
- USAGE: #{File.basename($0)} build [DIRECTORY]
30
-
31
- If DIRECTORY is given, validates it's structure against the SMC packaging
32
- guidelines and then packages it into a .smcpak file. If DIRECTORY is not
33
- given, enters an interactive process that queries you for the files you
34
- want to include in your SMC package. In both cases you’ll end up with
35
- a SMC package in your current directory.
36
-
37
- Files you enter during the interrogative process are looked for in your
38
- user-level SMC directory, i.e. ~/.smc.
39
- EOF
40
- end
41
-
42
- def self.summary
43
- "build\t\tBuild SMC packages."
44
- end
45
-
46
- def parse(args)
47
- CUI.debug("Parsing #{args.count} args for build.")
48
- raise(InvalidCommandline, "Too many arguments.") if args.count > 1
49
- @directory = args.shift #nil if not given
50
- end
51
-
52
- def execute(config)
53
- CUI.debug("Executing build.")
54
- begin
55
- if @directory
56
- Package.create(@directory)
57
- else #No directory given
58
- puts(<<-MSG)
59
- Welcome to the smc-get build process!
60
- Answer the following questions properly and you'll end up with a ready-to-
61
- install package you can either install locally or contribute to the repository
62
- (which would make it installable via 'smc-get install' directly). When a question
63
- asks you to input multiple files, you can end the query with an empty line.
64
- If you like, you can specify multiple files at once by separating them
65
- with a comma. Wildcards in filenames are allowed.
66
-
67
- Files you don't specify via an absolute path (i.e. a path beginning with
68
- either / on *nix or <letter>:\\ on Windows) are searched for in your
69
- home directory's .smc directory and your SMC installation.
70
- MSG
71
- #All the information will be collected in this hash
72
- spec = Hash.new{|hsh, key| hsh[key] = []}
73
-
74
- #Start the questionaire
75
- [:levels, :graphics, :music, :sounds, :worlds].each do |sym|
76
- spec[sym].concat(input_files(sym))
77
- end
78
-
79
- puts
80
- puts("Who participated in creating this package?")
81
- loop do
82
- print "> "
83
- str = $stdin.gets.chomp
84
-
85
- if str.empty?
86
- if spec[:authors].empty?
87
- $stderr.puts("You have to input at least one author.")
88
- else
89
- break
90
- end
91
- else #Something was entered
92
- spec[:authors].concat(str.split(",").map(&:strip))
93
- end
94
- end
95
-
96
- puts
97
- puts "Enter this package's dependecy packages:"
98
- loop do
99
- print "> "
100
- str = $stdin.gets.chomp
101
- if str.empty?
102
- break
103
- else
104
- spec[:dependencies].concat(str.split(",").map(&:strip))
105
- end
106
- end
107
-
108
- loop do
109
- puts
110
- print("Enter the difficulty: ")
111
- break unless (spec[:difficulty] = $stdin.gets.chomp).empty?
112
- end
113
-
114
- puts
115
- puts("Enter the package's description. A single line containing containg")
116
- puts("END")
117
- puts("terminates the query.")
118
- spec[:description] = ""
119
- loop do
120
- print "> "
121
- str = $stdin.gets #No chomp here, the user may set spaces at the line end intentionally
122
- if str == "END\n"
123
- if spec[:description].strip.empty?
124
- $stderr.puts("You *have* to input a description!")
125
- $stderr.puts("And it must consist of something else than only whitespace!")
126
- else
127
- break
128
- end
129
- else
130
- spec[:description] << str
131
- end
132
- end
133
-
134
- [:install_message, :remove_message].each{|sym| spec[sym] = input_desc(sym)}
135
-
136
- loop do
137
- puts
138
- print("Enter the package's full title (it can contain whitespace):")
139
- spec[:title] = $stdin.gets.chomp
140
- if spec[:title].strip.empty?
141
- $stderr.puts "You *have* to specify a title that doesn't consist solely of whitespace!"
142
- else
143
- break
144
- end
145
- end
146
-
147
- pkgname = nil
148
- loop do
149
- puts
150
- print "Enter the package's name (this mustn't contain whitespace):"
151
- pkgname = $stdin.gets.chomp
152
- if pkgname.strip.empty?
153
- $stderr.puts "You *have* to specify a name!"
154
- elsif pkgname =~ /\s/
155
- $stderr.puts "The package name mustn't contain whitespace!"
156
- else
157
- break
158
- end
159
- end
160
-
161
- #Set the last_update spec field to now
162
- spec[:last_update] = Time.now.utc
163
-
164
- puts
165
- #This is all. Start building the package.
166
- Dir.mktmpdir("smc-get-build-package") do |tmpdir|
167
- puts "Creating package..."
168
- tmpdir = Pathname.new(tmpdir)
169
- pkgdir = tmpdir + pkgname
170
- pkgdir.mkdir
171
- #Copy all the level, music, etc. files
172
- [:levels, :music, :sounds, :worlds].each do |sym|
173
- subdir = pkgdir + sym.to_s
174
- subdir.mkdir
175
- spec[sym].each{|file| FileUtils.cp(file, subdir)}
176
- end
177
- #The graphics for whatever reason have an own name...
178
- subdir = pkgdir + "pixmaps"
179
- subdir.mkdir
180
- spec[:graphics].each{|file| FileUtils.cp(file, subdir)}
181
-
182
- #Turn the file paths in the spec to relative ones and the
183
- #keys to strings. Compute the checksums.
184
- puts "Creating spec and computing SHA1 checksums..."
185
- real_spec = {"checksums" => {}}
186
- spec.each_pair do |key, value|
187
- real_spec[key.to_s] = case key
188
- when :levels, :graphics, :music, :sounds
189
- real_spec["checksums"][key.to_s] = {}
190
- value.map do |abs_path|
191
- basename = File.basename(abs_path)
192
- real_spec["checksums"][key.to_s][basename] = Digest::SHA1.hexdigest(File.read(abs_path))
193
- basename #for map
194
- end
195
- when :worlds
196
- real_spec["checksums"]["worlds"] = {}
197
- value.map do |abs_path|
198
- basename = File.basename(abs_path)
199
- real_spec["checksums"]["worlds"][basename] = {
200
- "description.xml" => Digest::SHA1.hexdigest(File.read(File.join(abs_path, "description.xml"))),
201
- "layer.xml" => Digest::SHA1.hexdigest(File.read(File.join(abs_path, "layer.xml"))),
202
- "world.xml" => Digest::SHA1.hexdigest(File.read(File.join(abs_path, "world.xml")))
203
- }
204
- basename #for map
205
- end
206
- else
207
- value
208
- end
209
- end
210
-
211
- #Create the spec
212
- File.open(pkgdir + "#{pkgname}.yml", "w"){|file| YAML.dump(real_spec, file)}
213
-
214
- puts "Compressing..."
215
- pkg = Package.create(pkgdir)
216
- puts "Copying..."
217
- FileUtils.cp(pkg.path, "./")
218
- #compressed_file_name returns only the name of the package file,
219
- #no path. Expanding it therefore results in the current
220
- #working directory prepended to it.
221
- puts "Done. Your package is at #{File.expand_path(pkg.spec.compressed_file_name)}."
222
- end
223
-
224
- end
225
- rescue Errors::BrokenPackageError => e
226
- $stderr.puts("Failed to build SMC package:")
227
- $stderr.puts(e.message)
228
- if CUI.debug_mode?
229
- $stderr.puts("Class: #{e.class}")
230
- $stderr.puts("Message: #{e.message}")
231
- $stderr.puts("Backtrace:")
232
- $stderr.puts(e.backtrace.join("\n\t"))
233
- end
234
- return 1 #Exit code
235
- end
236
- end
237
-
238
- private
239
-
240
- #Queries the user for a set of file names in an uniform mannor.
241
- #Pass in the pluralized symbol of the resource you want to query
242
- #for, e.g. :levels or :graphics. Return value is an array of all
243
- #found files which may be empty if no files were found.
244
- def input_files(plural_name)
245
- result = []
246
- puts
247
- puts "Enter the names of the #{plural_name} you want to include:"
248
- loop do
249
- print "> "
250
- str = $stdin.gets.chomp
251
-
252
- #Test if the user entered an empty line, i.e. wants to end the
253
- #query for this question
254
- if str.empty?
255
- if result.empty?
256
- print("No #{plural_name} specified. Is this correct?(y/n) ")
257
- break if $stdin.gets.chomp.downcase == "y"
258
- else
259
- break
260
- end
261
- else #User entered something
262
- str.split(",").each do |file|
263
- file.strip! #Due to possible whitespace behind the comma
264
- ary = get_file_paths(plural_name.to_s, file)
265
- $stderr.puts("Warning: File(s) not found: #{file}. Ignoring.") if ary.empty?
266
- result.concat(ary)
267
- end
268
- end
269
- end
270
- result
271
- end
272
-
273
- #Queries the user for a longer, but optional text that is returned. If
274
- #the user enters no text beside the END marker, returns nil. Pass in
275
- #what to tell the user is to be entered.
276
- def input_desc(sym)
277
- puts
278
- puts("Enter the package's #{sym}. A single line containing containg")
279
- puts("END")
280
- puts("terminates the query. Enter END immediately if you don't want")
281
- puts "a #{sym}."
282
- result = ""
283
- loop do
284
- print "> "
285
- str = $stdin.gets #No chomp here, the user may set spaces at the line end intentionally
286
- if str == "END\n"
287
- break
288
- else
289
- result << str
290
- end
291
- end
292
- result.empty? ? nil : result
293
- end
294
-
295
- def get_file_paths(plural_name, path)
296
- #Even on Windows we work with forward slash (Windows supports this,
297
- #although it’s not well known)
298
- path = path.gsub("\\", "/")
299
- ary = []
300
- #First check if it’s an absolute path
301
- if RUBY_PLATFORM =~ /mswin|mingw|cygwin/ and path =~ /^[a-z]:/i
302
- ary.replace(Dir.glob(path)) #Works even without an actual escape char like *
303
- elsif path.start_with?("/")
304
- ary.replace(Dir.glob(path))
305
- else #OK, relative path
306
- plural_name = "pixmaps" if plural_name == "graphics" #As always...
307
-
308
- #The user level directory only contains levels, but for the
309
- #sake of simplicity I treat it as if sounds etc existed there
310
- #as well. It doesn’t hurt if not, because that just causes
311
- #an empty array.
312
- user_level_dir = CUI::USER_SMC_DIR + plural_name
313
- smc_install_dir = @cui.local_repository.path + plural_name
314
- global_files = Dir.glob(smc_install_dir.join(path).to_s)
315
- user_files = Dir.glob(user_level_dir.join(path).to_s)
316
- #In case a file with the same name exists in both paths,
317
- #the user-level file overrides the SMC installation ones’s.
318
- ary.replace(user_files)
319
- global_files.each{|path| ary << path unless ary.any?{|p2| File.basename(path) == File.basename(p2)}}
320
- end
321
- ary
322
- end
323
-
324
- end
325
-
326
- end
327
-
328
- end
329
- # vim:set ts=8 sts=2 sw=2 et: #
1
+ # -*- coding: utf-8 -*-
2
+ ################################################################################
3
+ # This file is part of smc-get.
4
+ # Copyright (C) 2010-2011 Entertaining Software, Inc.
5
+ # Copyright (C) 2011 Marvin Gülker
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+ ################################################################################
20
+
21
+ module SmcGet
22
+
23
+ module CUICommands
24
+
25
+ class BuildCommand < Command
26
+
27
+ def self.help
28
+ <<-EOF
29
+ USAGE: #{File.basename($0)} build [DIRECTORY]
30
+
31
+ If DIRECTORY is given, validates it's structure against the SMC packaging
32
+ guidelines and then packages it into a .smcpak file. If DIRECTORY is not
33
+ given, enters an interactive process that queries you for the files you
34
+ want to include in your SMC package. In both cases you’ll end up with
35
+ a SMC package in your current directory.
36
+
37
+ Files you enter during the interrogative process are looked for in your
38
+ user-level SMC directory, i.e. ~/.smc.
39
+ EOF
40
+ end
41
+
42
+ def self.summary
43
+ "build\t\tBuild SMC packages."
44
+ end
45
+
46
+ def parse(args)
47
+ CUI.debug("Parsing #{args.count} args for build.")
48
+ raise(InvalidCommandline, "Too many arguments.") if args.count > 1
49
+ @directory = args.shift #nil if not given
50
+ end
51
+
52
+ def execute(config)
53
+ CUI.debug("Executing build.")
54
+ begin
55
+ if @directory
56
+ Package.create(@directory)
57
+ else #No directory given
58
+ puts(<<-MSG)
59
+ Welcome to the smc-get build process!
60
+ Answer the following questions properly and you'll end up with a ready-to-
61
+ install package you can either install locally or contribute to the repository
62
+ (which would make it installable via 'smc-get install' directly). When a question
63
+ asks you to input multiple files, you can end the query with an empty line.
64
+ If you like, you can specify multiple files at once by separating them
65
+ with a comma. Wildcards in filenames are allowed.
66
+
67
+ Files you don't specify via an absolute path (i.e. a path beginning with
68
+ either / on *nix or <letter>:\\ on Windows) are searched for in your
69
+ home directory's .smc directory and your SMC installation.
70
+ MSG
71
+ #All the information will be collected in this hash
72
+ spec = {}
73
+
74
+ #Start the questionaire
75
+ [:levels, :graphics, :music, :sounds, :worlds].each do |sym|
76
+ spec[sym] = input_files(sym)
77
+ end
78
+
79
+ spec[:authors] = ask_list "Who participated in creating this package?"
80
+ spec[:dependencies] = ask_list "Enter this package's dependecy packages.", true
81
+ spec[:difficulty] = ask_string "Enter the difficulty: "
82
+ spec[:description] = ask_text "Enter the package's description."
83
+ spec[:install_message] = ask_text "Enter the installation message.", true
84
+ spec[:remove_message] = ask_text "Enter the remove message.", true
85
+ spec[:title] = ask_string "Enter the package's full title (it can contain whitespace):"
86
+ pkgname = ask_string "Enter the package's name (this mustn't contain whitespace):"
87
+
88
+ #Set the last_update spec field to now
89
+ spec[:last_update] = Time.now.utc #autoconvert to UTC
90
+
91
+ #This is all. Start building the package.
92
+ Dir.mktmpdir("smc-get-build-package") do |tmpdir|
93
+ puts "Creating package..."
94
+ tmpdir = Pathname.new(tmpdir)
95
+ pkgdir = tmpdir + pkgname
96
+ pkgdir.mkdir
97
+ #Copy all the level, music, etc. files
98
+ [:levels, :music, :sounds].each do |sym|
99
+ subdir = pkgdir + sym.to_s
100
+ subdir.mkdir
101
+ spec[sym].each{|file| FileUtils.cp(file, subdir)}
102
+ end
103
+ #Copy worlds
104
+ subdir = pkgdir + "worlds"
105
+ subdir.mkdir
106
+ spec[:worlds].each{|dir| FileUtils.cp_r(dir, subdir)}
107
+ #The graphics for whatever reason have an own name...
108
+ subdir = pkgdir + "pixmaps"
109
+ subdir.mkdir
110
+ spec[:graphics].each{|file| FileUtils.cp(file, subdir)}
111
+
112
+ #Turn the file paths in the spec to relative ones and the
113
+ #keys to strings. Compute the checksums.
114
+ puts "Creating spec and computing SHA1 checksums..."
115
+ real_spec = {"checksums" => {}}
116
+ spec.each_pair do |key, value|
117
+ real_spec[key.to_s] = case key
118
+ when :levels, :graphics, :music, :sounds
119
+ real_spec["checksums"][key.to_s] = {}
120
+ value.map do |abs_path|
121
+ basename = File.basename(abs_path)
122
+ real_spec["checksums"][key.to_s][basename] = Digest::SHA1.hexdigest(File.read(abs_path))
123
+ basename #for map
124
+ end
125
+ when :worlds
126
+ real_spec["checksums"]["worlds"] = {}
127
+ value.map do |abs_path|
128
+ basename = File.basename(abs_path)
129
+ real_spec["checksums"]["worlds"][basename] = {
130
+ "description.xml" => Digest::SHA1.hexdigest(File.read(File.join(abs_path, "description.xml"))),
131
+ "layer.xml" => Digest::SHA1.hexdigest(File.read(File.join(abs_path, "layer.xml"))),
132
+ "world.xml" => Digest::SHA1.hexdigest(File.read(File.join(abs_path, "world.xml")))
133
+ }
134
+ basename #for map
135
+ end
136
+ else
137
+ value
138
+ end
139
+ end
140
+
141
+ #Create the spec
142
+ File.open(pkgdir + "#{pkgname}.yml", "w"){|file| YAML.dump(real_spec, file)}
143
+
144
+ puts "Compressing..."
145
+ pkg = Package.create(pkgdir)
146
+ puts "Copying..."
147
+ FileUtils.cp(pkg.path, "./")
148
+ #compressed_file_name returns only the name of the package file,
149
+ #no path. Expanding it therefore results in the current
150
+ #working directory prepended to it.
151
+ puts "Done. Your package is at #{File.expand_path(pkg.spec.compressed_file_name)}."
152
+ end
153
+
154
+ end
155
+ rescue Errors::BrokenPackageError => e
156
+ $stderr.puts("Failed to build SMC package:")
157
+ $stderr.puts(e.message)
158
+ if CUI.debug_mode?
159
+ $stderr.puts("Class: #{e.class}")
160
+ $stderr.puts("Message: #{e.message}")
161
+ $stderr.puts("Backtrace:")
162
+ $stderr.puts(e.backtrace.join("\n\t"))
163
+ end
164
+ return 1 #Exit code
165
+ end
166
+ end
167
+
168
+ private
169
+
170
+ #Queries the user for a set of file names in an uniform mannor.
171
+ #Pass in the pluralized symbol of the resource you want to query
172
+ #for, e.g. :levels or :graphics. Return value is an array of all
173
+ #found files which may be empty if no files were found.
174
+ def input_files(plural_name)
175
+ result = []
176
+ puts
177
+ puts "Enter the names of the #{plural_name} you want to include:"
178
+ loop do
179
+ print "> "
180
+ str = $stdin.gets.chomp
181
+
182
+ #Test if the user entered an empty line, i.e. wants to end the
183
+ #query for this question
184
+ if str.empty?
185
+ if result.empty?
186
+ print("No #{plural_name} specified. Is this correct?(y/n) ")
187
+ break if $stdin.gets.chomp.downcase == "y"
188
+ else
189
+ break
190
+ end
191
+ else #User entered something
192
+ str.split(",").each do |file|
193
+ file.strip! #Due to possible whitespace behind the comma
194
+ ary = get_file_paths(plural_name.to_s, file)
195
+ $stderr.puts("Warning: File(s) not found: #{file}. Ignoring.") if ary.empty?
196
+ result.concat(ary)
197
+ end
198
+ end
199
+ end
200
+ result
201
+ end
202
+
203
+ def get_file_paths(plural_name, path)
204
+ #Even on Windows we work with forward slash (Windows supports this,
205
+ #although it’s not well known)
206
+ path = path.gsub("\\", "/")
207
+ ary = []
208
+ #First check if it’s an absolute path
209
+ if RUBY_PLATFORM =~ /mswin|mingw|cygwin/ and path =~ /^[a-z]:/i
210
+ ary.replace(Dir.glob(path)) #Works even without an actual escape char like *
211
+ elsif path.start_with?("/")
212
+ ary.replace(Dir.glob(path))
213
+ else #OK, relative path
214
+ plural_name = "pixmaps" if plural_name == "graphics" #As always...
215
+
216
+ #The user level directory only contains levels, but for the
217
+ #sake of simplicity I treat it as if sounds etc existed there
218
+ #as well. It doesn’t hurt if not, because that just causes
219
+ #an empty array.
220
+ user_level_dir = CUI::USER_SMC_DIR + plural_name
221
+ smc_install_dir = @cui.local_repository.path + plural_name
222
+ global_files = Dir.glob(smc_install_dir.join(path).to_s)
223
+ user_files = Dir.glob(user_level_dir.join(path).to_s)
224
+ #In case a file with the same name exists in both paths,
225
+ #the user-level file overrides the SMC installation ones’s.
226
+ ary.replace(user_files)
227
+ global_files.each{|path| ary << path unless ary.any?{|p2| File.basename(path) == File.basename(p2)}}
228
+ end
229
+ ary
230
+ end
231
+
232
+ #Asks for a newline and/or comma-separated list of things which are
233
+ #returned as an array.
234
+ def ask_list(message, blank_allowed = false)
235
+ puts(message)
236
+ list = []
237
+ loop do
238
+ print "> "
239
+ str = $stdin.gets.chomp
240
+
241
+ if str.empty?
242
+ if list.empty? and !blank_allowed
243
+ $stderr.puts("You have to input at least one entry.")
244
+ else
245
+ break
246
+ end
247
+ else #Something was entered
248
+ list.concat(str.split(",").map(&:strip))
249
+ end
250
+ end
251
+ puts #Blank line
252
+ list
253
+ end
254
+
255
+ #Ask for a short string. If +no_whitespace+ is true, the user is
256
+ #prompted again and again until he finally enters something without
257
+ #whitespace. The string is returned.
258
+ def ask_string(message, no_whitespace = false)
259
+ loop do
260
+ print(message)
261
+ str = $stdin.gets.chomp
262
+ if no_whitespace and str =~ /\s/
263
+ $stderr.puts("No whitespace allowed.")
264
+ else
265
+ puts #Blank line
266
+ return str
267
+ end
268
+ end
269
+ end
270
+
271
+ #Ask for a longer text that will be returned. If +blank_allowed+ is true,
272
+ #an empty text causes a return value of +nil+.
273
+ def ask_text(message, blank_allowed = false)
274
+ puts(message)
275
+ puts "End input with the word END on a single line."
276
+ text = ""
277
+ loop do
278
+ str = $stdin.gets
279
+ if str == "END\n"
280
+ if text.empty? and !blank_allowed
281
+ $stderr.puts("Nothing entered. Please input some text.")
282
+ else
283
+ puts #Blank line
284
+ return text.empty? ? nil : text.strip
285
+ end #if empty?
286
+ else
287
+ text << str
288
+ end #if END
289
+ end #loop
290
+ end #ask_text
291
+
292
+ end
293
+
294
+ end
295
+
296
+ end
297
+ # vim:set ts=8 sts=2 sw=2 et: #