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