lyp-win 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,296 @@
|
|
1
|
+
;;;; This file is part of LilyPond, the GNU music typesetter.
|
2
|
+
;;;;
|
3
|
+
;;;; Copyright (C) 2004--2014 Han-Wen Nienhuys <hanwen@xs4all.nl>
|
4
|
+
;;;;
|
5
|
+
;;;; LilyPond is free software: you can redistribute it and/or modify
|
6
|
+
;;;; it under the terms of the GNU General Public License as published by
|
7
|
+
;;;; the Free Software Foundation, either version 3 of the License, or
|
8
|
+
;;;; (at your option) any later version.
|
9
|
+
;;;;
|
10
|
+
;;;; LilyPond is distributed in the hope that it will be useful,
|
11
|
+
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
;;;; GNU General Public License for more details.
|
14
|
+
;;;;
|
15
|
+
;;;; You should have received a copy of the GNU General Public License
|
16
|
+
;;;; along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
;; This file supplied by Abraham Leigh and used by lyp to patch lilypond
|
19
|
+
;; versions from 2.18.2 to 2.19.11 in order to support custom music fonts.
|
20
|
+
|
21
|
+
;; TODO:
|
22
|
+
;;
|
23
|
+
;; lookup-font should be written in C.
|
24
|
+
;;
|
25
|
+
|
26
|
+
;; We have a tree, where each level of the tree is a qualifier
|
27
|
+
;; (eg. encoding, family, shape, series etc.) this defines the levels
|
28
|
+
;; in the tree. The first one is encoding, so we can directly select
|
29
|
+
;; between text or music in the first step of the selection.
|
30
|
+
(define default-qualifier-order
|
31
|
+
'(font-encoding font-family font-shape font-series))
|
32
|
+
|
33
|
+
(define-class <Font-tree-element>
|
34
|
+
())
|
35
|
+
|
36
|
+
(define-class <Font-tree-leaf> (<Font-tree-element>)
|
37
|
+
(default-size #:init-keyword #:default-size)
|
38
|
+
(size-vector #:init-keyword #:size-vector))
|
39
|
+
|
40
|
+
(define-class <Font-tree-node> (<Font-tree-element>)
|
41
|
+
(qualifier #:init-keyword #:qualifier #:accessor font-qualifier)
|
42
|
+
(default #:init-keyword #:default #:accessor font-default)
|
43
|
+
(children #:init-keyword #:children #:accessor font-children))
|
44
|
+
|
45
|
+
(define (make-font-tree-leaf size size-font-vector)
|
46
|
+
(make <Font-tree-leaf> #:default-size size #:size-vector size-font-vector))
|
47
|
+
|
48
|
+
(define (make-font-tree-node
|
49
|
+
qualifier default)
|
50
|
+
(make <Font-tree-node>
|
51
|
+
#:qualifier qualifier
|
52
|
+
#:default default
|
53
|
+
#:children (make-hash-table 11)))
|
54
|
+
|
55
|
+
(define-method (display (leaf <Font-tree-leaf>) port)
|
56
|
+
(for-each (lambda (x) (display x port))
|
57
|
+
(list
|
58
|
+
"#<Font-size-family:\n"
|
59
|
+
(slot-ref leaf 'default-size)
|
60
|
+
(slot-ref leaf 'size-vector)
|
61
|
+
"#>"
|
62
|
+
)))
|
63
|
+
|
64
|
+
(define-method (display (node <Font-tree-node>) port)
|
65
|
+
(for-each
|
66
|
+
(lambda (x)
|
67
|
+
(display x port))
|
68
|
+
(list
|
69
|
+
"Font_node {\nqual: "
|
70
|
+
(font-qualifier node)
|
71
|
+
"(def: "
|
72
|
+
(font-default node)
|
73
|
+
") {\n"))
|
74
|
+
(for-each
|
75
|
+
(lambda (x)
|
76
|
+
(display "\n")
|
77
|
+
(display (car x) port)
|
78
|
+
(display "=" port)
|
79
|
+
(display (cdr x) port))
|
80
|
+
(hash-table->alist (font-children node)))
|
81
|
+
(display "} }\n"))
|
82
|
+
|
83
|
+
|
84
|
+
(define-method (add-font (node <Font-tree-node>) fprops size-family)
|
85
|
+
(define (assoc-delete key alist)
|
86
|
+
(assoc-remove! (list-copy alist) key))
|
87
|
+
|
88
|
+
(define (make-node fprops size-family)
|
89
|
+
(if (null? fprops)
|
90
|
+
(make-font-tree-leaf (car size-family) (cdr size-family))
|
91
|
+
(let* ((qual (next-qualifier default-qualifier-order fprops)))
|
92
|
+
(make-font-tree-node qual
|
93
|
+
(assoc-get qual fprops)))))
|
94
|
+
|
95
|
+
(define (next-qualifier order props)
|
96
|
+
(cond
|
97
|
+
((and (null? props) (null? order))
|
98
|
+
#f)
|
99
|
+
((null? props) (car order))
|
100
|
+
((null? order) (caar props))
|
101
|
+
(else
|
102
|
+
(if (assoc-get (car order) props)
|
103
|
+
(car order)
|
104
|
+
(next-qualifier (cdr order) props)))))
|
105
|
+
|
106
|
+
(let* ((q (font-qualifier node))
|
107
|
+
(d (font-default node))
|
108
|
+
(v (assoc-get q fprops d))
|
109
|
+
(new-fprops (assoc-delete q fprops))
|
110
|
+
(child (hashq-ref (slot-ref node 'children)
|
111
|
+
v #f)))
|
112
|
+
(if (not child)
|
113
|
+
(begin
|
114
|
+
(set! child (make-node new-fprops size-family))
|
115
|
+
(hashq-set! (slot-ref node 'children) v child)))
|
116
|
+
(if (pair? new-fprops)
|
117
|
+
(add-font child new-fprops size-family))))
|
118
|
+
|
119
|
+
(define-method (add-font (node <Font-tree-leaf>) fprops size-family)
|
120
|
+
(throw "must add to node, not leaf"))
|
121
|
+
|
122
|
+
(define-method (g-lookup-font (node <Font-tree-node>) alist-chain)
|
123
|
+
(let* ((qual (font-qualifier node))
|
124
|
+
(def (font-default node))
|
125
|
+
(val (chain-assoc-get qual alist-chain def))
|
126
|
+
(desired-child (hashq-ref (font-children node) val)))
|
127
|
+
|
128
|
+
(if desired-child
|
129
|
+
(g-lookup-font desired-child alist-chain)
|
130
|
+
(g-lookup-font (hashq-ref (font-children node) def) alist-chain))))
|
131
|
+
|
132
|
+
(define-method (g-lookup-font (node <Font-tree-leaf>) alist-chain)
|
133
|
+
node)
|
134
|
+
|
135
|
+
;; two step call is handy for debugging.
|
136
|
+
(define (lookup-font node alist-chain)
|
137
|
+
(g-lookup-font node alist-chain))
|
138
|
+
|
139
|
+
;; TODO - we could actually construct this by loading all OTFs and
|
140
|
+
;; inspecting their design size fields.
|
141
|
+
(define-public feta-design-size-mapping
|
142
|
+
'((11 . 11.22)
|
143
|
+
(13 . 12.60)
|
144
|
+
(14 . 14.14)
|
145
|
+
(16 . 15.87)
|
146
|
+
(18 . 17.82)
|
147
|
+
(20 . 20)
|
148
|
+
(23 . 22.45)
|
149
|
+
(26 . 25.20)))
|
150
|
+
|
151
|
+
;; Each size family is a vector of fonts, loaded with a delay. The
|
152
|
+
;; vector should be sorted according to ascending design size.
|
153
|
+
(define-public (add-music-fonts node family name brace design-size-alist factor)
|
154
|
+
"Set up music fonts.
|
155
|
+
|
156
|
+
Arguments:
|
157
|
+
@itemize
|
158
|
+
@item
|
159
|
+
@var{node} is the font tree to modify.
|
160
|
+
|
161
|
+
@item
|
162
|
+
@var{family} is the family name of the music font.
|
163
|
+
|
164
|
+
@item
|
165
|
+
@var{name} is the basename for the music font.
|
166
|
+
@file{@var{name}-<designsize>.otf} should be the music font,
|
167
|
+
|
168
|
+
@item
|
169
|
+
@var{brace} is the basename for the brace font.
|
170
|
+
@file{@var{brace}-brace.otf} should have piano braces.
|
171
|
+
|
172
|
+
@item
|
173
|
+
@var{design-size-alist} is a list of @code{(rounded . designsize)}.
|
174
|
+
@code{rounded} is a suffix for font filenames, while @code{designsize}
|
175
|
+
should be the actual design size. The latter is used for text fonts
|
176
|
+
loaded through pango/@/fontconfig.
|
177
|
+
|
178
|
+
@item
|
179
|
+
@var{factor} is a size factor relative to the default size that is being
|
180
|
+
used. This is used to select the proper design size for the text fonts.
|
181
|
+
@end itemize"
|
182
|
+
(for-each
|
183
|
+
(lambda (x)
|
184
|
+
(add-font node
|
185
|
+
(list (cons 'font-encoding (car x))
|
186
|
+
(cons 'font-family family))
|
187
|
+
(cons (* factor (cadr x))
|
188
|
+
(caddr x))))
|
189
|
+
|
190
|
+
`((fetaText ,(ly:pt 20.0)
|
191
|
+
,(list->vector
|
192
|
+
(map (lambda (tup)
|
193
|
+
(cons (ly:pt (cdr tup))
|
194
|
+
(format #f "~a-~a ~a"
|
195
|
+
name
|
196
|
+
(car tup)
|
197
|
+
(ly:pt (cdr tup)))))
|
198
|
+
design-size-alist)))
|
199
|
+
(fetaMusic ,(ly:pt 20.0)
|
200
|
+
,(list->vector
|
201
|
+
(map (lambda (size-tup)
|
202
|
+
(delay (ly:system-font-load
|
203
|
+
(format #f "~a-~a" name (car size-tup)))))
|
204
|
+
design-size-alist
|
205
|
+
)))
|
206
|
+
(fetaBraces ,(ly:pt 20.0)
|
207
|
+
#(,(delay (ly:system-font-load
|
208
|
+
(format #f "~a-brace" brace)))))
|
209
|
+
)))
|
210
|
+
|
211
|
+
(define-public (add-pango-fonts node lily-family family factor)
|
212
|
+
;; Synchronized with the `text-font-size' variable in
|
213
|
+
;; layout-set-absolute-staff-size-in-module (see paper.scm).
|
214
|
+
(define text-font-size (ly:pt (* factor 11.0)))
|
215
|
+
|
216
|
+
(define (add-node shape series)
|
217
|
+
(add-font node
|
218
|
+
`((font-family . ,lily-family)
|
219
|
+
(font-shape . ,shape)
|
220
|
+
(font-series . ,series)
|
221
|
+
(font-encoding . latin1) ;; ugh.
|
222
|
+
)
|
223
|
+
`(,text-font-size
|
224
|
+
. #(,(cons
|
225
|
+
(ly:pt 12)
|
226
|
+
(ly:make-pango-description-string
|
227
|
+
`(((font-family . ,family)
|
228
|
+
(font-series . ,series)
|
229
|
+
(font-shape . ,shape)))
|
230
|
+
(ly:pt 12)))))))
|
231
|
+
|
232
|
+
(add-node 'upright 'normal)
|
233
|
+
(add-node 'caps 'normal)
|
234
|
+
(add-node 'upright 'bold)
|
235
|
+
(add-node 'italic 'normal)
|
236
|
+
(add-node 'italic 'bold))
|
237
|
+
|
238
|
+
; This function allows the user to change the specific fonts, leaving others
|
239
|
+
; to the default values. This way, "make-pango-font-tree"'s syntax doesn't
|
240
|
+
; have to change from the user's perspective.
|
241
|
+
;
|
242
|
+
; Usage:
|
243
|
+
; \paper {
|
244
|
+
; #(define fonts
|
245
|
+
; (set-global-fonts
|
246
|
+
; #:music "gonville" ; (the main notation font)
|
247
|
+
; #:roman "FreeSerif" ; (the main/serif text font)
|
248
|
+
; ))
|
249
|
+
; }
|
250
|
+
;
|
251
|
+
; Leaving out "#:brace", "#:sans", and "#:typewriter" leave them at
|
252
|
+
; "emmentaler", "sans-serif", and "monospace", respectively. All fonts are
|
253
|
+
; still accesible through the usual scheme symbols: 'feta, 'roman, 'sans, and
|
254
|
+
; 'typewriter.
|
255
|
+
(define*-public (set-global-fonts #:key
|
256
|
+
(music "emmentaler")
|
257
|
+
(brace "emmentaler")
|
258
|
+
(roman "Century Schoolbook L")
|
259
|
+
(sans "sans-serif")
|
260
|
+
(typewriter "monospace")
|
261
|
+
(factor 1))
|
262
|
+
(let ((n (make-font-tree-node 'font-encoding 'fetaMusic)))
|
263
|
+
(add-music-fonts n 'feta music brace feta-design-size-mapping factor)
|
264
|
+
(add-pango-fonts n 'roman roman factor)
|
265
|
+
(add-pango-fonts n 'sans sans factor)
|
266
|
+
(add-pango-fonts n 'typewriter typewriter factor)
|
267
|
+
n))
|
268
|
+
|
269
|
+
(define-public (make-pango-font-tree roman-str sans-str typewrite-str factor)
|
270
|
+
(let ((n (make-font-tree-node 'font-encoding 'fetaMusic)))
|
271
|
+
(add-music-fonts n 'feta "emmentaler" "emmentaler" feta-design-size-mapping factor)
|
272
|
+
(add-pango-fonts n 'roman roman-str factor)
|
273
|
+
(add-pango-fonts n 'sans sans-str factor)
|
274
|
+
(add-pango-fonts n 'typewriter typewrite-str factor)
|
275
|
+
n))
|
276
|
+
|
277
|
+
(define-public (make-century-schoolbook-tree factor)
|
278
|
+
(make-pango-font-tree
|
279
|
+
"Century Schoolbook L"
|
280
|
+
"sans-serif"
|
281
|
+
"monospace"
|
282
|
+
factor))
|
283
|
+
|
284
|
+
(define-public all-text-font-encodings
|
285
|
+
'(latin1))
|
286
|
+
|
287
|
+
(define-public all-music-font-encodings
|
288
|
+
'(fetaBraces
|
289
|
+
fetaMusic
|
290
|
+
fetaText))
|
291
|
+
|
292
|
+
(define-public (magstep s)
|
293
|
+
(exp (* (/ s 6) (log 2))))
|
294
|
+
|
295
|
+
(define-public (magnification->font-size m)
|
296
|
+
(* 6 (/ (log m) (log 2))))
|
data/lib/lyp/etc/lyp.ly
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
#(begin
|
2
|
+
(define lyp:path-separator "/")
|
3
|
+
;(define lyp:path-separator (list->string (list
|
4
|
+
; (if (eq? PLATFORM 'windows) #\\ #\/ ))))
|
5
|
+
|
6
|
+
(define (lyp:file-join . ls) (string-join ls lyp:path-separator))
|
7
|
+
|
8
|
+
; hash table mapping package refs to package names
|
9
|
+
(define lyp:package-refs (make-hash-table))
|
10
|
+
|
11
|
+
; hash table mapping package names to package directories
|
12
|
+
(define lyp:package-dirs (make-hash-table))
|
13
|
+
|
14
|
+
; hash table mapping package names to loaded state
|
15
|
+
(define lyp:package-loaded? (make-hash-table))
|
16
|
+
|
17
|
+
; hash table mapping file paths to included state
|
18
|
+
(define lyp:file-included? (make-hash-table))
|
19
|
+
|
20
|
+
; convert package ref to package name
|
21
|
+
(define (lyp:ref->name ref) (let* (
|
22
|
+
(clean-ref (car (string-split ref #\:)))
|
23
|
+
(name (hash-ref lyp:package-refs clean-ref))
|
24
|
+
)
|
25
|
+
(or name (throw 'lyp:failure "lyp:ref->name"
|
26
|
+
(format "Invalid package ref ~a" ref) #f))
|
27
|
+
))
|
28
|
+
|
29
|
+
; convert package reef to directory
|
30
|
+
(define (lyp:name->dir name) (let (
|
31
|
+
(dir (hash-ref lyp:package-dirs name))
|
32
|
+
)
|
33
|
+
(or dir (throw 'lyp-failure "lyp:name->dir"
|
34
|
+
(format "Invalid package name ~a" ref) #f))
|
35
|
+
))
|
36
|
+
|
37
|
+
; converts a package-relative path to absolute path. If the package is null,
|
38
|
+
; uses lyp:current-package-dir (which value is valid only on package loading)
|
39
|
+
(define (lyp:package-file-path package path) (let* (
|
40
|
+
(base-path (if (null? package)
|
41
|
+
lyp:current-package-dir (lyp:name->dir package)))
|
42
|
+
)
|
43
|
+
(lyp:file-join base-path path)
|
44
|
+
))
|
45
|
+
|
46
|
+
; converts a package file reference to absolute path
|
47
|
+
(define (lyp:fileref->path ref) (let* (
|
48
|
+
(split (string-split ref #\:))
|
49
|
+
(qualified? (eq? (length split) 2))
|
50
|
+
(package (if qualified? (list-ref split 0) %nil))
|
51
|
+
(path (if qualified? (list-ref split 1) (list-ref split 0)))
|
52
|
+
)
|
53
|
+
(lyp:package-file-path package path)
|
54
|
+
))
|
55
|
+
|
56
|
+
(define (lyp:load ref) (load (lyp:fileref->path ref)))
|
57
|
+
|
58
|
+
(define (lyp:include-ly-file path once-only?) (let* (
|
59
|
+
(included? (and once-only? (hash-ref lyp:file-included? path)))
|
60
|
+
)
|
61
|
+
(if (not (file-exists? path))
|
62
|
+
(throw 'lyp:failure "lyp:include-ly-file"
|
63
|
+
(format "File not found ~a" path) #f)
|
64
|
+
)
|
65
|
+
(if (not included?) (begin
|
66
|
+
(hash-set! lyp:file-included? path #t)
|
67
|
+
#{ \include #path #}
|
68
|
+
))
|
69
|
+
))
|
70
|
+
|
71
|
+
(define (lyp:include ref)
|
72
|
+
(lyp:include-ly-file (lyp:fileref->path ref) #f))
|
73
|
+
(define (lyp:include-once ref)
|
74
|
+
(lyp:include-ly-file (lyp:fileref->path ref) #t))
|
75
|
+
|
76
|
+
(define (lyp:require ref) (let* (
|
77
|
+
(name (lyp:ref->name ref))
|
78
|
+
(package-dir (lyp:name->dir name))
|
79
|
+
(entry-point-path (lyp:file-join package-dir "package.ly"))
|
80
|
+
(loaded? (hash-ref lyp:package-loaded? name))
|
81
|
+
(prev-package-dir lyp:current-package-dir)
|
82
|
+
)
|
83
|
+
(if (not loaded?) (begin
|
84
|
+
(ly:debug "Loading package ~a at ~a" name package-dir)
|
85
|
+
(set! lyp:current-package-dir package-dir)
|
86
|
+
(hash-set! lyp:package-loaded? name #t)
|
87
|
+
#{ \include #entry-point-path #}
|
88
|
+
(set! lyp:current-package-dir prev-package-dir)
|
89
|
+
))
|
90
|
+
))
|
91
|
+
)
|
92
|
+
|
93
|
+
% command form
|
94
|
+
require = #(define-void-function (parser location ref)(string?)
|
95
|
+
(lyp:require ref))
|
96
|
+
|
97
|
+
|
98
|
+
pinclude = #(define-void-function (parser location ref)(string?)
|
99
|
+
(lyp:include ref))
|
100
|
+
|
101
|
+
pincludeOnce = #(define-void-function (parser location ref)(string?)
|
102
|
+
(lyp:include-once ref))
|
103
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# A quick-n-dirty rugged swap-in. Since the rugged gem includes a native
|
2
|
+
# extension (libgit2), and since traveling-ruby does not yet include an updated
|
3
|
+
# version of it (the latest is 0.22.0b5 and we need >=0.23.0), we make a
|
4
|
+
# compromise, and make a traveling-ruby-based standalone release without
|
5
|
+
# rugged, but using plain git in order to install packages. So, users will have
|
6
|
+
# to have git installed on their machines.
|
7
|
+
#
|
8
|
+
# So here's an absolutely minimal replacement for rugged (just for the
|
9
|
+
# functionality we need) wrapping the git command.
|
10
|
+
|
11
|
+
module Rugged
|
12
|
+
class Repository
|
13
|
+
def self.clone_at(url, path)
|
14
|
+
`git clone -q \"#{url}\" \"#{path}\"`
|
15
|
+
new(path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(path)
|
19
|
+
@path = path
|
20
|
+
exec('status')
|
21
|
+
end
|
22
|
+
|
23
|
+
Ref = Struct.new(:name)
|
24
|
+
|
25
|
+
def head
|
26
|
+
h = exec("show-ref --head").lines.map {|r| r =~ /^(\S+)\sHEAD$/ && $1}[0]
|
27
|
+
Ref.new(h)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def checkout(ref, opts)
|
32
|
+
# strategy: :force
|
33
|
+
exec("checkout -qf #{ref}")
|
34
|
+
end
|
35
|
+
|
36
|
+
def tags
|
37
|
+
exec("tag").lines.map {|l| Ref.new(l.chomp)}
|
38
|
+
end
|
39
|
+
|
40
|
+
def exec(cmd)
|
41
|
+
`cd #{@path} && git #{cmd}`
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/lyp/lilypond.rb
ADDED
@@ -0,0 +1,570 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'httpclient'
|
3
|
+
require 'open3'
|
4
|
+
require 'ruby-progressbar'
|
5
|
+
|
6
|
+
module Lyp::Lilypond
|
7
|
+
class << self
|
8
|
+
def compile(argv, opts = {})
|
9
|
+
fn = Lyp.wrap(argv.pop, opts)
|
10
|
+
argv << fn
|
11
|
+
|
12
|
+
invoke(argv, opts)
|
13
|
+
end
|
14
|
+
|
15
|
+
def invoke(argv, opts = {})
|
16
|
+
lilypond = detect_use_version_argument(argv) || current_lilypond
|
17
|
+
|
18
|
+
case opts[:mode]
|
19
|
+
when :system
|
20
|
+
system("#{lilypond} #{argv.join(" ")}")
|
21
|
+
else
|
22
|
+
Kernel.exec(lilypond, *argv)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def detect_use_version_argument(argv)
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def default_lilypond
|
31
|
+
Lyp::Settings['lilypond/default']
|
32
|
+
end
|
33
|
+
|
34
|
+
def set_default_lilypond(path)
|
35
|
+
Lyp::Settings['lilypond/default'] = path
|
36
|
+
end
|
37
|
+
|
38
|
+
# The current lilypond path is stored in a temporary file named by the
|
39
|
+
# session id. Thus we can persist the version selected by the user
|
40
|
+
def current_lilypond
|
41
|
+
return forced_lilypond if @forced_version
|
42
|
+
|
43
|
+
settings = get_session_settings
|
44
|
+
|
45
|
+
if !settings[:current]
|
46
|
+
settings[:current] = default_lilypond
|
47
|
+
set_session_settings(settings)
|
48
|
+
end
|
49
|
+
|
50
|
+
settings[:current]
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_current_lilypond(path)
|
54
|
+
settings = get_session_settings
|
55
|
+
settings[:current] = path
|
56
|
+
set_session_settings(settings)
|
57
|
+
end
|
58
|
+
|
59
|
+
def forced_lilypond
|
60
|
+
lilypond = filter_installed_list(@forced_version)[0]
|
61
|
+
if lilypond
|
62
|
+
lilypond[:path]
|
63
|
+
else
|
64
|
+
raise "No installed version found matching '#{@forced_version}'"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def force_env_version!
|
69
|
+
@forced_version = ENV['LILYPOND_VERSION']
|
70
|
+
unless @forced_version
|
71
|
+
raise "LILYPOND_VERSION not set"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def force_version!(version)
|
76
|
+
@forced_version = version
|
77
|
+
end
|
78
|
+
|
79
|
+
attr_reader :forced_version
|
80
|
+
|
81
|
+
def check_lilypond!
|
82
|
+
# check default
|
83
|
+
select_default_lilypond! unless valid_lilypond?(default_lilypond)
|
84
|
+
|
85
|
+
set_current_lilypond(default_lilypond) unless valid_lilypond?(current_lilypond)
|
86
|
+
end
|
87
|
+
|
88
|
+
def valid_lilypond?(path)
|
89
|
+
(File.file?(path) rescue nil) && (`#{path} -v` =~ /^GNU LilyPond/)
|
90
|
+
end
|
91
|
+
|
92
|
+
def select_default_lilypond!
|
93
|
+
latest = system_lilyponds.sort(&CMP_VERSION).last || lyp_lilyponds.sort(&CMP_VERSION).last
|
94
|
+
if latest
|
95
|
+
default = latest[:path]
|
96
|
+
set_default_lilypond(default)
|
97
|
+
else
|
98
|
+
raise Lyp::LILYPOND_NOT_FOUND_MSG
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def get_session_settings
|
103
|
+
YAML.load(IO.read(session_settings_filename)) rescue {}
|
104
|
+
end
|
105
|
+
|
106
|
+
def set_session_settings(settings)
|
107
|
+
File.open(session_settings_filename, 'w+') do |f|
|
108
|
+
f << YAML.dump(settings)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def session_settings_filename
|
113
|
+
"#{$TMP_ROOT}/session.#{Process.getsid}.yml"
|
114
|
+
end
|
115
|
+
|
116
|
+
CMP_VERSION = proc do |x, y|
|
117
|
+
Gem::Version.new(x[:version]) <=> Gem::Version.new(y[:version])
|
118
|
+
end
|
119
|
+
|
120
|
+
def filter_installed_list(version_specifier)
|
121
|
+
list = (system_lilyponds + lyp_lilyponds).sort!(&CMP_VERSION)
|
122
|
+
list.select {|l| version_match(l[:version], version_specifier, list)}
|
123
|
+
end
|
124
|
+
|
125
|
+
def list(opts = {})
|
126
|
+
system_list = opts[:lyp_only] ? [] : system_lilyponds
|
127
|
+
lyp_list = opts[:system_only] ? [] : lyp_lilyponds
|
128
|
+
|
129
|
+
default = default_lilypond
|
130
|
+
unless default
|
131
|
+
latest = system_list.sort(&CMP_VERSION).last || lyp_list.sort(&CMP_VERSION).last
|
132
|
+
if latest
|
133
|
+
default = latest[:path]
|
134
|
+
set_default_lilypond(default)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
current = current_lilypond
|
138
|
+
|
139
|
+
lilyponds = system_list + lyp_list
|
140
|
+
|
141
|
+
lilyponds.each do |l|
|
142
|
+
l[:default] = l[:path] == default
|
143
|
+
l[:current] = l[:path] == current
|
144
|
+
end
|
145
|
+
|
146
|
+
# sort by version
|
147
|
+
lilyponds.sort!(&CMP_VERSION)
|
148
|
+
end
|
149
|
+
|
150
|
+
def lyp_lilyponds
|
151
|
+
list = []
|
152
|
+
|
153
|
+
Dir["#{Lyp.lilyponds_dir}/*"].each do |path|
|
154
|
+
next unless File.directory?(path) && File.basename(path) =~ /^[\d\.]+$/
|
155
|
+
|
156
|
+
root_path = path
|
157
|
+
version = File.basename(path)
|
158
|
+
path = File.join(path, "bin/lilypond")
|
159
|
+
list << {
|
160
|
+
root_path: root_path,
|
161
|
+
path: path,
|
162
|
+
version: version
|
163
|
+
}
|
164
|
+
end
|
165
|
+
|
166
|
+
list
|
167
|
+
end
|
168
|
+
|
169
|
+
def system_lilyponds
|
170
|
+
list = get_system_lilyponds_paths
|
171
|
+
return list if list.empty?
|
172
|
+
|
173
|
+
list.inject([]) do |m, path|
|
174
|
+
begin
|
175
|
+
resp = `#{path} -v`
|
176
|
+
if resp.lines.first =~ /LilyPond ([0-9\.]+)/i
|
177
|
+
m << {
|
178
|
+
root_path: File.expand_path(File.join(File.dirname(path), '..')),
|
179
|
+
path: path,
|
180
|
+
version: $1,
|
181
|
+
system: true
|
182
|
+
}
|
183
|
+
end
|
184
|
+
rescue
|
185
|
+
# ignore error
|
186
|
+
end
|
187
|
+
m
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def get_system_lilyponds_paths
|
192
|
+
self_bin_dir = File.dirname(File.expand_path($0))
|
193
|
+
|
194
|
+
list = `which -a lilypond`
|
195
|
+
list = list.lines.map {|f| f.chomp}.reject do |l|
|
196
|
+
dir = File.dirname(l)
|
197
|
+
(dir == Gem.bindir) || (dir == Lyp::LYP_BIN_DIRECTORY) || (dir == self_bin_dir)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
BASE_URL = "http://download.linuxaudio.org/lilypond/binaries"
|
202
|
+
|
203
|
+
# Returns a list of versions of lilyponds available for download
|
204
|
+
def search(version_specifier = nil)
|
205
|
+
require 'open-uri'
|
206
|
+
|
207
|
+
platform = detect_lilypond_platform
|
208
|
+
url = "#{BASE_URL}/#{platform}/"
|
209
|
+
|
210
|
+
versions = []
|
211
|
+
|
212
|
+
open(url).read.scan(/a href=\"lilypond-([0-9\.]+)[^>]+\"/) do |m|
|
213
|
+
versions << $1
|
214
|
+
end
|
215
|
+
|
216
|
+
installed_versions = list.map {|l| l[:version]}
|
217
|
+
versions.select! {|v| version_match(v, version_specifier, versions)}
|
218
|
+
versions.map do |v|
|
219
|
+
{
|
220
|
+
version: v,
|
221
|
+
installed: installed_versions.include?(v)
|
222
|
+
}
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def version_match(version, specifier, all_versions)
|
227
|
+
case specifier
|
228
|
+
when 'latest'
|
229
|
+
version == all_versions.last
|
230
|
+
when 'stable'
|
231
|
+
Gem::Version.new(version).segments[1].even?
|
232
|
+
when 'unstable'
|
233
|
+
Gem::Version.new(version).segments[1].odd?
|
234
|
+
else
|
235
|
+
Gem::Requirement.new(specifier) =~ Gem::Version.new(version)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def latest_stable_version
|
240
|
+
search.reverse.find {|l| Gem::Version.new(l[:version]).segments[1].even?}[:version]
|
241
|
+
end
|
242
|
+
|
243
|
+
def latest_unstable_version
|
244
|
+
search.reverse.find {|l| Gem::Version.new(l[:version]).segments[1].odd?}[:version]
|
245
|
+
end
|
246
|
+
|
247
|
+
def latest_version
|
248
|
+
search.last[:version]
|
249
|
+
end
|
250
|
+
|
251
|
+
def install_if_missing(version_specifier, opts = {})
|
252
|
+
if filter_installed_list(version_specifier).empty?
|
253
|
+
install(version_specifier, opts)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def install(version_specifier, opts = {})
|
258
|
+
version = detect_version_from_specifier(version_specifier)
|
259
|
+
raise "No version found matching specifier #{version_specifier}" unless version
|
260
|
+
|
261
|
+
STDERR.puts "Installing version #{version}" unless opts[:silent]
|
262
|
+
install_version(version, opts)
|
263
|
+
|
264
|
+
lilypond_path = lyp_lilypond_path(version)
|
265
|
+
set_current_lilypond(lilypond_path)
|
266
|
+
set_default_lilypond(lilypond_path) if opts[:default]
|
267
|
+
end
|
268
|
+
|
269
|
+
def lyp_lilypond_path(version)
|
270
|
+
"#{Lyp.lilyponds_dir}/#{version}/bin/lilypond"
|
271
|
+
end
|
272
|
+
|
273
|
+
def detect_version_from_specifier(version_specifier)
|
274
|
+
case version_specifier
|
275
|
+
when /^\d/
|
276
|
+
version_specifier
|
277
|
+
when nil, 'stable'
|
278
|
+
latest_stable_version
|
279
|
+
when 'unstable'
|
280
|
+
latest_unstable_version
|
281
|
+
when 'latest'
|
282
|
+
latest_version
|
283
|
+
else
|
284
|
+
req = Gem::Requirement.new(version_specifier)
|
285
|
+
lilypond = search.reverse.find {|l| req =~ Gem::Version.new(l[:version])}
|
286
|
+
if lilypond
|
287
|
+
lilypond[:version]
|
288
|
+
else
|
289
|
+
raise "Could not find version matching #{version_specifier}"
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def detect_lilypond_platform
|
295
|
+
case RUBY_PLATFORM
|
296
|
+
when /x86_64-darwin/
|
297
|
+
"darwin-x86"
|
298
|
+
when /ppc-darwin/
|
299
|
+
"darwin-ppc"
|
300
|
+
when "i686-linux"
|
301
|
+
"linux-x86"
|
302
|
+
when "x86_64-linux"
|
303
|
+
"linux-64"
|
304
|
+
when "ppc-linux"
|
305
|
+
"linux-ppc"
|
306
|
+
when "x64-mingw32"
|
307
|
+
"mingw"
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def install_version(version, opts)
|
312
|
+
platform = detect_lilypond_platform
|
313
|
+
url = lilypond_install_url(platform, version, opts)
|
314
|
+
fn = temp_install_filename(url)
|
315
|
+
|
316
|
+
download_lilypond(url, fn, opts) unless File.file?(fn)
|
317
|
+
install_lilypond_files(fn, platform, version, opts)
|
318
|
+
|
319
|
+
patch_font_scm(version)
|
320
|
+
copy_fonts_from_all_packages(version, opts)
|
321
|
+
end
|
322
|
+
|
323
|
+
def lilypond_install_url(platform, version, opts)
|
324
|
+
ext = case platform
|
325
|
+
when /darwin/
|
326
|
+
".tar.bz2"
|
327
|
+
when /linux/
|
328
|
+
".sh"
|
329
|
+
when /mingw/
|
330
|
+
".exe"
|
331
|
+
end
|
332
|
+
filename = "lilypond-#{version}-1.#{platform}"
|
333
|
+
|
334
|
+
"#{BASE_URL}/#{platform}/#{filename}#{ext}"
|
335
|
+
end
|
336
|
+
|
337
|
+
def temp_install_filename(url)
|
338
|
+
u = URI(url)
|
339
|
+
"#{$TMP_ROOT}/#{File.basename(u.path)}"
|
340
|
+
end
|
341
|
+
|
342
|
+
def download_lilypond(url, fn, opts)
|
343
|
+
STDERR.puts "Downloading #{url}" unless opts[:silent]
|
344
|
+
|
345
|
+
download_count = 0
|
346
|
+
client = HTTPClient.new
|
347
|
+
conn = client.get_async(url)
|
348
|
+
msg = conn.pop
|
349
|
+
total_size = msg.header['Content-Length'].first.to_i
|
350
|
+
io = msg.content
|
351
|
+
|
352
|
+
unless opts[:silent]
|
353
|
+
pbar = ProgressBar.create(title: 'Download', total: total_size)
|
354
|
+
end
|
355
|
+
File.open(fn, 'w+') do |f|
|
356
|
+
while data = io.read(10000)
|
357
|
+
download_count += data.bytesize
|
358
|
+
f << data
|
359
|
+
unless opts[:silent]
|
360
|
+
pbar.progress = download_count if download_count <= total_size
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
pbar.finish unless opts[:silent]
|
365
|
+
end
|
366
|
+
|
367
|
+
def install_lilypond_files(fn, platform, version, opts)
|
368
|
+
tmp_target = "#{$TMP_ROOT}/lilypond-#{version}"
|
369
|
+
FileUtils.mkdir_p(tmp_target)
|
370
|
+
|
371
|
+
case platform
|
372
|
+
when /darwin/
|
373
|
+
install_lilypond_files_osx(fn, tmp_target, platform, version, opts)
|
374
|
+
when /linux/
|
375
|
+
install_lilypond_files_linux(fn, tmp_target, platform, version, opts)
|
376
|
+
when /mingw/
|
377
|
+
install_lilypond_files_windows(fn, tmp_target, platform, version, opts)
|
378
|
+
end
|
379
|
+
|
380
|
+
ensure
|
381
|
+
FileUtils.rm_rf(tmp_target)
|
382
|
+
end
|
383
|
+
|
384
|
+
def install_lilypond_files_osx(fn, target, platform, version, opts)
|
385
|
+
STDERR.puts "Extracting..." unless opts[:silent]
|
386
|
+
exec "tar -xjf #{fn} -C #{target}"
|
387
|
+
|
388
|
+
copy_lilypond_files("#{target}/LilyPond.app/Contents/Resources", version, opts)
|
389
|
+
end
|
390
|
+
|
391
|
+
# Since linux versions are distributed as sh archives, we need first to
|
392
|
+
# extract the sh archive, then extract the resulting tar file
|
393
|
+
def install_lilypond_files_linux(fn, target, platform, version, opts)
|
394
|
+
STDERR.puts "Extracting..." unless opts[:silent]
|
395
|
+
|
396
|
+
# create temp directory in which to extract .sh file
|
397
|
+
tmp_dir = "#{$TMP_ROOT}/#{Time.now.to_f}"
|
398
|
+
FileUtils.mkdir_p(tmp_dir)
|
399
|
+
|
400
|
+
FileUtils.cd(tmp_dir) do
|
401
|
+
exec "sh #{fn} --tarball >/dev/null"
|
402
|
+
end
|
403
|
+
|
404
|
+
tmp_fn = "#{tmp_dir}/lilypond-#{version}-1.#{platform}.tar.bz2"
|
405
|
+
|
406
|
+
exec "tar -xjf #{tmp_fn} -C #{target}"
|
407
|
+
|
408
|
+
copy_lilypond_files("#{target}/usr", version, opts)
|
409
|
+
ensure
|
410
|
+
FileUtils.rm_rf(tmp_dir)
|
411
|
+
end
|
412
|
+
|
413
|
+
def install_lilypond_files_windows(fn, target, platform, version, opts)
|
414
|
+
STDERR.puts "Running NSIS Installer..." unless opts[:silent]
|
415
|
+
|
416
|
+
target_dir = File.join(Lyp.lilyponds_dir, version)
|
417
|
+
FileUtils.mkdir_p(target_dir)
|
418
|
+
|
419
|
+
# run installer
|
420
|
+
cmd = "#{fn} /S /D=#{target_dir.gsub('/', '\\')}"
|
421
|
+
`#{cmd}`
|
422
|
+
|
423
|
+
# wait for installer to finish
|
424
|
+
t1 = Time.now
|
425
|
+
while !File.file?("#{target_dir}/usr/bin/lilypond.exe")
|
426
|
+
sleep 0.5
|
427
|
+
raise "Windows installation failed" if Time.now - t1 >= 60
|
428
|
+
end
|
429
|
+
|
430
|
+
# Show lilypond versions
|
431
|
+
STDERR.puts `#{target_dir}/usr/bin/lilypond -v` unless opts[:silent] || opts[:no_version_test]
|
432
|
+
end
|
433
|
+
|
434
|
+
def copy_lilypond_files(base_path, version, opts)
|
435
|
+
target_dir = File.join(Lyp.lilyponds_dir, version)
|
436
|
+
|
437
|
+
FileUtils.rm_rf(target_dir) if File.exists?(target_dir)
|
438
|
+
|
439
|
+
# create directory for lilypond files
|
440
|
+
FileUtils.mkdir_p(target_dir)
|
441
|
+
|
442
|
+
# copy files
|
443
|
+
STDERR.puts "Copying..." unless opts[:silent]
|
444
|
+
%w{bin etc lib lib64 share var}.each do |entry|
|
445
|
+
dir = File.join(base_path, entry)
|
446
|
+
FileUtils.cp_r(dir, target_dir, remove_destination: true) if File.directory?(dir)
|
447
|
+
end
|
448
|
+
|
449
|
+
# Show lilypond versions
|
450
|
+
STDERR.puts `#{target_dir}/bin/lilypond -v` unless opts[:silent] || opts[:no_version_test]
|
451
|
+
rescue => e
|
452
|
+
puts e.message
|
453
|
+
end
|
454
|
+
|
455
|
+
def patch_font_scm(version)
|
456
|
+
return unless Lyp::FONT_PATCH_REQ =~ Gem::Version.new(version)
|
457
|
+
|
458
|
+
target_fn = File.join(lyp_lilypond_share_dir(version), 'lilypond/current/scm/font.scm')
|
459
|
+
FileUtils.cp(Lyp::FONT_PATCH_FILENAME, target_fn)
|
460
|
+
end
|
461
|
+
|
462
|
+
def copy_fonts_from_all_packages(version, opts)
|
463
|
+
return unless Lyp::FONT_COPY_REQ =~ Gem::Version.new(version)
|
464
|
+
|
465
|
+
ly_fonts_dir = File.join(lyp_lilypond_share_dir(version), 'lilypond/current/fonts')
|
466
|
+
|
467
|
+
Dir["#{Lyp.packages_dir}/**/fonts"].each do |package_fonts_dir|
|
468
|
+
|
469
|
+
Dir["#{package_fonts_dir}/*.otf"].each do |fn|
|
470
|
+
target_fn = File.join(ly_fonts_dir, 'otf', File.basename(fn))
|
471
|
+
FileUtils.cp(fn, target_fn)
|
472
|
+
end
|
473
|
+
|
474
|
+
Dir["#{package_fonts_dir}/*.svg"].each do |fn|
|
475
|
+
target_fn = File.join(ly_fonts_dir, 'svg', File.basename(fn))
|
476
|
+
FileUtils.cp(fn, target_fn)
|
477
|
+
end
|
478
|
+
|
479
|
+
Dir["#{package_fonts_dir}/*.woff"].each do |fn|
|
480
|
+
target_fn = File.join(ly_fonts_dir, 'svg', File.basename(fn))
|
481
|
+
FileUtils.cp(fn, target_fn)
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
def lyp_lilypond_share_dir(version)
|
487
|
+
File.join(Lyp.lilyponds_dir, version, 'share')
|
488
|
+
end
|
489
|
+
|
490
|
+
def use(version, opts)
|
491
|
+
lilypond_list = list.reverse
|
492
|
+
|
493
|
+
case version
|
494
|
+
when 'system'
|
495
|
+
lilypond = lilypond_list.find {|v| v[:system] }
|
496
|
+
unless lilypond
|
497
|
+
raise "Could not find a system installed version of lilypond"
|
498
|
+
end
|
499
|
+
when 'latest'
|
500
|
+
lilypond = lilypond_list.first
|
501
|
+
when 'stable'
|
502
|
+
lilypond = lilypond_list.find do |v|
|
503
|
+
Gem::Version.new(v[:version]).segments[1].even?
|
504
|
+
end
|
505
|
+
when 'unstable'
|
506
|
+
lilypond = lilypond_list.find do |v|
|
507
|
+
Gem::Version.new(v[:version]).segments[1].odd?
|
508
|
+
end
|
509
|
+
else
|
510
|
+
version = "~>#{version}.0" if version =~ /^\d+\.\d+$/
|
511
|
+
req = Gem::Requirement.new(version)
|
512
|
+
lilypond = lilypond_list.find {|v| req =~ Gem::Version.new(v[:version])}
|
513
|
+
end
|
514
|
+
|
515
|
+
unless lilypond
|
516
|
+
raise "Could not find a lilypond matching \"#{version}\""
|
517
|
+
end
|
518
|
+
|
519
|
+
set_current_lilypond(lilypond[:path])
|
520
|
+
set_default_lilypond(lilypond[:path]) if opts[:default]
|
521
|
+
|
522
|
+
lilypond
|
523
|
+
end
|
524
|
+
|
525
|
+
def uninstall(version_specifier, opts = {})
|
526
|
+
list = list(lyp_only: true)
|
527
|
+
if version_specifier
|
528
|
+
list.select! {|l| version_match(l[:version], version_specifier, list)}
|
529
|
+
elsif !opts[:all]
|
530
|
+
# if no version is specified
|
531
|
+
raise "No version specifier given.\nTo uninstall all versions run 'lyp uninstall lilypond -a'.\n"
|
532
|
+
end
|
533
|
+
|
534
|
+
if list.empty?
|
535
|
+
if version_specifier
|
536
|
+
raise "No lilypond found matching #{version_specifier}"
|
537
|
+
else
|
538
|
+
raise "No lilypond found"
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
list.each do |l|
|
543
|
+
puts "Uninstalling lilypond #{l[:version]}" unless opts[:silent]
|
544
|
+
set_current_lilypond(nil) if l[:current]
|
545
|
+
set_default_lilypond(nil) if l[:default]
|
546
|
+
uninstall_lilypond_version(l[:root_path])
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
def uninstall_lilypond_version(path)
|
551
|
+
FileUtils.rm_rf(path)
|
552
|
+
end
|
553
|
+
|
554
|
+
def exec(cmd, raise_on_failure = true)
|
555
|
+
$_out = ""
|
556
|
+
$_err = ""
|
557
|
+
success = nil
|
558
|
+
Open3.popen3(cmd) do |_in, _out, _err, wait_thr|
|
559
|
+
exit_value = wait_thr.value
|
560
|
+
$_out = _out.read
|
561
|
+
$_err = _err.read
|
562
|
+
success = exit_value == 0
|
563
|
+
end
|
564
|
+
if !success && raise_on_failure
|
565
|
+
raise "Error executing cmd #{cmd}: #{$_err}"
|
566
|
+
end
|
567
|
+
success
|
568
|
+
end
|
569
|
+
end
|
570
|
+
end
|