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