Module: Lyp::Lilypond

Defined in:
lib/lyp/lilypond.rb

Constant Summary collapse

CMP_VERSION =
proc do |x, y|
  Gem::Version.new(x[:version]) <=> Gem::Version.new(y[:version])
end
BASE_URL =
"http://download.linuxaudio.org/lilypond/binaries"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.forced_versionObject (readonly)

Returns the value of attribute forced_version.



74
75
76
# File 'lib/lyp/lilypond.rb', line 74

def forced_version
  @forced_version
end

Class Method Details

.check_lilypond!Object



76
77
78
79
80
81
# File 'lib/lyp/lilypond.rb', line 76

def check_lilypond!
  # check default
  select_default_lilypond! unless valid_lilypond?(default_lilypond)
  
  set_current_lilypond(default_lilypond) unless valid_lilypond?(current_lilypond)
end

.compile(argv, opts = {}) ⇒ Object



8
9
10
11
12
13
# File 'lib/lyp/lilypond.rb', line 8

def compile(argv, opts = {})
  fn = Lyp.wrap(argv.pop, opts)
  argv << fn
  
  invoke(argv, opts)
end

.copy_fonts_from_all_packages(version, opts) ⇒ Object



409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/lyp/lilypond.rb', line 409

def copy_fonts_from_all_packages(version, opts)
  return unless Lyp::FONT_COPY_REQ =~ Gem::Version.new(version)
  
  ly_fonts_dir = File.join(Lyp.lilyponds_dir, version, 'share/lilypond/current/fonts')
  
  Dir["#{Lyp.packages_dir}/**/fonts"].each do |package_fonts_dir|

    Dir["#{package_fonts_dir}/*.otf"].each do |fn|
      target_fn = File.join(ly_fonts_dir, 'otf', File.basename(fn))
      FileUtils.cp(fn, target_fn)
    end
    
    Dir["#{package_fonts_dir}/*.svg"].each do |fn|
      target_fn = File.join(ly_fonts_dir, 'svg', File.basename(fn))
      FileUtils.cp(fn, target_fn)
    end
    
    Dir["#{package_fonts_dir}/*.woff"].each do |fn|
      target_fn = File.join(ly_fonts_dir, 'svg', File.basename(fn))
      FileUtils.cp(fn, target_fn)
    end
  end
end

.copy_lilypond_files(base_path, version, opts) ⇒ Object



382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/lyp/lilypond.rb', line 382

def copy_lilypond_files(base_path, version, opts)
  target_dir = File.join(Lyp.lilyponds_dir, version)
  
  FileUtils.rm_rf(target_dir) if File.exists?(target_dir)
  
  # create directory for lilypond files
  FileUtils.mkdir_p(target_dir)

  # copy files
  STDERR.puts "Copying..." unless opts[:silent]
  %w{bin etc lib lib64 share var}.each do |entry|
    dir = File.join(base_path, entry)
    FileUtils.cp_r(dir, target_dir, remove_destination: true) if File.directory?(dir)
  end
  
  STDERR.puts exec "#{target_dir}/bin/lilypond -v"  unless opts[:silent]
rescue => e
  puts e.message
end

.current_lilypondObject

The current lilypond path is stored in a temporary file named by the session id. Thus we can persist the version selected by the user



40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/lyp/lilypond.rb', line 40

def current_lilypond
  return forced_lilypond if @forced_version
  
  settings = get_session_settings

  if !settings[:current]
    settings[:current] = default_lilypond
    set_session_settings(settings)
  end
  
  settings[:current]
end

.default_lilypondObject



30
31
32
# File 'lib/lyp/lilypond.rb', line 30

def default_lilypond
  Lyp::Settings['lilypond/default']
end

.detect_lilypond_platformObject



274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/lyp/lilypond.rb', line 274

def detect_lilypond_platform
  case RUBY_PLATFORM
  when /x86_64-darwin/
    "darwin-x86"
  when /ppc-darwin/
    "darwin-ppc"
  when "i686-linux"
    "linux-x86"
  when "x86_64-linux"
    "linux-64"
  when "ppc-linux"
    "linux-ppc"
  end
end

.detect_use_version_argument(argv) ⇒ Object



26
27
28
# File 'lib/lyp/lilypond.rb', line 26

def detect_use_version_argument(argv)
  nil
end

.detect_version_from_specifier(version_specifier) ⇒ Object



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/lyp/lilypond.rb', line 253

def detect_version_from_specifier(version_specifier)
  case version_specifier
  when /^\d/
    version_specifier
  when nil, 'stable'
    latest_stable_version
  when 'unstable'
    latest_unstable_version
  when 'latest'
    latest_version
  else
    req = Gem::Requirement.new(version_specifier)
    lilypond = search.reverse.find {|l| req =~ Gem::Version.new(l[:version])}
    if lilypond
      lilypond[:version]
    else
      raise "Could not find version matching #{version_specifier}"
    end
  end
