lyp-win 0.2.2
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.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +508 -0
- data/bin/install_release.sh +51 -0
- data/bin/lilypond +88 -0
- data/bin/lyp +5 -0
- data/bin/release_wrapper_lilypond.sh +13 -0
- data/bin/release_wrapper_lyp.sh +13 -0
- data/lib/lyp.rb +42 -0
- data/lib/lyp/base.rb +51 -0
- data/lib/lyp/cli.rb +314 -0
- data/lib/lyp/etc/font.scm +296 -0
- data/lib/lyp/etc/lyp.ly +103 -0
- data/lib/lyp/git_based_rugged.rb +44 -0
- data/lib/lyp/lilypond.rb +570 -0
- data/lib/lyp/package.rb +457 -0
- data/lib/lyp/resolver.rb +481 -0
- data/lib/lyp/settings.rb +37 -0
- data/lib/lyp/system.rb +135 -0
- data/lib/lyp/template.rb +59 -0
- data/lib/lyp/templates/deps_wrapper.rb +59 -0
- data/lib/lyp/version.rb +3 -0
- data/lib/lyp/windows.rb +110 -0
- data/lib/lyp/wrapper.rb +22 -0
- metadata +128 -0
data/lib/lyp/package.rb
ADDED
@@ -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
|