]> 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 #   * 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
204 key = 'HAVE_FLEXLEXER_YY_CURRENT_BUFFER'
205 defines[key] = conf.TryCompile ("""using namespace std;
206 #include <FlexLexer.h>
207 class yy_flex_lexer: public yyFlexLexer
208 {
209   public:
210     yy_flex_lexer ()
211     {
212       yy_current_buffer = 0;
213     }
214 };""", 'cc')
215
216 if conf.CheckLib ('dl'):
217         pass
218
219 if conf.CheckLib ('kpathsea'):
220         defines['KPATHSEA'] = '1'
221
222 # huh? 
223 if conf.CheckLib ('kpathsea', 'kpse_find_file'):
224         defines['HAVE_KPSE_FIND_FILE'] = '1'
225 if conf.CheckLib ('kpathsea', 'kpse_find_tfm'):
226         defines['HAVE_KPSE_FIND_TFM'] = '1'
227
228 #this could happen after flower...
229 env.ParseConfig ('guile-config compile')
230
231 #this could happen only for compiling pango-*
232 if env['gui']:
233         env.ParseConfig ('pkg-config --cflags --libs gtk+-2.0')
234         env.ParseConfig ('pkg-config --cflags --libs pango')
235         if conf.CheckCHeader ('pango/pangofc-fontmap.h'):
236                 defines['HAVE_PANGO_PANGOFC_FONTMAP_H'] = '1'
237
238         if conf.CheckLib ('pango-1.0',
239                           'pango_fc_font_map_add_decoder_find_func'):
240                 defines['HAVE_PANGO_CVS'] = '1'
241                 defines['HAVE_PANGO_FC_FONT_MAP_ADD_DECODER_FIND_FUNC'] = '1'
242
243 env = conf.Finish ()
244
245 here = os.getcwd ()
246 reldir = str (Dir ('.').srcnode ())
247 os.chdir (reldir)
248 srcdir = os.getcwd ()
249 os.chdir (here)
250 env['srcdir'] = srcdir
251
252 build = env['build']
253 out = env['out']
254 ##reldir = str (Dir ('.').srcnode ())
255 reldir = os.getcwd ()
256 outdir = os.path.join (env['build'], reldir, env['out'])
257 if not os.path.exists (outdir):
258         os.mkdir (outdir)
259
260 def list_sort (lst):
261         sorted = lst
262         sorted.sort ()
263         return sorted
264         
265 config = open (os.path.join (outdir, 'config.h'), 'w')
266 for i in list_sort (defines.keys ()):
267         config.write ('#define %s %s\n' % (i, defines[i]))
268 config.close ()
269
270 os.system (sys.executable \
271            + ' ./stepmake/bin/make-version.py VERSION > '\
272            + os.path.join (outdir, 'version.hh'))
273
274 if os.path.exists ('parser'):
275         env.Append (LIBPATH = ['#/flower', '#/lily', '#/parser', '#/gui',],
276                     CPPPATH = [outdir, '#',])
277 else:   
278         env.Append (LIBPATH = ['#/flower/' + out,],
279                     CPPPATH = [outdir, '#',])
280
281 if required:
282         print
283         print '********************************'
284         print 'Please install required packages'
285         for i in required:
286                 print '%s:      %s-%s or newer (found: %s %s)' % i
287         sys.exit (1)
288
289 if optional:
290         print
291         print '*************************************'
292         print 'Consider installing optional packages'
293         for i in optional:
294                 print '%s:      %s-%s or newer (found: %s %s)' % i
295
296 #env['tarball'] = os.path.join (outdir,
297 #                              package.name + '-' + env['version'] + '.tar.gz')
298
299 env['tarball'] = os.path.join (os.environ['HOME'], 'tmp',
300                                package.name + '-' + env['version'] + '.tar.gz')
301
302 # huh?
303 if 'tar' in COMMAND_LINE_TARGETS:
304         #env.Default (env['tarball'])
305         #env.Default (tar)
306         env.Default (env['tarball'])
307         #Default (tar)
308
309 Export ('env')
310
311 #ugr
312 if build == '.':
313         absbuild = os.getcwd ()
314 else:
315         absbuild = build
316 env['absbuild'] = absbuild
317
318 # duh
319 env['MAKEINFO'] = 'LANG= makeinfo'
320 env['PYTHON'] = 'python'
321 env['LILYPOND_BIN'] = os.path.join (absbuild, 'lily', out, 'lilypond-bin')
322 env['LILYPONDPREFIX'] = os.path.join (outdir, 'usr/share/lilypond')
323 env['LILYPOND_BOOK'] = srcdir + '/scripts/lilypond-book.py'
324 env['LILYPOND_BOOK_FLAGS'] = ''
325 env['LILYPOND_BOOK_FORMAT'] = 'texi-html'
326 env['LILYPOND_BOOK_PATH'] = ['.', '#/input', '#/input/regression',
327                              '#/input/test', '#/input/tutorial',
328                              os.path.join (absbuild, 'mf', out),
329                              '#/Documentation/user',
330                              os.path.join (absbuild, 'Documentation', out)]
331                              
332 ## TEXINFO_PAPERSIZE_OPTION= $(if $(findstring $(PAPERSIZE),a4),,-t @afourpaper)
333 env['TEXINFO_PAPERSIZE_OPTION'] = '-t@afourpaper'
334
335 SConscript ('buildscripts/builder.py')
336
337 #subdirs = ['mf',]
338 #subdirs = ['flower', 'lily', 'parser', 'gui', 'main',]
339 #subdirs = ['flower', 'lily', 'mf', 'scm', 'ly']
340 subdirs = ['flower', 'lily', 'mf', 'scm', 'ly', 'Documentation']
341 for d in subdirs:
342         b = os.path.join (build, d, out)
343         # Support clean sourctree build (srcdir build)
344         # and outdir build.
345         # TODO: figure out SConscript (dir, builddir, duplicate)) feature
346         if (build and build != '.') \
347            or (out and out != '.'):
348                 env.BuildDir (b, d, duplicate=0)
349         SConscript (os.path.join (b, 'SConscript'))
350
351 readme_files = ['ChangeLog', 'COPYING', 'DEDICATION', 'ROADMAP', 'THANKS']
352 readme_txt = ['AUTHORS.txt', 'README.txt', 'INSTALL.txt', 'NEWS.txt']
353 # to be [re]moved after spit
354 patch_files = ['emacsclient.patch', 'server.el.patch', 'darwin.patch']
355
356 map (lambda x: env.Texi2txt (x, os.path.join ('Documentation/topdocs',
357                                               os.path.splitext (x)[0])),
358      readme_txt)
359
360 #testing
361 env.Append (TARFLAGS = '-z --owner=0 --group=0')
362 env.Append (GZIPFLAGS = '-9')
363 all_sources = ['SConstruct',] + subdirs \
364               + ['VERSION', '.cvsignore']\
365               + readme_files + readme_txt + patch_files
366
367 tar = env.Tar (env['tarball'], all_sources)
368
369 # as a builder?
370 def symlink_tree (prefix):
371         def mkdirs (dir):
372                 def mkdir (dir):
373                         if not dir:
374                                 os.chdir (os.sep)
375                                 return
376                         if not os.path.isdir (dir):
377                                 if os.path.exists (dir):
378                                         os.unlink (dir)
379                                 os.mkdir (dir)
380                         os.chdir (dir)
381                 map (mkdir, string.split (dir, os.sep))
382         #srcdir = os.getcwd ()
383         def symlink (src, dst):
384                 dir = os.path.dirname (dst)
385                 mkdirs (dir)
386                 if src[0] == '#':
387                         frm = os.path.join (srcdir, src[1:])
388                 else:
389                         depth = len (string.split (dir))
390                         frm = os.path.join ('../' * depth, src, out)
391                 os.symlink (frm, os.path.basename (dst))
392                 os.chdir (srcdir)
393         map (lambda x: symlink (x[0], os.path.join (prefix, x[1])),
394              (('python', 'lib/lilypond/python'),
395               ('#mf',    'share/lilypond/fonts/mf'),
396               ('mf',     'share/lilypond/fonts/amf'),
397               ('mf',     'share/lilypond/fonts/tfm'),
398               ('mf',     'share/lilypond/fonts/type1'),
399               ('#tex',   'share/lilypond/tex/source'),
400               ('mf',     'share/lilypond/tex/generate'),
401               ('#ly',    'share/lilypond/ly'),
402               ('#scm',   'share/lilypond/scm'),
403               ('#ps',    'share/lilypond/ps'),
404               ('elisp',  'share/lilypond/elisp')))
405
406 if env['debugging']:
407         prefix = os.path.join (outdir, 'usr')
408         if not os.path.exists (prefix):
409                 symlink_tree (prefix)