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