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
@@ -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
|