end

.download_lilypond(url, fn, opts) ⇒ Object



313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/lyp/lilypond.rb', line 313

def download_lilypond(url, fn, opts)
  STDERR.puts "Downloading #{url}" unless opts[:silent]
  
  download_count = 0
  client = HTTPClient.new
  conn = client.get_async(url)
  msg = conn.pop
  total_size = msg.header['Content-Length'].first.to_i
  io = msg.content

  unless opts[:silent]
    pbar = ProgressBar.create(title: 'Download', total: total_size)
  end
  File.open(fn, 'w+') do |f|
    while data = io.read(10000)
      download_count += data.bytesize
      f << data
      unless opts[:silent]
        pbar.progress = download_count if download_count <= total_size
      end
    end
  end
  pbar.finish unless opts[:silent]
end

.exec(cmd, raise_on_failure = true) ⇒ Object



488
489
490
491
492
493
494
495
496
497
498
499
500
# File 'lib/lyp/lilypond.rb', line 488

def exec(cmd, raise_on_failure = true)
  success = nil
  Open3.popen3(cmd) do |_in, _out, _err, wait_thr|
    exit_value = wait_thr.value
    $_out = _out.read
    $_err = _err.read
    success = exit_value == 0
  end
  if !success && raise_on_failure
    raise "Error executing cmd #{cmd}: #{$_err}"
  end
  success
end

.force_env_version!Object



67
68
69
70
71
72
# File 'lib/lyp/lilypond.rb', line 67

def force_env_version!
  @forced_version = ENV['LILYPOND_VERSION']
  unless @forced_version
    raise "LILYPOND_VERSION not set"
  end
end

.forced_lilypondObject



59
60
61
62
63
64
65
# File 'lib/lyp/lilypond.rb', line 59

def forced_lilypond
  lilypond = lyp_lilyponds.find do |l|
    l[:version] == @forced_version
  end
  
  lilypond && lilypond[:path]
end

.get_session_settingsObject



95
96
97
# File 'lib/lyp/lilypond.rb', line 95

def get_session_settings
  YAML.load(IO.read(session_settings_filename)) rescue {}
end

.get_system_lilyponds_pathsObject



179
180
181
182
183
184
185
186
187
# File 'lib/lyp/lilypond.rb', line 179

def get_system_lilyponds_paths
  self_bin_dir = File.dirname(File.expand_path($0))
  
  list = `which -a lilypond`
  list = list.lines.map {|f| f.chomp}.reject do |l|
    dir = File.dirname(l)
    (dir == Gem.bindir) || (dir == Lyp::LYP_BIN_DIRECTORY) || (dir == self_bin_dir)
  end
end

.install(version_specifier, opts = {}) ⇒ Object



241
242
243
244
245
246
247
248
249
250
251
# File 'lib/lyp/lilypond.rb', line 241

def install(version_specifier, opts = {})
  version = detect_version_from_specifier(version_specifier)
  raise "No version found matching specifier #{version_specifier}" unless version
  
  STDERR.puts "Installing version #{version}" unless opts[:silent]
  install_version(version, opts)

  lilypond_path = "#{Lyp.lilyponds_dir}/#{version}/bin/lilypond"
  set_current_lilypond(lilypond_path)
  set_default_lilypond(lilypond_path) if opts[:default]
end

.install_lilypond_files(fn, platform, version, opts) ⇒ Object



338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/lyp/lilypond.rb', line 338

def install_lilypond_files(fn, platform, version, opts)
  tmp_target = "/tmp/lyp-lilypond-#{version}"
  FileUtils.mkdir_p(tmp_target)

  case platform
  when /darwin/
    install_lilypond_files_osx(fn, tmp_target, platform, version, opts)
  when /linux/
    install_lilypond_files_linux(fn, tmp_target, platform, version, opts)
  end
  
ensure
  FileUtils.rm_rf(tmp_target)
end

.install_lilypond_files_linux(fn, target, platform, version, opts) ⇒ Object

Since linux versions are distributed as sh archives, we need first to extract the sh archive, then extract the resulting tar file



362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/lyp/lilypond.rb', line 362

def install_lilypond_files_linux(fn, target, platform, version, opts)
  STDERR.puts "Extracting..." unless opts[:silent]

  # create temp directory in which to extract .sh file
  tmp_dir = "/tmp/lyp-#{Time.now.to_f}"
  FileUtils.mkdir_p(tmp_dir)

  FileUtils.cd(tmp_dir) do
    exec "sh #{fn} --tarball >/dev/null"
  end
  
  tmp_fn = "#{tmp_dir}/lilypond-#{version}-1.#{platform}.tar.bz2"
  
  exec "tar -xjf #{tmp_fn} -C #{target}"

  copy_lilypond_files("#{target}/usr", version, opts)
