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