]> git.donarmstrong.com Git - lilypond.git/blob - SConstruct
* SConstruct:
[lilypond.git] / SConstruct
1 # -*-python-*-
2
3 '''
4 Experimental scons (www.scons.org) building:
5
6 Usage:
7     scons
8     scons lily            # build lily
9
10     LILYPONDPREFIX=out-scons/usr/share/lilypond lily/out-scons/lilypond-bin
11     scons doc             # build web doc
12
13     scons fonts           # build all font stuff (split this? )
14
15 XXX    scons                 # without args builds all targets below ./
16 XXX                          # maybe catch this one and only build lily?
17     scons /               # builds all possible targets
18
19     scons install
20     scons -c              # clean
21     scons -h              # help
22
23     scons build=DIR       # scrdir build, write to new tree =build
24     scons out=DIR         # write output to deeper dir DIR
25
26 Optionally, make a custom.py.  I have
27
28 import os
29 out='out-scons'
30 optimising=0
31 debugging=1
32 gui=1
33 os.path.join (os.getcwd (), '=install')
34 prefix=os.path.join (os.environ['HOME'], 'usr', 'pkg', 'lilypond')
35
36 '''
37
38
39 # TODO:
40 #   * separate environments?
41 #     - compile environment checks headers and libraries
42 #     - doc environment checks doc stuff
43 #   * - help for targets?
44 #     - build symlink tree
45 #   * commandline targets:
46 #      - clean => -c
47 #      - dist, tar => env.Tar
48 #   * Documentation, scripts
49 #   * env.Tar
50 #   * more fine-grained config.h -- move lilypondprefix to version.hh?
51 #     - config.h:   changes after system upgrades, affects all files
52 #     - version.hh:  prefix, version etc?  affects few
53
54 import re
55 import glob
56 import os
57 import sys
58 import string
59
60 env = Environment ()
61
62 # Without target arguments, build lily only
63 if not COMMAND_LINE_TARGETS:
64         env.Default ('lily')
65
66 # Target 'all' builds everything
67 if 'all' in COMMAND_LINE_TARGETS:
68         env.Default ('lily', 'fonts', 'doc')
69
70 # put your favourite stuff in custom.py
71 opts = Options ('custom.py', ARGUMENTS)
72 #opts = Options (['config.cache', 'custom.py'], ARGUMENTS)
73 opts.Add ('prefix', 'Install prefix', '/usr/')
74 opts.Add ('out', 'Output directory', 'out-scons')
75 opts.Add ('build', 'Build directory', '.')
76 opts.AddOptions (
77         BoolOption ('warnings', 'compile with -Wall and similiar',
78                    1),
79         BoolOption ('debugging', 'compile with debugging symbols',
80                     0),
81         BoolOption ('optimising', 'compile with optimising',
82                     1),
83         BoolOption ('shared', 'build shared libraries',
84                     0),
85         BoolOption ('static', 'build static libraries',
86                     1),
87         BoolOption ('gui', 'build with GNOME backend (EXPERIMENTAL)',
88                     1),
89         BoolOption ('verbose', 'run commands with verbose flag',
90                     0),
91         )
92
93 Help (opts.GenerateHelpText (env))
94
95 env = Environment (options = opts)
96
97 opts.Update (env)
98 #opts.Save ('config.cache', env)
99
100
101 env.CacheDir (os.path.join (env['build'], '=build-cache'))
102
103 #ugh
104 sys.path.append (os.path.join ('.', 'stepmake', 'bin'))
105 import packagepython
106 package = packagepython.Package ('.')
107
108 env['version'] = packagepython.version_tuple_to_str (package.version)
109 env['bindir'] = os.path.join (env['prefix'], 'bin')
110 env['sharedir'] = os.path.join (env['prefix'], 'share')
111 env['libdir'] = os.path.join (env['prefix'], 'lib')
112 env['localedir'] = os.path.join (env['sharedir'], 'locale')
113
114 env['sharedir_package'] = os.path.join (env['sharedir'], package.name)
115 env['sharedir_package_version'] = os.path.join (env['sharedir_package'],
116                                                  env['version'])
117 env['lilypondprefix'] = os.path.join (env['sharedir_package_version'])
118
119
120 if env['debugging']:
121         env.Append (CFLAGS = '-g')
122         env.Append (CXXFLAGS = '-g')
123 if env['optimising']:
124         env.Append (CFLAGS = '-O2')
125         env.Append (CXXFLAGS = '-O2')
126         env.Append (CXXFLAGS = '-DSTRING_UTILS_INLINED')
127 if env['warnings']:
128         env.Append (CFLAGS = '-W ')
129         env.Append (CFLAGS = '-Wall')
130         # what about = ['-W', '-Wall', ...]?
131         env.Append (CXXFLAGS = '-W')
132         env.Append (CXXFLAGS = '-Wall')
133         env.Append (CXXFLAGS = '-Wconversion')
134
135 env['MFMODE'] = 'ljfour'
136
137
138 conf = Configure (env)
139
140
141 vre = re.compile ('^.*[^-.0-9]([0-9][0-9]*\.[0-9][.0-9]*).*$', re.DOTALL)
142 def get_version (program):
143         command = '(%(program)s --version || %(program)s -V) 2>&1' % vars ()
144         pipe = os.popen (command)
145         output = pipe.read ()
146         if pipe.close ():
147                 return None
148         v = re.sub (vre, '\\1', output)
149         return string.split (v, '.')
150
151 def assert_version (lst, program, minimal, description, package):
152         global required
153         sys.stdout.write ('Checking %s version... ' % program)
154         actual = get_version (program)
155         if not actual:
156                 print 'not found'
157                 lst.append ((description, package, minimal, program,
158                              'not installed'))
159                 return
160         sys.stdout.write (string.join (actual, '.'))
161         sys.stdout.write ('\n')
162         if actual < string.split (minimal, '.'):
163                 lst.append ((description, package, minimal, program,
164                              string.join (actual, '.')))
165
166 required = []
167 assert_version (required, 'gcc', '2.8', 'GNU C compiler', 'gcc')
168 assert_version (required, 'g++', '3.0.5', 'GNU C++ compiler', 'g++')
169 assert_version (required, 'python', '2.1', 'Python (www.python.org)', 'python')
170 assert_version (required, 'guile-config', '1.6', 'GUILE development',
171                 'libguile-dev or guile-devel')
172 # Do not use bison 1.50 and 1.75.
173 assert_version (required, 'bison', '1.25', 'Bison -- parser generator',
174                 'bison')
175 assert_version (required, 'flex', '0.0', 'Flex -- lexer generator', 'flex')
176
177
178 optional = []
179 assert_version (optional, 'makeinfo', '4.7', 'Makeinfo tool', 'texinfo')
180 assert_version (optional, 'guile', '1.6', 'GUILE scheme',
181                 'libguile-dev or guile-devel')
182 assert_version (optional, 'mftrace', '1.0.27', 'Metafont tracing Type1',
183                 'mftrace')
184 assert_version (optional, 'perl', '4.0',
185                 'Perl practical efficient readonly language', 'perl')
186 #assert_version (optional, 'foo', '2.0', 'Foomatic tester', 'bar')
187
188
189 defines = {
190    'DIRSEP' : "'/'",
191    'PATHSEP' : "':'",
192    'TOPLEVEL_VERSION' : '"' + env['version'] + '"',
193    'PACKAGE': '"' + package.name + '"',
194    'DATADIR' : '"' + env['sharedir'] + '"',
195    'LILYPOND_DATADIR' : '"' + env['sharedir_package'] + '"',
196    'LOCAL_LILYPOND_DATADIR' : '"' + env['sharedir_package_version'] + '"',
197    'LOCALEDIR' : '"' + env['localedir'] + '"',
198 }
199
200
201 command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % (sys.prefix, sys.version[:3]))'""" #"
202 PYTHON_INCLUDE = os.popen (command).read ()
203 env.Append (CPPPATH = PYTHON_INCLUDE)
204
205 headers = ('sys/stat.h', 'assert.h', 'kpathsea/kpathsea.h', 'Python.h')
206 for i in headers:
207         if conf.CheckCHeader (i):
208                 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
209                 defines[key] = '1'
210
211 ccheaders = ('sstream',)
212 for i in ccheaders:
213         if conf.CheckCXXHeader (i):
214                 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
215                 defines[key] = '1'
216
217 functions = ('gettext', 'isinf', 'memmem', 'snprintf', 'vsnprintf')
218 for i in functions:
219         if 0 or conf.CheckFunc (i):
220                 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
221                 defines[key] = '1'
222
223 key = 'HAVE_FLEXLEXER_YY_CURRENT_BUFFER'
224
225 sys.stdout.write('Checking for yy_current_buffer ... ')
226 sys.stdout.flush()
227 res = conf.TryCompile ("""using namespace std;
228 #include <FlexLexer.h>
229 class yy_flex_lexer: public yyFlexLexer
230 {
231   public:
232     yy_flex_lexer ()
233     {
234       yy_current_buffer = 0;
235     }
236 };""", '.cc')
237 if res:
238         defines[key] = '1'
239         sys.stdout.write('yes\n')
240 else:
241         sys.stdout.write('no\n')
242
243
244 if conf.CheckLib ('dl'):
245         pass
246
247 if conf.CheckLib ('kpathsea'):
248         defines['KPATHSEA'] = '1'
249
250 # huh? 
251 if conf.CheckLib ('kpathsea', 'kpse_find_file'):
252         defines['HAVE_KPSE_FIND_FILE'] = '1'
253 if conf.CheckLib ('kpathsea', 'kpse_find_tfm'):
254         defines['HAVE_KPSE_FIND_TFM'] = '1'
255
256 #this could happen after flower...
257 env.ParseConfig ('guile-config compile')
258
259 #this could happen only for compiling pango-*
260 if env['gui']:
261         env.ParseConfig ('pkg-config --cflags --libs gtk+-2.0')
262         env.ParseConfig ('pkg-config --cflags --libs pango')
263         if conf.CheckCHeader ('pango/pangofc-fontmap.h'):
264                 defines['HAVE_PANGO_PANGOFC_FONTMAP_H'] = '1'
265
266         if conf.CheckLib ('pango-1.0',
267                           'pango_fc_font_map_add_decoder_find_func'):
268                 defines['HAVE_PANGO_CVS'] = '1'
269                 defines['HAVE_PANGO_FC_FONT_MAP_ADD_DECODER_FIND_FUNC'] = '1'
270
271 env = conf.Finish ()
272
273 here = os.getcwd ()
274 reldir = str (Dir ('.').srcnode ())
275 os.chdir (reldir)
276 srcdir = os.getcwd ()
277 os.chdir (here)
278 env['srcdir'] = srcdir
279
280 build = env['build']
281 out = env['out']
282 ##reldir = str (Dir ('.').srcnode ())
283 reldir = os.getcwd ()
284 outdir = os.path.join (env['build'], reldir, env['out'])
285 if not os.path.exists (outdir):
286         os.mkdir (outdir)
287
288 def list_sort (lst):
289         sorted = lst
290         sorted.sort ()
291         return sorted
292         
293 config = open (os.path.join (outdir, 'config.h'), 'w')
294 for i in list_sort (defines.keys ()):
295         config.write ('#define %s %s\n' % (i, defines[i]))
296 config.close ()
297
298 os.system (sys.executable \
299            + ' ./stepmake/bin/make-version.py VERSION > '\
300            + os.path.join (outdir, 'version.hh'))
301
302 if os.path.exists ('parser'):
303         env.Append (LIBPATH = ['#/flower', '#/lily', '#/parser', '#/gui',],
304                     CPPPATH = [outdir, '#',])
305 else:   
306         env.Append (LIBPATH = ['#/flower/' + out,],
307                     CPPPATH = [outdir, '#',])
308
309 if required:
310         print
311         print '********************************'
312         print 'Please install required packages'
313         for i in required:
314                 print '%s:      %s-%s or newer (found: %s %s)' % i
315         sys.exit (1)
316
317 if optional:
318         print
319         print '*************************************'
320         print 'Consider installing optional packages'
321         for i in optional:
322                 print '%s:      %s-%s or newer (found: %s %s)' % i
323
324 #env['tarball'] = os.path.join (outdir,
325 #                              package.name + '-' + env['version'] + '.tar.gz')
326
327 env['tarball'] = os.path.join (os.environ['HOME'], 'tmp',
328                                package.name + '-' + env['version'] + '.tar.gz')
329
330 # huh?
331 if 'tar' in COMMAND_LINE_TARGETS:
332         #env.Default (env['tarball'])
333         #env.Default (tar)
334         env.Default (env['tarball'])
335         #Default (tar)
336
337 Export ('env')
338
339 #ugr
340 if build == '.':
341         absbuild = os.getcwd ()
342 else:
343         absbuild = build
344 env['absbuild'] = absbuild
345
346 # duh
347 env['MAKEINFO'] = 'LANG= makeinfo'
348 env['PYTHON'] = 'python'
349 env['LILYPOND_BIN'] = os.path.join (absbuild, 'lily', out, 'lilypond-bin')
350 env['LILYPONDPREFIX'] = os.path.join (outdir, 'usr/share/lilypond')
351 env['LILYPOND_BOOK'] = srcdir + '/scripts/lilypond-book.py'
352 env['ABC2LY_PY'] = srcdir + '/scripts/abc2ly.py'
353 env['MF_TO_TABLE_PY'] = srcdir + '/buildscripts/mf-to-table.py'
354 env['LILYPOND_PY'] = srcdir + '/scripts/lilypond.py'
355 env['LILYPOND_BOOK_FLAGS'] = ''
356 env['LILYPOND_BOOK_FORMAT'] = 'texi-html'
357 # ugh?
358 env['LILYPOND_BOOK_PATH'] = ['.', '#/input', '#/input/regression',
359                              '#/input/test', '#/input/tutorial',
360                              os.path.join (absbuild, 'mf', out),
361                              '#/Documentation/user',
362                              os.path.join (absbuild, 'Documentation', out),
363                              os.path.join (absbuild, 'Documentation/user', out),
364                              ]
365                              
366 env['MAKEINFO_PATH'] = ['.', '#/Documentation/user',
367                         os.path.join (absbuild, 'Documentation/user', out)]
368
369 ## TEXINFO_PAPERSIZE_OPTION= $(if $(findstring $(PAPERSIZE),a4),,-t @afourpaper)
370 env['TEXINFO_PAPERSIZE_OPTION'] = '-t @afourpaper'
371 env.Append (PYTHONPATH = [os.path.join (outdir, 'usr/lib/python'),
372                           os.path.join (srcdir, 'buildscripts'),
373                           os.path.join (srcdir, 'python')])
374 # huh, aha?
375 # GS_FONTPATH, GS_LIB?
376 env.Append (ENV = { 'PYTHONPATH' : string.join (env['PYTHONPATH'],
377                                                 os.pathsep) } )
378 # UGHR, lilypond.py uses lilypond-bin from PATH
379 env.Append (ENV = { 'PATH' : os.path.join (outdir, 'usr/bin') })
380
381 SConscript ('buildscripts/builder.py')
382
383 #subdirs = ['mf',]
384 #subdirs = ['flower', 'lily', 'parser', 'gui', 'main',]
385 #subdirs = ['flower', 'lily', 'mf', 'scm', 'ly']
386 subdirs = ['flower', 'lily', 'mf', 'scm', 'ly', 'Documentation',
387            'Documentation/user', 'input']
388 for d in subdirs:
389         b = os.path.join (build, d, out)
390         # Support clean sourctree build (srcdir build)
391         # and outdir build.
392         # TODO: figure out SConscript (dir, builddir, duplicate)) feature
393         if (build and build != '.') \
394            or (out and out != '.'):
395                 env.BuildDir (b, d, duplicate=0)
396         SConscript (os.path.join (b, 'SConscript'))
397
398 readme_files = ['ChangeLog', 'COPYING', 'DEDICATION', 'ROADMAP', 'THANKS']
399 readme_txt = ['AUTHORS.txt', 'README.txt', 'INSTALL.txt', 'NEWS.txt']
400 # to be [re]moved after spit
401 patch_files = ['emacsclient.patch', 'server.el.patch', 'darwin.patch']
402
403 map (lambda x: env.Texi2txt (x, os.path.join ('Documentation/topdocs',
404                                               os.path.splitext (x)[0])),
405      readme_txt)
406
407 #testing
408 env.Append (TARFLAGS = '-z --owner=0 --group=0')
409 env.Append (GZIPFLAGS = '-9')
410 all_sources = ['SConstruct',] + subdirs \
411               + ['VERSION', '.cvsignore']\
412               + readme_files + readme_txt + patch_files
413
414 tar = env.Tar (env['tarball'], all_sources)
415
416 # as a builder?
417 def symlink_tree (prefix):
418         def mkdirs (dir):
419                 def mkdir (dir):
420                         if not dir:
421                                 os.chdir (os.sep)
422                                 return
423                         if not os.path.isdir (dir):
424                                 if os.path.exists (dir):
425                                         os.unlink (dir)
426                                 os.mkdir (dir)
427                         os.chdir (dir)
428                 map (mkdir, string.split (dir, os.sep))
429         #srcdir = os.getcwd ()
430         def symlink (src, dst):
431                 os.chdir (absbuild)
432                 dir = os.path.dirname (dst)
433                 mkdirs (dir)
434                 if src[0] == '#':
435                         frm = os.path.join (srcdir, src[1:])
436                 else:
437                         print 'dst: ' + dst
438                         depth = len (string.split (dir, '/'))
439                         print 'depth: ' + `depth`
440                         frm = os.path.join ('../' * depth, src, out)
441                 print 'cwd: ' + `os.getcwd ()`
442                 print 'frm: ' + frm
443                 print 'dst: ' + dst
444                 os.symlink (frm, os.path.basename (dst))
445         map (lambda x: symlink (x[0], os.path.join (prefix, x[1])),
446              (('python', 'lib/lilypond/python'),
447               # UGHR, lilypond.py uses lilypond-bin from PATH
448               ('lily',   'bin'),
449               ('#mf',    'share/lilypond/fonts/mf'),
450               ('mf',     'share/lilypond/fonts/afm'),
451               ('mf',     'share/lilypond/fonts/tfm'),
452               ('mf',     'share/lilypond/fonts/type1'),
453               ('#tex',   'share/lilypond/tex/source'),
454               ('mf',     'share/lilypond/tex/generate'),
455               ('#ly',    'share/lilypond/ly'),
456               ('#scm',   'share/lilypond/scm'),
457               ('#ps',    'share/lilypond/ps'),
458               ('elisp',  'share/lilypond/elisp')))
459         os.chdir (srcdir)
460
461 if env['debugging']:
462         prefix = os.path.join (out, 'usr')
463         if not os.path.exists (prefix):
464                 symlink_tree (prefix)