ensure
  FileUtils.rm_rf(tmp_dir)
end

.install_lilypond_files_osx(fn, target, platform, version, opts) ⇒ Object



353
354
355
356
357
358
# File 'lib/lyp/lilypond.rb', line 353

def install_lilypond_files_osx(fn, target, platform, version, opts)
  STDERR.puts "Extracting..." unless opts[:silent]
  exec "tar -xjf #{fn} -C #{target}"

  copy_lilypond_files("#{target}/LilyPond.app/Contents/Resources", version, opts)
end

.install_version(version, opts) ⇒ Object



289
290
291
292
293
294
295
296
297
298
299
# File 'lib/lyp/lilypond.rb', line 289

def install_version(version, opts)
  platform = detect_lilypond_platform
  url = lilypond_install_url(platform, version, opts)
  fn = temp_install_filename(url)

  download_lilypond(url, fn, opts) unless File.file?(fn)
  install_lilypond_files(fn, platform, version, opts)
  
  patch_font_scm(version)
  copy_fonts_from_all_packages(version, opts)
end

.invoke(argv, opts = {}) ⇒ Object



15
16
17
18
19
20
21
22
23
24
# File 'lib/lyp/lilypond.rb', line 15

def invoke(argv, opts = {})
  lilypond = detect_use_version_argument(argv) || current_lilypond

  case opts[:mode]
  when :system
    system("#{lilypond} #{argv.join(" ")}")
  else
    Kernel.exec(lilypond, *argv)
  end
end

.latest_stable_versionObject



229
230
231
# File 'lib/lyp/lilypond.rb', line 229

def latest_stable_version
  search.reverse.find {|l| Gem::Version.new(l[:version]).segments[1].even?}[:version]
end

.latest_unstable_versionObject



233
234
235
# File 'lib/lyp/lilypond.rb', line 233

def latest_unstable_version
  search.reverse.find {|l| Gem::Version.new(l[:version]).segments[1].odd?}[:version]
end

.latest_versionObject



237
238
239
# File 'lib/lyp/lilypond.rb', line 237

def latest_version
  search.last[:version]
end

.lilypond_install_url(platform, version, opts) ⇒ Object



301
302
303
304
305
306
# File 'lib/lyp/lilypond.rb', line 301

def lilypond_install_url(platform, version, opts)
  ext = platform =~ /darwin/ ? ".tar.bz2" : ".sh"
  filename = "lilypond-#{version}-1.#{platform}"

  "#{BASE_URL}/#{platform}/#{filename}#{ext}"
end

.listObject



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/lyp/lilypond.rb', line 113

def list
  system_list = system_lilyponds
  lyp_list = lyp_lilyponds
  
  default = default_lilypond
  unless default
    latest = system_list.sort(&CMP_VERSION).last || lyp_list.sort(&CMP_VERSION).last
    if latest
      default = latest[:path]
      set_default_lilypond(default)
    end
  end
  current = current_lilypond
  
  lilyponds = system_list + lyp_list

  lilyponds.each do |l|
    l[:default] = l[:path] == default
    l[:current] = l[:path] == current
  end
  
  # sort by version
  lilyponds.sort!(&CMP_VERSION)
end

.lyp_lilypondsObject



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/lyp/lilypond.rb', line 138

def lyp_lilyponds
  list = []
  
  Dir["#{Lyp.lilyponds_dir}/*"].each do |path|
    next unless File.directory?(path) && File.basename(path) =~ /^[\d\.]+$/
    
    root_path = path
    version = File.basename(path)
    path = File.join(path, "bin/lilypond")
    list << {
      root_path: root_path,
      path: path,
      version: version
    }
  end
  
  list
end

.patch_font_scm(version) ⇒ Object



402
403
404
405
406
407
# File 'lib/lyp/lilypond.rb', line 402

def patch_font_scm(version)
  return unless Lyp::FONT_PATCH_REQ =~ Gem::Version.new(version)
  
  target_fn = File.join(Lyp.lilyponds_dir, version, 'share/lilypond/current/scm/font.scm')
  FileUtils.cp(Lyp::FONT_PATCH_FILENAME, target_fn)
end

.search(version_specifier = nil) ⇒ Object

Returns a list of versions of lilyponds available for download



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/lyp/lilypond.rb', line 192

def search(version_specifier = nil)
  require 'open-uri'
  require 'nokogiri'

  platform = detect_lilypond_platform
  url = "#{BASE_URL}/#{platform}/"
  doc = Nokogiri::HTML(open(url))
  
  versions = []
  doc.xpath("//td//a").each do |a|
    if a[:href] =~ /^lilypond-([0-9\.]+)/
      versions << $1
    end
  end

  installed_versions = list.map {|l| l[:version]}
  versions.select {|v| version_match(v, version_specifier, versions)}.map do |v|
    {
      version: v,
      installed: installed_versions.include?(v)
    }
  end
