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