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