end

.select_default_lilypond!Object



87
88
89
90
91
92
93
# File 'lib/lyp/lilypond.rb', line 87

def select_default_lilypond!
  latest = system_lilyponds.sort(&CMP_VERSION).last || lyp_lilyponds.sort(&CMP_VERSION).last
  if latest
    default = latest[:path]
    set_default_lilypond(default)
  end
end

.session_settings_filenameObject



105
106
107
# File 'lib/lyp/lilypond.rb', line 105

def session_settings_filename
  "/tmp/lyp.session.#{Process.getsid}.yml"
end

.set_current_lilypond(path) ⇒ Object



53
54
55
56
57
# File 'lib/lyp/lilypond.rb', line 53

def set_current_lilypond(path)
  settings = get_session_settings
  settings[:current] = path
  set_session_settings(settings)
end

.set_default_lilypond(path) ⇒ Object



34
35
36
# File 'lib/lyp/lilypond.rb', line 34

def set_default_lilypond(path)
  Lyp::Settings['lilypond/default'] = path
end

.set_session_settings(settings) ⇒ Object



99
100
101
102
103
# File 'lib/lyp/lilypond.rb', line 99

def set_session_settings(settings)
  File.open(session_settings_filename, 'w+') do |f|
    f << YAML.dump(settings)
  end
end

.system_lilypondsObject



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/lyp/lilypond.rb', line 157

def system_lilyponds
  list = get_system_lilyponds_paths
  return list if list.empty?
  
  list.inject([]) do |m, path|
    begin
      resp = `#{path} -v`
      if resp.lines.first =~ /LilyPond ([0-9\.]+)/i
        m << {
          root_path: File.expand_path(File.join(File.dirname(path), '..')),
          path: path,
          version: $1,
          system: true
        }
      end
    rescue
      # ignore error
    end
    m
  end
end

.temp_install_filename(url) ⇒ Object



308
309
310
311
# File 'lib/lyp/lilypond.rb', line 308

def temp_install_filename(url)
  u = URI(url)
  "/tmp/lyp-installer-#{File.basename(u.path)}"
end

.uninstall(version) ⇒ Object



468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/lyp/lilypond.rb', line 468

def uninstall(version)
  lilyponds = list.reverse
  lilypond = lilyponds.find {|l| l[:version] == version && !l[:system]}
  unless lilypond
    raise "Invalid version specified: #{version}"
  end
  lilyponds.delete(lilypond)
  latest = lilyponds.first
  
  if lilypond[:default]
    set_default_lilypond(latest && latest[:path])
  end
  if lilypond[:current]
    set_current_lilypond(latest && latest[:path])
  end
  
  lilypond_dir = File.expand_path('../..', lilypond[:path])
  FileUtils.rm_rf(lilypond_dir)
end

.use(version, opts) ⇒ Object



433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'lib/lyp/lilypond.rb', line 433

def use(version, opts)
  lilypond_list = list.reverse
  
  case version
  when 'system'
    lilypond = lilypond_list.find {|v| v[:system] }
    unless lilypond
      raise "Could not find a system installed version of lilypond"
    end
  when 'latest'
    lilypond = lilypond_list.first
  when 'stable'
    lilypond = lilypond_list.find do |v|
      Gem::Version.new(v[:version]).segments[1].even?
    end
  when 'unstable'
    lilypond = lilypond_list.find do |v|
      Gem::Version.new(v[:version]).segments[1].odd?
    end
  else
    version = "~>#{version}.0" if version =~ /^\d+\.\d+$/
    req = Gem::Requirement.new(version)
    lilypond = lilypond_list.find {|v| req =~ Gem::Version.new(v[:version])}
  end
  
  unless lilypond
    raise "Could not find a lilypond matching \"#{version}\""
  end
  
  set_current_lilypond(lilypond[:path])
  set_default_lilypond(lilypond[:path]) if opts[:default]
  
  lilypond
end

.valid_lilypond?(path) ⇒ Boolean

Returns:

  • (Boolean)


83
84
85
# File 'lib/lyp/lilypond.rb', line 83

def valid_lilypond?(path)
  File.file?(path) && (`#{path} -v` =~ /^GNU LilyPond/)
end

.version_match(version, specifier, all_versions) ⇒ Object



216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/lyp/lilypond.rb', line 216

def version_match(version, specifier, all_versions)
  case specifier
  when 'latest'
    version == all_versions.last
  when 'stable'
    Gem::Version.new(version).segments[1].even?
  when 'unstable'
    Gem::Version.new(version).segments[1].odd?
  else
    Gem::Requirement.new(specifier) =~ Gem::Version.new(version)
  end
end