]> git.donarmstrong.com Git - lilypond.git/blob - SConstruct
*** empty log message ***
[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 subdirs = ['flower', 'lily', 'mf', 'scm', 'ly', 'Documentation',
61            'Documentation/user', 'input']
62
63 usage = r'''Usage:
64 scons [KEY=VALUE].. [TARGET]..
65
66 where TARGET is lily|all|fonts|doc|tar|dist|release
67 '''
68       
69 env = Environment ()
70
71 # Without target arguments, build lily only
72 if not COMMAND_LINE_TARGETS:
73         env.Default ('lily')
74
75 # Target 'all' builds everything
76 #if 'all' in COMMAND_LINE_TARGETS:
77 #       env.Default (
78
79 env.Alias ('all', ['lily', 'mf', 'input', 'Documentation'])
80
81 # Put your favourite stuff in custom.py
82 opts = Options ('custom.py', ARGUMENTS)
83 #opts = Options (['config.cache', 'custom.py'], ARGUMENTS)
84 opts.Add ('prefix', 'Install prefix', '/usr/')
85 opts.Add ('out', 'Output directory', 'out-scons')
86 opts.Add ('build', 'Build directory', '.')
87 opts.AddOptions (
88         BoolOption ('warnings', 'compile with -Wall and similiar',
89                    1),
90         BoolOption ('debugging', 'compile with debugging symbols',
91                     0),
92         BoolOption ('optimising', 'compile with optimising',
93                     1),
94         BoolOption ('shared', 'build shared libraries',
95                     0),
96         BoolOption ('static', 'build static libraries',
97                     1),
98         BoolOption ('gui', 'build with GNOME backend (EXPERIMENTAL)',
99                     1),
100         BoolOption ('verbose', 'run commands with verbose flag',
101                     0),
102         )
103
104 Help (usage + opts.GenerateHelpText (env))
105
106 env = Environment (options = opts)
107
108 opts.Update (env)
109 #opts.Save ('config.cache', env)
110
111
112 env.CacheDir (os.path.join (env['build'], '=build-cache'))
113
114 #ugh
115 sys.path.append (os.path.join ('.', 'stepmake', 'bin'))
116 import packagepython
117 package = packagepython.Package ('.')
118
119 env['version'] = packagepython.version_tuple_to_str (package.version)
120 env['bindir'] = os.path.join (env['prefix'], 'bin')
121 env['sharedir'] = os.path.join (env['prefix'], 'share')
122 env['libdir'] = os.path.join (env['prefix'], 'lib')
123 env['localedir'] = os.path.join (env['sharedir'], 'locale')
124
125 env['sharedir_package'] = os.path.join (env['sharedir'], package.name)
126 env['sharedir_package_version'] = os.path.join (env['sharedir_package'],
127                                                  env['version'])
128 env['lilypondprefix'] = os.path.join (env['sharedir_package_version'])
129
130
131 if env['debugging']:
132         env.Append (CFLAGS = '-g')
133         env.Append (CXXFLAGS = '-g')
134 if env['optimising']:
135         env.Append (CFLAGS = '-O2')
136         env.Append (CXXFLAGS = '-O2')
137         env.Append (CXXFLAGS = '-DSTRING_UTILS_INLINED')
138 if env['warnings']:
139         env.Append (CFLAGS = '-W ')
140         env.Append (CFLAGS = '-Wall')
141         # what about = ['-W', '-Wall', ...]?
142         env.Append (CXXFLAGS = '-W')
143         env.Append (CXXFLAGS = '-Wall')
144         env.Append (CXXFLAGS = '-Wconversion')
145
146 env['MFMODE'] = 'ljfour'
147
148
149 conf = Configure (env)
150
151
152 vre = re.compile ('^.*[^-.0-9]([0-9][0-9]*\.[0-9][.0-9]*).*$', re.DOTALL)
153 def get_version (program):
154         command = '(%(program)s --version || %(program)s -V) 2>&1' % vars ()
155         pipe = os.popen (command)
156         output = pipe.read ()
157         if pipe.close ():
158                 return None
159         v = re.sub (vre, '\\1', output)
160         return string.split (v, '.')
161
162 def assert_version (lst, program, minimal, description, package):
163         global required
164         sys.stdout.write ('Checking %s version... ' % program)
165         actual = get_version (program)
166         if not actual:
167                 print 'not found'
168                 lst.append ((description, package, minimal, program,
169                              'not installed'))
170                 return
171         sys.stdout.write (string.join (actual, '.'))
172         sys.stdout.write ('\n')
173         if actual < string.split (minimal, '.'):
174                 lst.append ((description, package, minimal, program,
175                              string.join (actual, '.')))
176
177 required = []
178 assert_version (required, 'gcc', '2.8', 'GNU C compiler', 'gcc')
179 assert_version (required, 'g++', '3.0.5', 'GNU C++ compiler', 'g++')
180 assert_version (required, 'python', '2.1', 'Python (www.python.org)', 'python')
181 assert_version (required, 'guile-config', '1.6', 'GUILE development',
182                 'libguile-dev or guile-devel')
183 # Do not use bison 1.50 and 1.75.
184 assert_version (required, 'bison', '1.25', 'Bison -- parser generator',
185                 'bison')
186 assert_version (required, 'flex', '0.0', 'Flex -- lexer generator', 'flex')
187
188
189 optional = []
190 assert_version (optional, 'makeinfo', '4.7', 'Makeinfo tool', 'texinfo')
191 assert_version (optional, 'guile', '1.6', 'GUILE scheme',
192                 'libguile-dev or guile-devel')
193 assert_version (optional, 'mftrace', '1.0.27', 'Metafont tracing Type1',
194                 'mftrace')
195 assert_version (optional, 'perl', '4.0',
196                 'Perl practical efficient readonly language', 'perl')
197 #assert_version (optional, 'foo', '2.0', 'Foomatic tester', 'bar')
198
199
200 defines = {
201    'DIRSEP' : "'/'",
202    'PATHSEP' : "':'",
203    'TOPLEVEL_VERSION' : '"' + env['version'] + '"',
204    'PACKAGE': '"' + package.name + '"',
205    'DATADIR' : '"' + env['sharedir'] + '"',
206    'LILYPOND_DATADIR' : '"' + env['sharedir_package'] + '"',
207    'LOCAL_LILYPOND_DATADIR' : '"' + env['sharedir_package_version'] + '"',
208    'LOCALEDIR' : '"' + env['localedir'] + '"',
209 }
210
211
212 command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % (sys.prefix, sys.version[:3]))'""" #"
213 PYTHON_INCLUDE = os.popen (command).read ()
214 env.Append (CPPPATH = PYTHON_INCLUDE)
215
216 headers = ('sys/stat.h', 'assert.h', 'kpathsea/kpathsea.h', 'Python.h')
217 for i in headers:
218         if conf.CheckCHeader (i):
219                 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
220                 defines[key] = '1'
221
222 ccheaders = ('sstream',)
223 for i in ccheaders:
224         if conf.CheckCXXHeader (i):
225                 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
226                 defines[key] = '1'
227
228 functions = ('gettext', 'isinf', 'memmem', 'snprintf', 'vsnprintf')
229 for i in functions:
230         if 0 or conf.CheckFunc (i):
231                 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
232                 defines[key] = '1'
233
234 key = 'HAVE_FLEXLEXER_YY_CURRENT_BUFFER'
235
236 sys.stdout.write('Checking for yy_current_buffer ... ')
237 sys.stdout.flush()
238 res = conf.TryCompile ("""using namespace std;
239 #include <FlexLexer.h>
240 class yy_flex_lexer: public yyFlexLexer
241 {
242   public:
243     yy_flex_lexer ()
244     {
245       yy_current_buffer = 0;
246     }
247 };""", '.cc')
248 if res:
249         defines[key] = '1'
250         sys.stdout.write('yes\n')
251 else:
252         sys.stdout.write('no\n')
253
254
255 if conf.CheckLib ('dl'):
256         pass
257
258 if conf.CheckLib ('kpathsea'):
259         defines['KPATHSEA'] = '1'
260
261 # huh? 
262 if conf.CheckLib ('kpathsea', 'kpse_find_file'):
263         defines['HAVE_KPSE_FIND_FILE'] = '1'
264 if conf.CheckLib ('kpathsea', 'kpse_find_tfm'):
265         defines['HAVE_KPSE_FIND_TFM'] = '1'
266
267 #this could happen after flower...
268 env.ParseConfig ('guile-config compile')
269
270 #this could happen only for compiling pango-*
271 if env['gui']:
272         env.ParseConfig ('pkg-config --cflags --libs gtk+-2.0')
273         env.ParseConfig ('pkg-config --cflags --libs pango')
274         if conf.CheckCHeader ('pango/pangofc-fontmap.h'):
275                 defines['HAVE_PANGO_PANGOFC_FONTMAP_H'] = '1'
276
277         if conf.CheckLib ('pango-1.0',
278                           'pango_fc_font_map_add_decoder_find_func'):
279                 defines['HAVE_PANGO_CVS'] = '1'
280                 defines['HAVE_PANGO_FC_FONT_MAP_ADD_DECODER_FIND_FUNC'] = '1'
281
282 env = conf.Finish ()
283
284 ##Import ('env')
285 here = os.getcwd ()
286 reldir = str (Dir ('.').srcnode ())
287 os.chdir (reldir)
288 srcdir = os.getcwd ()
289 os.chdir (here)
290 ##outdir = os.path.join (env['build'], reldir, env['out'])
291 outdir = os.path.join (env['build'], env['out'])
292
293 env['srcdir'] = srcdir
294 build = env['build']
295 out = env['out']
296
297
298 if not os.path.exists (outdir):
299         os.mkdir (outdir)
300
301 def list_sort (lst):
302         sorted = lst
303         sorted.sort ()
304         return sorted
305         
306 config = open (os.path.join (outdir, 'config.h'), 'w')
307 for i in list_sort (defines.keys ()):
308         config.write ('#define %s %s\n' % (i, defines[i]))
309 config.close ()
310
311 os.system (sys.executable \
312            + ' ./stepmake/bin/make-version.py VERSION > '\
313            + os.path.join (outdir, 'version.hh'))
314
315 if os.path.exists ('parser'):
316         env.Append (LIBPATH = ['#/flower', '#/lily', '#/parser', '#/gui',],
317                     CPPPATH = [outdir, '#',])
318 else:   
319         env.Append (LIBPATH = ['#/flower/' + out,],
320                     CPPPATH = [outdir, '#',])
321
322 if required:
323         print
324         print '********************************'
325         print 'Please install required packages'
326         for i in required:
327                 print '%s:      %s-%s or newer (found: %s %s)' % i
328         sys.exit (1)
329
330 if optional:
331         print
332         print '*************************************'
333         print 'Consider installing optional packages'
334         for i in optional:
335                 print '%s:      %s-%s or newer (found: %s %s)' % i
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
372 tarbase = package.name + '-' + env['version']
373 tarname = tarbase + '.tar.gz'
374 tarball = os.path.join (outdir, tarname)
375 env['tarball'] = tarball
376
377 ballprefix = os.path.join (outdir, tarbase) + '/'
378 env['ballprefix'] = ballprefix
379
380 SConscript ('buildscripts/builder.py')
381
382 readme_files = ['ChangeLog', 'COPYING', 'DEDICATION', 'ROADMAP', 'THANKS']
383 readme_txt = ['AUTHORS.txt', 'README.txt', 'INSTALL.txt', 'NEWS.txt']
384 # to be [re]moved after spit
385 patch_files = ['emacsclient.patch', 'server.el.patch', 'darwin.patch']
386
387 #testing
388 env.Append (TARFLAGS = '-z --owner=0 --group=0')
389 env.Append (GZIPFLAGS = '-9')
390 all_sources = ['SConstruct', 'VERSION', '.cvsignore']\
391               + readme_files + readme_txt + patch_files
392
393 map (lambda x: env.Texi2txt (x, os.path.join ('Documentation/topdocs',
394                                               os.path.splitext (x)[0])),
395      readme_txt)
396
397 #print `all_sources`
398 #print `map (lambda x: env['ballprefix'] + x, all_sources)`
399 #ballize = map (env.BALL, all_sources)
400 #ballize = map (env.BALL, ['SConstruct', 'VERSION'])
401 #tar = env.Tar (tarball, map (lambda x: env['ballprefix'] + x, all_sources))
402 tar = env.Tar (env['tarball'], all_sources)
403 env.Alias ('tar', tar)
404
405 distball = os.path.join (package.release_dir, tarname)
406 env.Command (distball, tarball,
407              'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
408              + 'ln $SOURCE $TARGET')
409 env.Depends ('dist', distball)
410 patchfile = os.path.join (outdir, tarbase + '.diff.gz')
411 patch = env.PATCH (patchfile, tarball)
412 env.Depends (patchfile, distball)
413 env.Alias ('release', patch)
414
415
416 for d in subdirs:
417         b = os.path.join (build, d, out)
418         # Support clean sourctree build (srcdir build)
419         # and outdir build.
420         # TODO: figure out SConscript (dir, builddir, duplicate)) feature
421         if (build and build != '.') \
422            or (out and out != '.'):
423                 env.BuildDir (b, d, duplicate=0)
424         SConscript (os.path.join (b, 'SConscript'))
425
426 # as a builder?
427 def symlink_tree (prefix):
428         def mkdirs (dir):
429                 def mkdir (dir):
430                         if not dir:
431                                 os.chdir (os.sep)
432                                 return
433                         if not os.path.isdir (dir):
434                                 if os.path.exists (dir):
435                                         os.unlink (dir)
436                                 os.mkdir (dir)
437                         os.chdir (dir)
438                 map (mkdir, string.split (dir, os.sep))
439         #srcdir = os.getcwd ()
440         def symlink (src, dst):
441                 os.chdir (absbuild)
442                 dir = os.path.dirname (dst)
443                 mkdirs (dir)
444                 if src[0] == '#':
445                         frm = os.path.join (srcdir, src[1:])
446                 else:
447                         print 'dst: ' + dst
448                         depth = len (string.split (dir, '/'))
449                         print 'depth: ' + `depth`
450                         frm = os.path.join ('../' * depth, src, out)
451                 print 'cwd: ' + `os.getcwd ()`
452                 print 'frm: ' + frm
453                 print 'dst: ' + dst
454                 os.symlink (frm, os.path.basename (dst))
455         map (lambda x: symlink (x[0], os.path.join (prefix, x[1])),
456              (('python', 'lib/lilypond/python'),
457               # UGHR, lilypond.py uses lilypond-bin from PATH
458               ('lily',   'bin'),
459               ('#mf',    'share/lilypond/fonts/mf'),
460               ('mf',     'share/lilypond/fonts/afm'),
461               ('mf',     'share/lilypond/fonts/tfm'),
462               ('mf',     'share/lilypond/fonts/type1'),
463               ('#tex',   'share/lilypond/tex/source'),
464               ('mf',     'share/lilypond/tex/generate'),
465               ('#ly',    'share/lilypond/ly'),
466               ('#scm',   'share/lilypond/scm'),
467               ('#ps',    'share/lilypond/ps'),
468               ('elisp',  'share/lilypond/elisp')))
469         os.chdir (srcdir)
470
471 if env['debugging']:
472         prefix = os.path.join (out, 'usr')
473         if not os.path.exists (prefix):
474                 symlink_tree (prefix)
475