Merge pull request #575 from techee/colourise
[geany-mirror.git] / wscript
blobd7cabeb72113e648e1ddd119def85ce1c417f62c
1 # -*- coding: utf-8 -*-
3 # WAF build script - this file is part of Geany, a fast and lightweight IDE
5 # Copyright 2008-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
6 # Copyright 2008-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 """
23 This is a WAF build script (http://code.google.com/p/waf/).
24 It can be used as an alternative build system to autotools
25 for Geany. It does not (yet) cover all of the autotools tests and
26 configure options but all important things are working.
27 "make dist" should be done with autotools, most other targets and
28 functions should work better (regarding performance and flexibility)
29 or at least equally.
31 Missing features: --enable-binreloc, make targets: dist, pdf (in doc/)
32 Known issues: Dependency handling is buggy, e.g. if src/document.h is
33               changed, depending source files are not rebuilt (maybe Waf bug).
35 The code of this file itself loosely follows PEP 8 with some exceptions
36 (line width 100 characters and some other minor things).
38 Requires WAF 1.6.1 and Python 2.5 (or later).
39 """
42 import sys
43 import os
44 import tempfile
45 from waflib import Logs, Options, Scripting, Utils
46 from waflib.Build import BuildContext
47 from waflib.Configure import ConfigurationContext
48 from waflib.Errors import ConfigurationError, WafError
49 from waflib.TaskGen import feature, before_method
50 from waflib.Tools.compiler_c import c_compiler
51 from waflib.Tools.compiler_cxx import cxx_compiler
54 APPNAME = 'geany'
55 VERSION = '1.26'
56 LINGUAS_FILE = os.path.join('po', 'LINGUAS')
57 MINIMUM_GTK_VERSION = '2.24.0'
58 MINIMUM_GTK3_VERSION = '3.0.0'
59 MINIMUM_GLIB_VERSION = '2.28.0'
61 GEANY_LIB_VERSION = '0.0.0'
63 top = '.'
64 out = '_build_'
66 mio_sources = set(['tagmanager/mio/mio.c'])
68 ctags_sources = set([
69     'tagmanager/ctags/abaqus.c',
70     'tagmanager/ctags/args.c',
71     'tagmanager/ctags/abc.c',
72     'tagmanager/ctags/actionscript.c',
73     'tagmanager/ctags/asciidoc.c',
74     'tagmanager/ctags/asm.c',
75     'tagmanager/ctags/basic.c',
76     'tagmanager/ctags/c.c',
77     'tagmanager/ctags/cobol.c',
78     'tagmanager/ctags/conf.c',
79     'tagmanager/ctags/css.c',
80     'tagmanager/ctags/ctags.c',
81     'tagmanager/ctags/diff.c',
82     'tagmanager/ctags/docbook.c',
83     'tagmanager/ctags/erlang.c',
84     'tagmanager/ctags/entry.c',
85     'tagmanager/ctags/fortran.c',
86     'tagmanager/ctags/get.c',
87     'tagmanager/ctags/go.c',
88     'tagmanager/ctags/haskell.c',
89     'tagmanager/ctags/haxe.c',
90     'tagmanager/ctags/html.c',
91     'tagmanager/ctags/js.c',
92     'tagmanager/ctags/json.c',
93     'tagmanager/ctags/keyword.c',
94     'tagmanager/ctags/latex.c',
95     'tagmanager/ctags/lregex.c',
96     'tagmanager/ctags/lua.c',
97     'tagmanager/ctags/make.c',
98     'tagmanager/ctags/markdown.c',
99     'tagmanager/ctags/matlab.c',
100     'tagmanager/ctags/nsis.c',
101     'tagmanager/ctags/nestlevel.c',
102     'tagmanager/ctags/objc.c',
103     'tagmanager/ctags/options.c',
104     'tagmanager/ctags/parse.c',
105     'tagmanager/ctags/pascal.c',
106     'tagmanager/ctags/r.c',
107     'tagmanager/ctags/perl.c',
108     'tagmanager/ctags/php.c',
109     'tagmanager/ctags/powershell.c',
110     'tagmanager/ctags/python.c',
111     'tagmanager/ctags/read.c',
112     'tagmanager/ctags/rest.c',
113     'tagmanager/ctags/ruby.c',
114     'tagmanager/ctags/rust.c',
115     'tagmanager/ctags/sh.c',
116     'tagmanager/ctags/sort.c',
117     'tagmanager/ctags/sql.c',
118     'tagmanager/ctags/strlist.c',
119     'tagmanager/ctags/txt2tags.c',
120     'tagmanager/ctags/tcl.c',
121     'tagmanager/ctags/vhdl.c',
122     'tagmanager/ctags/verilog.c',
123     'tagmanager/ctags/vstring.c'])
125 tagmanager_sources = set([
126     'tagmanager/src/tm_source_file.c',
127     'tagmanager/src/tm_tag.c',
128     'tagmanager/src/tm_workspace.c'])
130 scintilla_sources = set(['scintilla/gtk/scintilla-marshal.c'])
132 geany_sources = set([
133     'src/about.c', 'src/build.c', 'src/callbacks.c', 'src/dialogs.c', 'src/document.c',
134     'src/editor.c', 'src/encodings.c', 'src/filetypes.c', 'src/geanyentryaction.c',
135     'src/geanymenubuttonaction.c', 'src/geanyobject.c', 'src/geanywraplabel.c',
136     'src/highlighting.c', 'src/keybindings.c',
137     'src/keyfile.c', 'src/log.c', 'src/libmain.c', 'src/msgwindow.c', 'src/navqueue.c', 'src/notebook.c', 'src/osx.c',
138     'src/plugins.c', 'src/pluginutils.c', 'src/prefix.c', 'src/prefs.c', 'src/printing.c', 'src/project.c',
139     'src/sciwrappers.c', 'src/search.c', 'src/socket.c', 'src/spawn.c', 'src/stash.c',
140     'src/symbols.c',
141     'src/templates.c', 'src/toolbar.c', 'src/tools.c', 'src/sidebar.c',
142     'src/ui_utils.c', 'src/utils.c'])
144 geany_bin_sources = set(['src/main.c'])
146 geany_icons = {
147     'hicolor/16x16/apps':       ['16x16/classviewer-class.png',
148                                  '16x16/classviewer-macro.png',
149                                  '16x16/classviewer-member.png',
150                                  '16x16/classviewer-method.png',
151                                  '16x16/classviewer-namespace.png',
152                                  '16x16/classviewer-other.png',
153                                  '16x16/classviewer-struct.png',
154                                  '16x16/classviewer-var.png',
155                                  '16x16/geany.png'],
156     'hicolor/16x16/actions':    ['16x16/geany-build.png',
157                                  '16x16/geany-close-all.png',
158                                  '16x16/geany-save-all.png'],
159     'hicolor/24x24/actions':    ['24x24/geany-build.png',
160                                  '24x24/geany-close-all.png',
161                                  '24x24/geany-save-all.png'],
162     'hicolor/32x32/actions':    ['32x32/geany-build.png',
163                                  '32x32/geany-close-all.png',
164                                  '32x32/geany-save-all.png'],
165     'hicolor/32x32/apps':       ['32x32/geany.png'],
166     'hicolor/48x48/actions':    ['48x48/geany-build.png',
167                                  '48x48/geany-close-all.png',
168                                  '48x48/geany-save-all.png'],
169     'hicolor/48x48/apps':       ['48x48/geany.png'],
170     'hicolor/scalable/apps':    ['scalable/geany.svg'],
171     'hicolor/scalable/actions': ['scalable/geany-build.svg',
172                                  'scalable/geany-close-all.svg',
173                                  'scalable/geany-save-all.svg'],
174     'Tango/16x16/actions':      ['tango/16x16/geany-save-all.png'],
175     'Tango/24x24/actions':      ['tango/24x24/geany-save-all.png'],
176     'Tango/32x32/actions':      ['tango/32x32/geany-save-all.png'],
177     'Tango/48x48/actions':      ['tango/48x48/geany-save-all.png'],
178     'Tango/scalable/actions':   ['tango/scalable/geany-save-all.svg']
180 geany_icons_indexes = {
181     'hicolor':  ['index.theme'],
182     'Tango':    ['tango/index.theme']
186 def configure(conf):
188     conf.check_waf_version(mini='1.6.1')
190     conf.load('compiler_c')
191     _check_c99(conf)
192     is_win32 = _target_is_win32(conf)
194     visibility_hidden_supported = conf.check_cc(cflags=['-Werror', '-fvisibility=hidden'], mandatory=False)
195     conf.check_cc(header_name='fcntl.h', mandatory=False)
196     conf.check_cc(header_name='fnmatch.h', mandatory=False)
197     conf.check_cc(header_name='glob.h', mandatory=False)
198     conf.check_cc(header_name='sys/time.h', mandatory=False)
199     conf.check_cc(header_name='sys/types.h', mandatory=False)
200     conf.check_cc(header_name='sys/stat.h', mandatory=False)
201     conf.define('HAVE_STDLIB_H', 1)  # are there systems without stdlib.h?
202     conf.define('STDC_HEADERS', 1)  # an optimistic guess ;-)
203     _add_to_env_and_define(conf, 'HAVE_REGCOMP', 1)  # needed for CTags
205     conf.check_cc(function_name='fgetpos', header_name='stdio.h', mandatory=False)
206     conf.check_cc(function_name='fnmatch', header_name='fnmatch.h', mandatory=False)
207     conf.check_cc(function_name='ftruncate', header_name='unistd.h', mandatory=False)
208     conf.check_cc(function_name='mkstemp', header_name='stdlib.h', mandatory=False)
209     conf.check_cc(function_name='strstr', header_name='string.h')
211     conf.check_cc(function_name='pow', header_name='math.h', lib='m', uselib_store='M')
213     # check sunOS socket support
214     if Options.platform == 'sunos':
215         conf.check_cc(function_name='socket', lib='socket',
216                       header_name='sys/socket.h', uselib_store='SUNOS_SOCKET', mandatory=True)
218     # check for cxx after the header and function checks have been done to ensure they are
219     # checked with cc not cxx
220     conf.load('compiler_cxx')
221     if is_win32:
222         conf.load('winres')
223     _load_intltool_if_available(conf)
225     # GTK / GIO version check
226     gtk_package_name = 'gtk+-3.0' if conf.options.use_gtk3 else 'gtk+-2.0'
227     minimum_gtk_version = MINIMUM_GTK3_VERSION if conf.options.use_gtk3 else MINIMUM_GTK_VERSION
228     conf.check_cfg(package=gtk_package_name, atleast_version=minimum_gtk_version, uselib_store='GTK',
229         mandatory=True, args='--cflags --libs')
230     conf.check_cfg(package='glib-2.0', atleast_version=MINIMUM_GLIB_VERSION, uselib_store='GLIB',
231         mandatory=True, args='--cflags --libs')
232     conf.check_cfg(package='gmodule-2.0', uselib_store='GMODULE',
233         mandatory=True, args='--cflags --libs')
234     conf.check_cfg(package='gio-2.0', uselib_store='GIO', args='--cflags --libs', mandatory=True)
235     gtk_version = conf.check_cfg(modversion=gtk_package_name, uselib_store='GTK') or 'Unknown'
236     conf.check_cfg(package='gthread-2.0', uselib_store='GTHREAD', args='--cflags --libs')
237     if conf.options.enable_mac_integration:
238         pkgname = 'gtk-mac-integration-gtk3' if conf.options.use_gtk3 else 'gtk-mac-integration-gtk2'
239         conf.check_cfg(package=pkgname, uselib_store='MAC_INTEGRATION',
240             mandatory=True, args='--cflags --libs')
241     # remember GTK version for the build step
242     conf.env['gtk_package_name'] = gtk_package_name
243     conf.env['gtk_version'] = gtk_version
244     conf.env['minimum_gtk_version'] = minimum_gtk_version
245     conf.env['use_gtk3'] = conf.options.use_gtk3
247     revision = _get_git_rev(conf)
249     # rst2html for the HTML manual
250     if not conf.options.no_html_doc and revision is not None:
251         try:
252             conf.env['RST2HTML'] = _find_rst2html(conf)
253         except WafError:
254             error_msg = '''Documentation enabled but rst2html not found.
255 You can explicitly disable building of the HTML manual with --disable-html-docs,
256 but you then may not have a local copy of the HTML manual.'''
257             raise WafError(error_msg)
259     # Windows specials
260     if is_win32:
261         if conf.env['PREFIX'].lower() == tempfile.gettempdir().lower():
262             # overwrite default prefix on Windows (tempfile.gettempdir() is the Waf default)
263             new_prefix = os.path.join(str(conf.root), '%s-%s' % (APPNAME, VERSION))
264             _add_to_env_and_define(conf, 'PREFIX', new_prefix, quote=True)
265             _add_to_env_and_define(conf, 'BINDIR', os.path.join(new_prefix, 'bin'), quote=True)
266         _add_to_env_and_define(conf, 'DOCDIR', os.path.join(conf.env['PREFIX'], 'doc'), quote=True)
267         _add_to_env_and_define(conf, 'LIBDIR', '%s/lib' % conf.env['PREFIX'], quote=True)
268         conf.define('LOCALEDIR', os.path.join('share' 'locale'), quote=True)
269         # overwrite LOCALEDIR to install message catalogues properly
270         conf.env['LOCALEDIR'] = os.path.join(conf.env['PREFIX'], 'share', 'locale')
271         # DATADIR is defined in objidl.h, so we remove it from config.h but keep it in env
272         conf.undefine('DATADIR')
273         conf.env['DATADIR'] = os.path.join(conf.env['PREFIX'], 'data')
274         conf.env.append_value('LINKFLAGS_cprogram', [
275             '-mwindows',
276             '-static-libgcc',
277             '-static-libstdc++'])
278         conf.env.append_value('LIB_WIN32', ['wsock32', 'uuid', 'ole32', 'comdlg32'])
279     else:
280         conf.env['cshlib_PATTERN'] = '%s.so'
281         # DATADIR and LOCALEDIR are defined by the intltool tool
282         # but they are not added to the environment, so we need to
283         _add_define_to_env(conf, 'DATADIR')
284         _add_define_to_env(conf, 'LOCALEDIR')
285         docdir = os.path.join(conf.env['DATADIR'], 'doc', 'geany')
286         libdir = os.path.join(conf.env['PREFIX'], 'lib')
287         mandir = os.path.join(conf.env['DATADIR'], 'man')
288         _define_from_opt(conf, 'DOCDIR', conf.options.docdir, docdir)
289         _define_from_opt(conf, 'LIBDIR', conf.options.libdir, libdir)
290         _define_from_opt(conf, 'MANDIR', conf.options.mandir, mandir)
292     conf.define('ENABLE_NLS', 1)
293     conf.define('GEANY_LOCALEDIR', '' if is_win32 else conf.env['LOCALEDIR'], quote=True)
294     conf.define('GEANY_DATADIR', 'data' if is_win32 else conf.env['DATADIR'], quote=True)
295     conf.define('GEANY_DOCDIR', conf.env['DOCDIR'], quote=True)
296     conf.define('GEANY_LIBDIR', '' if is_win32 else conf.env['LIBDIR'], quote=True)
297     conf.define('GEANY_PREFIX', '' if is_win32 else conf.env['PREFIX'], quote=True)
298     conf.define('PACKAGE', APPNAME, quote=True)
299     conf.define('VERSION', VERSION, quote=True)
300     conf.define('REVISION', revision or '-1', quote=True)
302     conf.define('GETTEXT_PACKAGE', APPNAME, quote=True)
304     # no VTE on Windows
305     if is_win32:
306         conf.options.no_vte = True
308     _define_from_opt(conf, 'HAVE_PLUGINS', not conf.options.no_plugins, None)
309     _define_from_opt(conf, 'HAVE_SOCKET', not conf.options.no_socket, None)
310     _define_from_opt(conf, 'HAVE_VTE', not conf.options.no_vte, None)
312     conf.write_config_header('config.h', remove=False)
314     # GEANY_EXPORT_SYMBOL and GEANY_API_SYMBOL
315     if is_win32:
316         geanyexport_cflags = []
317         geanyexport_defines = ['GEANY_EXPORT_SYMBOL=__declspec(dllexport)']
318     elif visibility_hidden_supported:
319         geanyexport_cflags = ['-fvisibility=hidden']
320         geanyexport_defines = ['GEANY_EXPORT_SYMBOL=__attribute__((visibility("default")))']
321     else:  # unknown, define to nothing
322         geanyexport_cflags = []
323         geanyexport_defines = ['GEANY_EXPORT_SYMBOL=']
324     geanyexport_defines.append('GEANY_API_SYMBOL=GEANY_EXPORT_SYMBOL')
325     conf.env['DEFINES_geanyexport'] = geanyexport_defines
326     conf.env['CFLAGS_geanyexport'] = geanyexport_cflags
327     conf.env['CXXFLAGS_geanyexport'] = geanyexport_cflags
329     # some more compiler flags
330     conf.env.append_value('CFLAGS', ['-DHAVE_CONFIG_H'])
331     if conf.env['CC_NAME'] == 'gcc' and '-O' not in ''.join(conf.env['CFLAGS']):
332         conf.env.append_value('CFLAGS', ['-O2'])
333     if revision is not None:
334         conf.env.append_value('CFLAGS', ['-g', '-DGEANY_DEBUG'])
335     # Scintilla flags
336     conf.env.append_value('CFLAGS', ['-DGTK'])
337     conf.env.append_value('CXXFLAGS',
338         ['-DNDEBUG', '-DGTK', '-DSCI_LEXER', '-DG_THREADS_IMPL_NONE'])
339     if conf.env['CXX_NAME'] == 'gcc' and '-O' not in ''.join(conf.env['CXXFLAGS']):
340         conf.env.append_value('CXXFLAGS', ['-O2'])
341     if revision is not None:
342         conf.env.append_value('CXXFLAGS', ['-g'])
344     # summary
345     Logs.pprint('BLUE', 'Summary:')
346     conf.msg('Install Geany ' + VERSION + ' in', conf.env['PREFIX'])
347     conf.msg('Using GTK version', gtk_version)
348     conf.msg('Build with plugin support', conf.options.no_plugins and 'no' or 'yes')
349     conf.msg('Use virtual terminal support', conf.options.no_vte and 'no' or 'yes')
350     if revision is not None:
351         conf.msg('Compiling Git revision', revision)
352     # deprecation warning
353     _show_deprecation_warning(conf)
356 def _show_deprecation_warning(ctx):
357     Logs.pprint(
358         'RED',
359         'The Waf build system is deprecated and will be removed in the removed in Geany 1.27. '
360         'Please use the Autotools build system.')
363 def options(opt):
364     # Disable MSVC detection on win32: building Geany with MSVC is currently not supported
365     # If anyone wants to add support for building with MSVC, this hack should be removed.
366     c_compiler['win32'] = ['gcc']
367     cxx_compiler['win32'] = ['g++']
369     opt.load('compiler_cc')
370     opt.load('compiler_cxx')
371     opt.load('intltool')
373     # Option
374     opt.add_option('--no-scm', action='store_true', default=False,
375         help='Disable SCM detection [default: No]', dest='no_scm')
376     # Features
377     opt.add_option('--disable-plugins', action='store_true', default=False,
378         help='compile without plugin support [default: No]', dest='no_plugins')
379     opt.add_option('--disable-socket', action='store_true', default=False,
380         help='compile without support to detect a running instance [[default: No]',
381         dest='no_socket')
382     opt.add_option('--disable-vte', action='store_true', default=False,
383         help='compile without support for an embedded virtual terminal [[default: No]',
384         dest='no_vte')
385     opt.add_option('--enable-gtk3', action='store_true', default=False,
386         help='compile with GTK3 support (experimental) [[default: No]',
387         dest='use_gtk3')
388     opt.add_option('--enable-mac-integration', action='store_true', default=False,
389         help='use gtk-mac-integration to enable improved OS X integration [[default: No]',
390         dest='enable_mac_integration')
391     opt.add_option('--disable-html-docs', action='store_true', default=False,
392         help='do not generate HTML documentation using rst2html [[default: No]',
393         dest='no_html_doc')
394     # Paths
395     opt.add_option('--mandir', type='string', default='',
396         help='man documentation', dest='mandir')
397     opt.add_option('--docdir', type='string', default='',
398         help='documentation root', dest='docdir')
399     opt.add_option('--libdir', type='string', default='',
400         help='object code libraries', dest='libdir')
403 def build(bld):
404     is_win32 = _target_is_win32(bld)
405     bld.add_post_fun(_show_deprecation_warning)
407     if bld.cmd == 'clean':
408         _remove_linguas_file()
409     if bld.cmd in ('install', 'uninstall'):
410         bld.add_post_fun(_post_install)
412     def build_plugin(plugin_name, install=True, uselib_add=[]):
413         if install:
414             instpath = '${LIBDIR}/geany'
415         else:
416             instpath = None
418         bld(
419             features                = ['c', 'cshlib'],
420             source                  = 'plugins/%s.c' % plugin_name,
421             includes                = ['.', 'src/', 'scintilla/include', 'tagmanager/src'],
422             defines                 = 'G_LOG_DOMAIN="%s"' % plugin_name,
423             target                  = plugin_name,
424             uselib                  = ['GTK', 'GLIB', 'GMODULE', 'C99'] + uselib_add,
425             use                     = ['geany'],
426             install_path            = instpath)
428     # CTags
429     bld.objects(
430         features        = ['c'],
431         source          = ctags_sources,
432         name            = 'ctags',
433         target          = 'ctags',
434         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
435         defines         = 'G_LOG_DOMAIN="CTags"',
436         uselib          = ['cshlib', 'GLIB', 'geanyexport', 'C99'])
438     # Tagmanager
439     bld.objects(
440         features        = ['c'],
441         source          = tagmanager_sources,
442         name            = 'tagmanager',
443         target          = 'tagmanager',
444         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
445         defines         = ['GEANY_PRIVATE', 'G_LOG_DOMAIN="Tagmanager"'],
446         uselib          = ['cshlib', 'GTK', 'GLIB', 'geanyexport', 'C99'])
448     # MIO
449     bld.objects(
450         features        = ['c'],
451         source          = mio_sources,
452         name            = 'mio',
453         target          = 'mio',
454         includes        = ['.', 'tagmanager/mio/'],
455         defines         = 'G_LOG_DOMAIN="MIO"',
456         uselib          = ['cshlib', 'GTK', 'GLIB', 'geanyexport'])
458     # Scintilla
459     files = bld.srcnode.ant_glob('scintilla/**/*.cxx', src=True, dir=False)
460     scintilla_sources.update([file.path_from(bld.srcnode) for file in files])
461     bld.objects(
462         features        = ['c', 'cxx'],
463         name            = 'scintilla',
464         target          = 'scintilla',
465         source          = scintilla_sources,
466         includes        = ['.', 'scintilla/include', 'scintilla/src', 'scintilla/lexlib'],
467         uselib          = ['cshlib', 'cxxshlib', 'GTK', 'GLIB', 'GMODULE', 'M', 'geanyexport'])
469     # Geany
470     if bld.env['HAVE_VTE'] == 1:
471         geany_sources.add('src/vte.c')
472     if is_win32:
473         geany_sources.add('src/win32.c')
474         geany_bin_sources.add('geany_private.rc')
476     def gen_signallist(task):
477         from xml.etree import ElementTree
479         def find_handlers(xml_filename):
480             tree = ElementTree.parse(xml_filename)
481             signals = tree.getroot().findall(".//signal")
482             return [sig.attrib["handler"] for sig in signals]
484         handlers = []
485         for node in task.inputs:
486             handlers += find_handlers(node.abspath())
487         handlers = sorted(set(handlers))
489         for node in task.outputs:
490             node.write("/* This file is auto-generated, do not edit. */\n" +
491                        ''.join(["ITEM(%s)\n" % h for h in handlers]))
493     # signallist.i
494     bld(
495         source  = 'data/geany.glade',
496         target  = bld.path.get_bld().make_node('src/signallist.i'),
497         name    = 'signallist.i',
498         rule    = gen_signallist)
500     base_uselibs = ['GTK', 'GLIB', 'GMODULE', 'GIO', 'GTHREAD', 'WIN32', 'MAC_INTEGRATION', 'SUNOS_SOCKET', 'M', 'C99']
502     # libgeany
503     bld.shlib(
504         features        = ['c', 'cxx'],
505         name            = 'geany',
506         target          = 'geany',
507         source          = geany_sources,
508         includes        = ['.', 'scintilla/include', 'tagmanager/src', 'src'],
509         defines         = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
510         uselib          = base_uselibs + ['geanyexport'],
511         use             = ['scintilla', 'ctags', 'tagmanager', 'mio'],
512         linkflags       = bld.env['LINKFLAGS_cprogram'],
513         vnum            = GEANY_LIB_VERSION,
514         install_path    = '${PREFIX}/bin' if is_win32 else '${LIBDIR}')
516     # geany executable
517     t = bld.program(
518         features        = ['c', 'cxx'],
519         name            = 'geany_bin',
520         target          = 'geany',
521         source          = geany_bin_sources,
522         includes        = ['.', 'scintilla/include', 'tagmanager/src'],
523         defines         = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
524         uselib          = base_uselibs + ['geanyexport'],
525         use             = ['geany'])
526     if not is_win32:
527         # http://www.freehackers.org/~tnagy/testdoc/single.html#common_c
528         t.rpath = bld.env['LIBDIR']
530     # Plugins
531     if bld.env['HAVE_PLUGINS'] == 1:
532         build_plugin('classbuilder')
533         build_plugin('demoplugin', False)
534         build_plugin('export', uselib_add=['M'])
535         build_plugin('filebrowser')
536         build_plugin('htmlchars')
537         build_plugin('saveactions')
538         build_plugin('splitwindow')
540     # Translations
541     if bld.env['INTLTOOL']:
542         bld(
543             features        = ['linguas', 'intltool_po'],
544             podir           = 'po',
545             install_path    = '${LOCALEDIR}',
546             appname         = 'geany')
548     # HTML documentation (build if it is not part of the tree already, as it is required for install)
549     html_doc_filename = os.path.join(bld.out_dir, 'doc', 'geany.html')
550     if bld.env['RST2HTML']:
551         rst2html = bld.env['RST2HTML']
552         bld(
553             source  = ['doc/geany.txt'],
554             deps    = ['doc/geany.css'],
555             target  = bld.path.get_bld().make_node('doc/geany.html'),
556             name    = 'geany.html',
557             cwd     = os.path.join(bld.path.abspath(), 'doc'),
558             rule    = '%s  -stg --stylesheet=geany.css geany.txt %s' % (rst2html, html_doc_filename))
560     # geany.pc
561     if is_win32:
562         # replace backward slashes by forward slashes as they could be interepreted as escape
563         # characters
564         geany_pc_prefix = bld.env['PREFIX'].replace('\\', '/')
565     else:
566         geany_pc_prefix = bld.env['PREFIX']
567     bld(
568         source          = 'geany.pc.in',
569         dct             = {'VERSION': VERSION,
570                            'DEPENDENCIES': '%s >= %s glib-2.0 >= %s' % \
571                                 (bld.env['gtk_package_name'],
572                                  bld.env['minimum_gtk_version'],
573                                  MINIMUM_GLIB_VERSION),
574                            'prefix': geany_pc_prefix,
575                            'exec_prefix': '${prefix}',
576                            'libdir': '${exec_prefix}/lib',
577                            'includedir': '${prefix}/include',
578                            'datarootdir': '${prefix}/share',
579                            'datadir': '${datarootdir}',
580                            'localedir': '${datarootdir}/locale'})
582     # geany.nsi
583     bld(
584         features        = 'subst',
585         source          = 'geany.nsi.in',
586         target          = 'geany.nsi',
587         dct             = {'VERSION': VERSION,
588                            'GTK_VERSION': bld.env['gtk_version']},
589         install_path    = None)
591     if not is_win32:
592         # geany.desktop
593         if bld.env['INTLTOOL']:
594             bld(
595                 features        = 'intltool_in',
596                 source          = 'geany.desktop.in',
597                 flags           = ['-d', '-q', '-u', '-c'],
598                 install_path    = '${DATADIR}/applications')
600         # geany.1
601         bld(
602             features        = 'subst',
603             source          = 'doc/geany.1.in',
604             target          = 'geany.1',
605             dct             = {'VERSION': VERSION,
606                                 'GEANY_DATA_DIR': bld.env['DATADIR'] + '/geany'},
607             install_path    = '${MANDIR}/man1')
609         # geany.spec
610         bld(
611             features        = 'subst',
612             source          = 'geany.spec.in',
613             target          = 'geany.spec',
614             install_path    = None,
615             dct             = {'VERSION': VERSION})
617         # Doxyfile
618         bld(
619             features        = 'subst',
620             source          = 'doc/Doxyfile.in',
621             target          = 'doc/Doxyfile',
622             install_path    = None,
623             dct             = {'VERSION': VERSION,
624                                'top_builddir': bld.out_dir,
625                                'top_srcdir': bld.top_dir,})
627     # disable build/install phase interleaving
628     bld.add_group()
630     ###
631     # Install files
632     ###
633     # Headers
634     bld.install_files('${PREFIX}/include/geany', '''
635         src/app.h
636         src/build.h
637         src/dialogs.h
638         src/document.h
639         src/editor.h
640         src/encodings.h
641         src/filetypes.h
642         src/geany.h
643         src/highlighting.h
644         src/keybindings.h
645         src/main.h
646         src/msgwindow.h
647         src/navqueue.h
648         src/plugindata.h
649         src/pluginutils.h
650         src/prefs.h
651         src/project.h
652         src/sciwrappers.h
653         src/search.h
654         src/spawn.h
655         src/stash.h
656         src/support.h
657         src/symbols.h
658         src/templates.h
659         src/toolbar.h
660         src/ui_utils.h
661         src/utils.h
662         src/gtkcompat.h
663         plugins/geanyplugin.h
664         plugins/geanyfunctions.h
665         ''')
666     bld.install_files('${PREFIX}/include/geany/scintilla', '''
667         scintilla/include/SciLexer.h scintilla/include/Scintilla.h
668         scintilla/include/Scintilla.iface scintilla/include/ScintillaWidget.h
669         scintilla/include/Sci_Position.h ''')
670     bld.install_files('${PREFIX}/include/geany/tagmanager', '''
671         tagmanager/src/tm_source_file.h
672         tagmanager/src/tm_tag.h
673         tagmanager/src/tm_tagmanager.h
674         tagmanager/src/tm_workspace.h ''')
675     # Docs
676     base_dir = '${PREFIX}' if is_win32 else '${DOCDIR}'
677     ext = '.txt' if is_win32 else ''
678     for filename in 'AUTHORS ChangeLog COPYING README NEWS THANKS TODO'.split():
679         basename = _uc_first(filename, bld)
680         destination_filename = '%s%s' % (basename, ext)
681         destination = os.path.join(base_dir, destination_filename)
682         bld.install_as(destination, filename)
684     # install HTML documentation only if it exists, i.e. it was built before
685     # local_html_doc_filename supports installing HTML doc from in-tree geany.html if it exists
686     local_html_doc_filename = os.path.join(bld.path.abspath(), 'doc', 'geany.html')
687     if os.path.exists(html_doc_filename) or os.path.exists(local_html_doc_filename):
688         html_dir = '' if is_win32 else 'html/'
689         html_name = 'Manual.html' if is_win32 else 'index.html'
690         start_dir = bld.path.find_dir('doc/images')
691         bld.install_files('${DOCDIR}/%simages' % html_dir, start_dir.ant_glob('*.png'), cwd=start_dir)
692         bld.install_as('${DOCDIR}/%s%s' % (html_dir, html_name), 'doc/geany.html')
694     bld.install_as('${DOCDIR}/%s' % _uc_first('manual.txt', bld), 'doc/geany.txt')
695     bld.install_as('${DOCDIR}/ScintillaLicense.txt', 'scintilla/License.txt')
696     if is_win32:
697         bld.install_as('${DOCDIR}/ReadMe.I18n.txt', 'README.I18N')
698         bld.install_as('${DOCDIR}/Hacking.txt', 'HACKING')
699     # Data
700     data_dir = '' if is_win32 else 'geany'
701     start_dir = bld.path.find_dir('data')
702     bld.install_as('${DATADIR}/%s/GPL-2' % data_dir, 'COPYING')
703     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('filetype*'), cwd=start_dir)
704     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('*.tags'), cwd=start_dir)
705     bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.glade')
706     bld.install_files('${DATADIR}/%s' % data_dir, 'data/snippets.conf')
707     bld.install_files('${DATADIR}/%s' % data_dir, 'data/ui_toolbar.xml')
708     if bld.env['use_gtk3']:
709         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.css')
710     else:
711         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.gtkrc')
713     start_dir = bld.path.find_dir('data/colorschemes')
714     template_dest = '${DATADIR}/%s/colorschemes' % data_dir
715     bld.install_files(template_dest, start_dir.ant_glob('*'), cwd=start_dir)
716     start_dir = bld.path.find_dir('data/templates')
717     template_dest = '${DATADIR}/%s/templates' % data_dir
718     bld.install_files(template_dest, start_dir.ant_glob('**/*'), cwd=start_dir, relative_trick=True)
719     # Icons
720     for dest, srcs in geany_icons.items():
721         dest_dir = os.path.join('${PREFIX}/share/icons' if is_win32 else '${DATADIR}/icons', dest)
722         bld.install_files(dest_dir, srcs, cwd=bld.path.find_dir('icons'))
723     # install theme indexes on Windows
724     if is_win32:
725         for dest, srcs in geany_icons_indexes.items():
726             bld.install_files(os.path.join('${PREFIX}/share/icons', dest), srcs, cwd=bld.path.find_dir('icons'))
729 def distclean(ctx):
730     Scripting.distclean(ctx)
731     _remove_linguas_file()
734 def _remove_linguas_file():
735     # remove LINGUAS file as well
736     try:
737         os.unlink(LINGUAS_FILE)
738     except OSError:
739         pass
742 @feature('linguas')
743 @before_method('apply_intltool_po')
744 def write_linguas_file(self):
745     if os.path.exists(LINGUAS_FILE):
746         return
747     linguas = ''
748     if 'LINGUAS' in self.env:
749         files = self.env['LINGUAS']
750         for po_filename in files.split(' '):
751             if os.path.exists('po/%s.po' % po_filename):
752                 linguas += '%s ' % po_filename
753     else:
754         files = os.listdir('%s/po' % self.path.abspath())
755         files.sort()
756         for filename in files:
757             if filename.endswith('.po'):
758                 linguas += '%s ' % filename[:-3]
759     file_h = open(LINGUAS_FILE, 'w')
760     file_h.write('# This file is autogenerated. Do not edit.\n%s\n' % linguas)
761     file_h.close()
764 def _post_install(ctx):
765     is_win32 = _target_is_win32(ctx)
766     if is_win32:
767         return
768     for d in 'hicolor', 'Tango':
769         theme_dir = Utils.subst_vars('${DATADIR}/icons/' + d, ctx.env)
770         icon_cache_updated = False
771         if not ctx.options.destdir:
772             ctx.exec_command('gtk-update-icon-cache -q -f -t %s' % theme_dir)
773             Logs.pprint('GREEN', 'GTK icon cache updated.')
774             icon_cache_updated = True
775         if not icon_cache_updated:
776             Logs.pprint('YELLOW', 'Icon cache not updated. After install, run this:')
777             Logs.pprint('YELLOW', 'gtk-update-icon-cache -q -f -t %s' % theme_dir)
780 def updatepo(ctx):
781     """update the message catalogs for internationalization"""
782     potfile = '%s.pot' % APPNAME
783     os.chdir('%s/po' % top)
784     try:
785         try:
786             old_size = os.stat(potfile).st_size
787         except OSError:
788             old_size = 0
789         ctx.exec_command('intltool-update --pot -g %s' % APPNAME)
790         size_new = os.stat(potfile).st_size
791         if size_new != old_size:
792             Logs.pprint('CYAN', 'Updated POT file.')
793             Logs.pprint('CYAN', 'Updating translations')
794             ret = ctx.exec_command('intltool-update -r -g %s' % APPNAME)
795             if ret != 0:
796                 Logs.pprint('RED', 'Updating translations failed')
797         else:
798             Logs.pprint('CYAN', 'POT file is up to date.')
799     except OSError:
800         Logs.pprint('RED', 'Failed to generate pot file.')
803 def apidoc(ctx):
804     """generate API reference documentation"""
805     ctx = BuildContext()  # create our own context to have ctx.top_dir
806     basedir = ctx.top_dir
807     doxygen = _find_program(ctx, 'doxygen')
808     doxyfile = '%s/doc/Doxyfile' % ctx.out_dir
809     Logs.pprint('CYAN', 'Generating API documentation')
810     ret = ctx.exec_command('%s %s' % (doxygen, doxyfile))
811     if ret != 0:
812         raise WafError('Generating API documentation failed')
815 def hackingdoc(ctx):
816     """generate HACKING documentation"""
817     ctx = BuildContext()  # create our own context to have ctx.top_dir
818     Logs.pprint('CYAN', 'Generating HACKING documentation')
819     cmd = _find_rst2html(ctx)
820     hacking_file = os.path.join(ctx.top_dir, 'HACKING')
821     hacking_html_file = os.path.join(ctx.top_dir, 'doc', 'hacking.html')
822     stylesheet = os.path.join(ctx.top_dir, 'doc', 'geany.css')
823     ret = ctx.exec_command('%s  -stg --stylesheet=%s %s %s' % (
824         cmd, stylesheet, hacking_file, hacking_html_file))
825     if ret != 0:
826         raise WafError('Generating HACKING documentation failed')
829 def _find_program(ctx, cmd, **kw):
830     def noop(*args):
831         pass
833     if ctx is None or not isinstance(ctx, ConfigurationContext):
834         ctx = ConfigurationContext()
835         ctx.to_log = noop
836         ctx.msg = noop
837     return ctx.find_program(cmd, **kw)
840 def _find_rst2html(ctx):
841     cmds = ['rst2html', 'rst2html2']
842     for command in cmds:
843         cmd = _find_program(ctx, command, mandatory=False, exts=',.py')
844         if cmd:
845             break
846     if not cmd:
847         raise WafError(
848             'rst2html.py could not be found. Please install the Python docutils package.')
849     return cmd
852 def _add_define_to_env(conf, key):
853     value = conf.get_define(key)
854     # strip quotes
855     value = value.replace('"', '')
856     conf.env[key] = value
859 def _add_to_env_and_define(conf, key, value, quote=False):
860     conf.define(key, value, quote)
861     conf.env[key] = value
864 def _define_from_opt(conf, define_name, opt_value, default_value, quote=1):
865     value = default_value
866     if opt_value:
867         if isinstance(opt_value, bool):
868             opt_value = 1
869         value = opt_value
871     if value is not None:
872         _add_to_env_and_define(conf, define_name, value, quote)
873     else:
874         conf.undefine(define_name)
877 def _get_git_rev(conf):
878     if conf.options.no_scm:
879         return
881     if not os.path.isdir('.git'):
882         return
884     try:
885         cmd = 'git rev-parse --short --revs-only HEAD'
886         revision = conf.cmd_and_log(cmd).strip()
887     except WafError:
888         return None
889     else:
890         return revision
893 def _load_intltool_if_available(conf):
894     try:
895         conf.load('intltool')
896         if 'LINGUAS' in os.environ:
897             conf.env['LINGUAS'] = os.environ['LINGUAS']
898     except WafError:
899         # on Windows, we don't hard depend on intltool, on all other platforms raise an error
900         if not _target_is_win32(conf):
901             raise
904 def _target_is_win32(ctx):
905     if 'is_win32' in ctx.env:
906         # cached
907         return ctx.env['is_win32']
908     is_win32 = None
909     if sys.platform == 'win32':
910         is_win32 = True
911     if is_win32 is None:
912         if ctx.env and 'CC' in ctx.env:
913             env_cc = ctx.env['CC']
914             if not isinstance(env_cc, str):
915                 env_cc = ''.join(env_cc)
916             is_win32 = (env_cc.find('mingw') != -1)
917     if is_win32 is None:
918         is_win32 = False
919     # cache for future checks
920     ctx.env['is_win32'] = is_win32
921     return is_win32
924 def _uc_first(string, ctx):
925     if _target_is_win32(ctx):
926         return string.title()
927     return string
930 # Copied from Geany-Plugins
931 def _check_c99(conf):
932     # FIXME: improve some checks?
933     # TODO: look at Autoconf's C99 checks?
934     fragment = '''
935     // single-line comments
937     #include <stdbool.h>
939     struct s { int a, b; };
941     // inlines
942     static inline void fun_inline(struct s param) {}
944     int main(void) {
945         _Bool b = false;
947         // variable declaration in for body
948         for (int i = 0; i < 2; i++);
950         // compound literals
951         fun_inline((struct s) { 1, 2 });
953         // mixed declarations and code
954         int mixed = 0;
956         // named initializers
957         struct s name_inited = {
958             .a = 42,
959             .b = 64
960         };
962         return (b || mixed || ! name_inited.a);
963     }
964     '''
966     exc = None
967     # list of flags is stolen from Autoconf 2.69
968     flags = ['', '-std=gnu99', '-std=c99', '-c99', '-AC99',
969              '-D_STDC_C99=', '-qlanglvl=extc99']
970     for flag in flags:
971         try:
972             desc = ['with flag %s' % flag, 'with no flags'][not flag]
973             conf.check_cc(fragment=fragment, uselib_store='C99', cflags=flag,
974                           msg="Checking for C99 support (%s)" % desc)
975             exc = None
976             break
977         except ConfigurationError as e:
978             exc = e
979     if exc:
980         raise exc
981     return True