lyp-win 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,457 @@
1
+ require 'fileutils'
2
+ require 'open-uri'
3
+ require 'yaml'
4
+
5
+ module Lyp::Package
6
+ class << self
7
+
8
+ def list(pattern = nil)
9
+ packages = Dir["#{Lyp.packages_dir}/**/package.ly"].map do |path|
10
+ File.dirname(path).gsub("#{Lyp.packages_dir}/", '')
11
+ end
12
+
13
+ if pattern
14
+ if (pattern =~ /[@\>\<\=\~]/) && (pattern =~ Lyp::PACKAGE_RE)
15
+ package, version = $1, $2
16
+ req = Gem::Requirement.new(version) rescue nil
17
+ packages.select! do |p|
18
+ p =~ Lyp::PACKAGE_RE
19
+ p_pack, p_ver = $1, $2
20
+
21
+ next false unless p_pack == package
22
+
23
+ if req && (p_gemver = Gem::Version.new(p_ver) rescue nil)
24
+ req =~ p_gemver
25
+ else
26
+ p_ver == version
27
+ end
28
+ end
29
+ else
30
+ packages.select! do |p|
31
+ p =~ Lyp::PACKAGE_RE
32
+ $1 =~ /#{pattern}/
33
+ end
34
+ end
35
+ end
36
+
37
+ packages.sort do |x, y|
38
+ x =~ Lyp::PACKAGE_RE; x_package, x_version = $1, $2
39
+ y =~ Lyp::PACKAGE_RE; y_package, y_version = $1, $2
40
+
41
+ x_version = (x_version && Gem::Version.new(x_version) rescue x)
42
+ y_version = (y_version && Gem::Version.new(y_version) rescue y)
43
+
44
+ if (x_package == y_package) && (x_version.class == y_version.class)
45
+ x_version <=> y_version
46
+ else
47
+ x <=> y
48
+ end
49
+ end
50
+ end
51
+
52
+ def which(pattern = nil)
53
+ list(pattern).map {|p| "#{Lyp.packages_dir}/#{p}" }
54
+ end
55
+
56
+ def install(package_specifier, opts = {})
57
+ unless package_specifier =~ Lyp::PACKAGE_RE
58
+ raise "Invalid package specifier #{package_specifier}"
59
+ end
60
+ package, version = $1, $2
61
+
62
+ if version =~ /\:/
63
+ info = install_from_local_files(package, version, opts)
64
+ else
65
+ info = install_from_repository(package, version, opts)
66
+ end
67
+
68
+ install_package_dependencies(info[:path], opts)
69
+
70
+ if File.directory?(File.join(info[:path], 'fonts'))
71
+ puts "Installing package fonts..." unless opts[:silent]
72
+ install_package_fonts(info[:path], opts)
73
+ end
74
+
75
+ puts "\nInstalled #{package}@#{info[:version]}\n\n" unless opts[:silent]
76
+
77
+ if opts[:test]
78
+ FileUtils.cd(info[:path]) do
79
+ run_tests(info[:path])
80
+ end
81
+ end
82
+
83
+ # important: return the installed version
84
+ info[:version]
85
+ end
86
+
87
+ LOCAL_PACKAGE_WRAPPER =
88
+ "#(set! lyp:current-package-dir \"%s\")\n\\include \"%s\"\n"
89
+
90
+ def install_from_local_files(package, version, opts)
91
+ version =~ /^([^\:]+)\:(.+)$/
92
+ version, local_path = $1, $2
93
+
94
+ entry_point_path = nil
95
+ local_path = File.expand_path(local_path)
96
+ if File.directory?(local_path)
97
+ ly_path = File.join(local_path, "package.ly")
98
+ if File.file?(ly_path)
99
+ entry_point_path = ly_path
100
+ else
101
+ raise "Could not find #{ly_path}. Please specify a valid lilypond file."
102
+ end
103
+ elsif File.file?(local_path)
104
+ entry_point_path = local_path
105
+ else
106
+ raise "Could not find #{local_path}"
107
+ end
108
+
109
+ entry_point_dirname = File.dirname(entry_point_path)
110
+ package_path = "#{Lyp.packages_dir}/#{package}@#{version}"
111
+ package_ly_path = "#{package_path}/package.ly"
112
+
113
+ FileUtils.rm_rf(package_path)
114
+ FileUtils.mkdir_p(package_path)
115
+ File.open(package_ly_path, 'w+') do |f|
116
+ f << LOCAL_PACKAGE_WRAPPER % [entry_point_dirname, entry_point_path]
117
+ end
118
+
119
+ prepare_local_package_fonts(local_path, package_path)
120
+
121
+ {version: version, path: package_path}
122
+ end
123
+
124
+ def prepare_local_package_fonts(local_path, package_path)
125
+ # create fonts directory symlink if needed
126
+ fonts_path = File.join(local_path, 'fonts')
127
+ if File.directory?(fonts_path)
128
+ FileUtils.ln_sf(fonts_path, File.join(package_path, 'fonts'))
129
+ end
130
+ end
131
+
132
+ def install_from_repository(package, version, opts)
133
+ url = package_git_url(package)
134
+ tmp_path = git_url_to_temp_path(url)
135
+
136
+ repo = package_repository(url, tmp_path, opts)
137
+ version = checkout_package_version(repo, version, opts)
138
+
139
+ # Copy files
140
+ package_path = git_url_to_package_path(
141
+ package !~ /\// ? package : url, version
142
+ )
143
+
144
+ FileUtils.mkdir_p(File.dirname(package_path))
145
+ FileUtils.rm_rf(package_path)
146
+ FileUtils.cp_r(tmp_path, package_path)
147
+
148
+ {version: version, path: package_path}
149
+ end
150
+
151
+ def uninstall(package, opts = {})
152
+ unless package =~ Lyp::PACKAGE_RE
153
+ raise "Invalid package specifier #{package}"
154
+ end
155
+ package, version = $1, $2
156
+ package_path = git_url_to_package_path(
157
+ package !~ /\// ? package : package_git_url(package), nil
158
+ )
159
+
160
+ if opts[:all]
161
+ Dir["#{package_path}@*"].each do |path|
162
+ name = path.gsub("#{Lyp.packages_dir}/", '')
163
+ puts "Uninstalling #{name}" unless opts[:silent]
164
+ FileUtils.rm_rf(path)
165
+ end
166
+ else
167
+ if version
168
+ package_path += "@#{version}"
169
+ else
170
+ packages = Dir["#{package_path}@*"] + Dir["#{package_path}"]
171
+ case packages.size
172
+ when 0
173
+ raise "Could not find package #{package}"
174
+ when 1
175
+ package_path = packages[0]
176
+ else
177
+ packages.each do |path|
178
+ name = path.gsub("#{Lyp.packages_dir}/", '')
179
+ puts "Uninstalling #{name}" unless opts[:silent]
180
+ FileUtils.rm_rf(path)
181
+ end
182
+ return
183
+ end
184
+ end
185
+
186
+ if File.directory?(package_path)
187
+ name = package_path.gsub("#{Lyp.packages_dir}/", '')
188
+ puts "Uninstalling #{name}" unless opts[:silent]
189
+ FileUtils.rm_rf(package_path)
190
+ else
191
+ raise "Could not find package #{package}"
192
+ end
193
+ end
194
+ end
195
+
196
+ def package_repository(url, tmp_path, opts = {})
197
+ # Create repository
198
+ if File.directory?(tmp_path)
199
+ begin
200
+ repo = Rugged::Repository.new(tmp_path)
201
+ repo.fetch('origin', [repo.head.name])
202
+ return repo
203
+ rescue
204
+ # ignore and try to clone
205
+ end
206
+ end
207
+
208
+ FileUtils.rm_rf(File.dirname(tmp_path))
209
+ FileUtils.mkdir_p(File.dirname(tmp_path))
210
+ puts "Cloning #{url}..." unless opts[:silent]
211
+ Rugged::Repository.clone_at(url, tmp_path)
212
+ rescue => e
213
+ raise "Could not clone repository (please check that the package URL is correct.)"
214
+ end
215
+
216
+ def checkout_package_version(repo, version, opts = {})
217
+ # Select commit to checkout
218
+ checkout_ref = select_checkout_ref(repo, version)
219
+ unless checkout_ref
220
+ raise "Could not find tag matching #{version}"
221
+ end
222
+
223
+ begin
224
+ repo.checkout(checkout_ref, strategy: :force)
225
+ rescue
226
+ raise "Invalid version specified (#{version})"
227
+ end
228
+
229
+ tag_version(checkout_ref) || version
230
+ end
231
+
232
+ def install_package_dependencies(package_path, opts = {})
233
+ # Install any missing sub-dependencies
234
+ sub_deps = []
235
+
236
+ resolver = Lyp::Resolver.new("#{package_path}/package.ly")
237
+ deps_tree = resolver.get_dependency_tree(ignore_missing: true)
238
+ deps_tree[:dependencies].each do |package_name, leaf|
239
+ sub_deps << leaf[:clause] if leaf[:versions].empty?
240
+ end
241
+ sub_deps.each {|d| install(d, opts)}
242
+ end
243
+
244
+ def install_package_fonts(package_path, opts = {})
245
+ req = Lyp::FONT_COPY_REQ
246
+
247
+ Lyp::Lilypond.list.each do |lilypond|
248
+ next unless req =~ Gem::Version.new(lilypond[:version])
249
+
250
+ ly_fonts_dir = File.join(lilypond[:root_path], lilypond_fonts_path)
251
+ package_fonts_dir = File.join(package_path, 'fonts')
252
+
253
+ Dir["#{package_fonts_dir}/*.otf"].each do |fn|
254
+ target_fn = File.join(ly_fonts_dir, 'otf', File.basename(fn))
255
+ FileUtils.cp(fn, target_fn)
256
+ end
257
+
258
+ Dir["#{package_fonts_dir}/*.svg"].each do |fn|
259
+ target_fn = File.join(ly_fonts_dir, 'svg', File.basename(fn))
260
+ FileUtils.cp(fn, target_fn)
261
+ end
262
+
263
+ Dir["#{package_fonts_dir}/*.woff"].each do |fn|
264
+ target_fn = File.join(ly_fonts_dir, 'svg', File.basename(fn))
265
+ FileUtils.cp(fn, target_fn)
266
+ end
267
+ end
268
+ end
269
+
270
+ def lilypond_fonts_path
271
+ 'share/lilypond/current/fonts'
272
+ end
273
+
274
+ def package_git_url(package, search_index = true)
275
+ case package
276
+ when /^(?:(?:[^\:]+)|http|https)\:/
277
+ package
278
+ when /^([^\.]+\..+)\/[^\/]+\/.+(?<!\.git)$/ # .git missing from end of URL
279
+ "https://#{package}.git"
280
+ when /^([^\.]+\..+)\/.+/
281
+ "https://#{package}"
282
+ when /^[^\/]+\/[^\/]+$/
283
+ "https://github.com/#{package}.git"
284
+ else
285
+ if search_index && (url = search_lyp_index(package))
286
+ package_git_url(url, false) # make sure url is qualified
287
+ else
288
+ raise "Could not find package '#{package}' in lyp-index"
289
+ end
290
+ end
291
+ end
292
+
293
+ LYP_INDEX_URL = "https://raw.githubusercontent.com/noteflakes/lyp-index/master/index.yaml"
294
+
295
+ def search_lyp_index(package)
296
+ entry = lyp_index['packages'][package]
297
+ entry && entry['url']
298
+ end
299
+
300
+ def list_lyp_index(pattern = nil)
301
+ list = lyp_index['packages'].inject([]) do |m, kv|
302
+ m << kv[1].merge(name: kv[0])
303
+ end
304
+
305
+ if pattern
306
+ list.select! {|p| p[:name] =~ /#{pattern}/}
307
+ end
308
+
309
+ list.sort_by {|p| p[:name]}
310
+ end
311
+
312
+ def lyp_index
313
+ @lyp_index ||= YAML.load(open(LYP_INDEX_URL))
314
+ end
315
+
316
+ TEMP_REPO_ROOT_PATH = "#{$TMP_ROOT}/repos"
317
+
318
+ def git_url_to_temp_path(url)
319
+ case url
320
+ when /^(?:http|https)\:(?:\/\/)?(.+)$/
321
+ path = $1.gsub(/\.git$/, '')
322
+ "#{TEMP_REPO_ROOT_PATH}/#{path}"
323
+ when /^(?:.+@)([^\:]+)\:(?:\/\/)?(.+)$/
324
+ domain, path = $1, $2.gsub(/\.git$/, '')
325
+ "#{TEMP_REPO_ROOT_PATH}/#{domain}/#{path}"
326
+ else
327
+ raise "Invalid URL #{url}"
328
+ end
329
+ end
330
+
331
+ def git_url_to_package_path(url, version)
332
+ # version = 'head' if version.nil? || (version == '')
333
+
334
+ package_path = case url
335
+ when /^(?:http|https)\:(?:\/\/)?(.+)$/
336
+ path = $1.gsub(/\.git$/, '')
337
+ "#{Lyp::packages_dir}/#{path}"
338
+ when /^(?:.+@)([^\:]+)\:(?:\/\/)?(.+)$/
339
+ domain, path = $1, $2.gsub(/\.git$/, '')
340
+ "#{Lyp::packages_dir}/#{domain}/#{path}"
341
+ else
342
+ if url !~ /\//
343
+ "#{Lyp::packages_dir}/#{url}"
344
+ else
345
+ raise "Invalid URL #{url}"
346
+ end
347
+ end
348
+
349
+ package_path += "@#{version}" if version
350
+ package_path
351
+ end
352
+
353
+ TAG_VERSION_RE = /^v?(\d.*)$/
354
+
355
+ def select_checkout_ref(repo, version_specifier)
356
+ case version_specifier
357
+ when nil, '', 'latest'
358
+ highest_versioned_tag(repo) || 'master'
359
+ when /^(\>=|~\>|\d)/
360
+ req = Gem::Requirement.new(version_specifier)
361
+ tag = repo_tags(repo).reverse.find do |t|
362
+ (v = tag_version(t.name)) && (req =~ Gem::Version.new(v))
363
+ end
364
+ unless tag
365
+ raise "Could not find a version matching #{version_specifier}"
366
+ else
367
+ tag.name
368
+ end
369
+ else
370
+ version_specifier
371
+ end
372
+ end
373
+
374
+ def highest_versioned_tag(repo)
375
+ tag = repo_tags(repo).select {|t| Gem::Version.new(tag_version(t.name)) rescue nil}.last
376
+ tag && tag.name
377
+ end
378
+
379
+ # Returns a list of tags sorted by version
380
+ def repo_tags(repo)
381
+ tags = []
382
+ repo.tags.each {|t| tags << t}
383
+
384
+ tags.sort do |x, y|
385
+ x_version, y_version = tag_version(x.name), tag_version(y.name)
386
+ if x_version && y_version
387
+ Gem::Version.new(x_version) <=> Gem::Version.new(y_version)
388
+ else
389
+ x.name <=> y.name
390
+ end
391
+ end
392
+ end
393
+
394
+ def tag_version(tag)
395
+ (tag =~ TAG_VERSION_RE) ? $1 : nil
396
+ end
397
+
398
+ # Runs all tests found in local directory
399
+ def run_local_tests(dir, opts = {})
400
+ package_dir = File.expand_path(dir)
401
+ test_files = Dir["#{package_dir}/**/*_test.ly"]
402
+ run_tests(opts) do |stats|
403
+ test_files.each do |f|
404
+ perform_test(f, stats)
405
+ end
406
+ end
407
+ end
408
+
409
+ # This method runs tests by yielding the test statistics.
410
+ # The caller should then call #perform_test to run each test file.
411
+ def run_tests(opts = {})
412
+ stats = {
413
+ start: Time.now,
414
+ test_count: 0,
415
+ fail_count: 0
416
+ }
417
+
418
+ yield stats
419
+
420
+ if stats[:test_count] == 0
421
+ STDERR.puts "No test files found" unless opts[:silent]
422
+ else
423
+ puts "\nFinished in %.2g seconds\n%d files, %d failures" % [
424
+ Time.now - stats[:start], stats[:test_count], stats[:fail_count]
425
+ ] unless opts[:silent]
426
+ exit(stats[:fail_count] > 0 ? 1 : 0) unless opts[:dont_exit]
427
+ end
428
+
429
+ stats
430
+ end
431
+
432
+ def perform_test(fn, stats)
433
+ stats[:test_count] += 1
434
+ unless Lyp::Lilypond.compile([fn], mode: :system, force_wrap: true)
435
+ stats[:fail_count] += 1
436
+ end
437
+ end
438
+
439
+ def run_package_tests(patterns, opts = {})
440
+ patterns = [''] if patterns.empty?
441
+ packages = patterns.inject([]) do |m, pat|
442
+ m += Dir["#{Lyp.packages_dir}/#{pat}*"]
443
+ end.uniq
444
+
445
+ run_tests(opts) do |stats|
446
+ packages.each do |path|
447
+ files = Dir["#{path}/**/*_test.ly"]
448
+ next if files.empty?
449
+
450
+ FileUtils.cd(path) do
451
+ files.each {|fn| perform_test(fn, stats)}
452
+ end
453
+ end
454
+ end
455
+ end
456
+ end
457
+ end