Add parentheses in an if to make the condition more clear
[geany-mirror.git] / wscript
blob864da741de3cc5ac3689fe07dd62ae6a81c1e4b1
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         tagmanager/src/tm_parser.h ''')
676     # Docs
677     base_dir = '${DOCDIR}'
678     ext = '.txt' if is_win32 else ''
679     for filename in 'AUTHORS ChangeLog COPYING README NEWS THANKS TODO'.split():
680         basename = _uc_first(filename, bld)
681         destination_filename = '%s%s' % (basename, ext)
682         destination = os.path.join(base_dir, destination_filename)
683         bld.install_as(destination, filename)
685     # install HTML documentation only if it exists, i.e. it was built before
686     # local_html_doc_filename supports installing HTML doc from in-tree geany.html if it exists
687     local_html_doc_filename = os.path.join(bld.path.abspath(), 'doc', 'geany.html')
688     if os.path.exists(html_doc_filename) or os.path.exists(local_html_doc_filename):
689         start_dir = bld.path.find_dir('doc/images')
690         bld.install_files('${DOCDIR}/html/images', start_dir.ant_glob('*.png'), cwd=start_dir)
691         bld.install_as('${DOCDIR}/html/index.html', 'doc/geany.html')
693     bld.install_as('${DOCDIR}/%s' % _uc_first('manual.txt', bld), 'doc/geany.txt')
694     bld.install_as('${DOCDIR}/ScintillaLicense.txt', 'scintilla/License.txt')
695     if is_win32:
696         bld.install_as('${DOCDIR}/ReadMe.I18n.txt', 'README.I18N')
697         bld.install_as('${DOCDIR}/Hacking.txt', 'HACKING')
698     # Data
699     data_dir = '' if is_win32 else 'geany'
700     start_dir = bld.path.find_dir('data')
701     bld.install_as('${DATADIR}/%s/GPL-2' % data_dir, 'COPYING')
702     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('filetype*'), cwd=start_dir)
703     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('*.tags'), cwd=start_dir)
704     bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.glade')
705     bld.install_files('${DATADIR}/%s' % data_dir, 'data/snippets.conf')
706     bld.install_files('${DATADIR}/%s' % data_dir, 'data/ui_toolbar.xml')
707     if bld.env['use_gtk3']:
708         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.css')
709     else:
710         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.gtkrc')
712     start_dir = bld.path.find_dir('data/colorschemes')
713     template_dest = '${DATADIR}/%s/colorschemes' % data_dir
714     bld.install_files(template_dest, start_dir.ant_glob('*'), cwd=start_dir)
715     start_dir = bld.path.find_dir('data/templates')
716     template_dest = '${DATADIR}/%s/templates' % data_dir
717     bld.install_files(template_dest, start_dir.ant_glob('**/*'), cwd=start_dir, relative_trick=True)
718     # Icons
719     for dest, srcs in geany_icons.items():
720         dest_dir = os.path.join('${PREFIX}/share/icons' if is_win32 else '${DATADIR}/icons', dest)
721         bld.install_files(dest_dir, srcs, cwd=bld.path.find_dir('icons'))
722     # install theme indexes on Windows
723     if is_win32:
724         for dest, srcs in geany_icons_indexes.items():
725             bld.install_files(os.path.join('${PREFIX}/share/icons', dest), srcs, cwd=bld.path.find_dir('icons'))
728 def distclean(ctx):
729     Scripting.distclean(ctx)
730     _remove_linguas_file()
733 def _remove_linguas_file():
734     # remove LINGUAS file as well
735     try:
736         os.unlink(LINGUAS_FILE)
737     except OSError:
738         pass
741 @feature('linguas')
742 @before_method('apply_intltool_po')
743 def write_linguas_file(self):
744     if os.path.exists(LINGUAS_FILE):
745         return
746     linguas = ''
747     if 'LINGUAS' in self.env:
748         files = self.env['LINGUAS']
749         for po_filename in files.split(' '):
750             if os.path.exists('po/%s.po' % po_filename):
751                 linguas += '%s ' % po_filename
752     else:
753         files = os.listdir('%s/po' % self.path.abspath())
754         files.sort()
755         for filename in files:
756             if filename.endswith('.po'):
757                 linguas += '%s ' % filename[:-3]
758     file_h = open(LINGUAS_FILE, 'w')
759     file_h.write('# This file is autogenerated. Do not edit.\n%s\n' % linguas)
760     file_h.close()
763 def _post_install(ctx):
764     is_win32 = _target_is_win32(ctx)
765     if is_win32:
766         return
767     for d in 'hicolor', 'Tango':
768         theme_dir = Utils.subst_vars('${DATADIR}/icons/' + d, ctx.env)
769         icon_cache_updated = False
770         if not ctx.options.destdir:
771             ctx.exec_command('gtk-update-icon-cache -q -f -t %s' % theme_dir)
772             Logs.pprint('GREEN', 'GTK icon cache updated.')
773             icon_cache_updated = True
774         if not icon_cache_updated:
775             Logs.pprint('YELLOW', 'Icon cache not updated. After install, run this:')
776             Logs.pprint('YELLOW', 'gtk-update-icon-cache -q -f -t %s' % theme_dir)
779 def updatepo(ctx):
780     """update the message catalogs for internationalization"""
781     potfile = '%s.pot' % APPNAME
782     os.chdir('%s/po' % top)
783     try:
784         try:
785             old_size = os.stat(potfile).st_size
786         except OSError:
787             old_size = 0
788         ctx.exec_command('intltool-update --pot -g %s' % APPNAME)
789         size_new = os.stat(potfile).st_size
790         if size_new != old_size:
791             Logs.pprint('CYAN', 'Updated POT file.')
792             Logs.pprint('CYAN', 'Updating translations')
793             ret = ctx.exec_command('intltool-update -r -g %s' % APPNAME)
794             if ret != 0:
795                 Logs.pprint('RED', 'Updating translations failed')
796         else:
797             Logs.pprint('CYAN', 'POT file is up to date.')
798     except OSError:
799         Logs.pprint('RED', 'Failed to generate pot file.')
802 def apidoc(ctx):
803     """generate API reference documentation"""
804     ctx = BuildContext()  # create our own context to have ctx.top_dir
805     basedir = ctx.top_dir
806     doxygen = _find_program(ctx, 'doxygen')
807     doxyfile = '%s/doc/Doxyfile' % ctx.out_dir
808     Logs.pprint('CYAN', 'Generating API documentation')
809     ret = ctx.exec_command('%s %s' % (doxygen, doxyfile))
810     if ret != 0:
811         raise WafError('Generating API documentation failed')
814 def hackingdoc(ctx):
815     """generate HACKING documentation"""
816     ctx = BuildContext()  # create our own context to have ctx.top_dir
817     Logs.pprint('CYAN', 'Generating HACKING documentation')
818     cmd = _find_rst2html(ctx)
819     hacking_file = os.path.join(ctx.top_dir, 'HACKING')
820     hacking_html_file = os.path.join(ctx.top_dir, 'doc', 'hacking.html')
821     stylesheet = os.path.join(ctx.top_dir, 'doc', 'geany.css')
822     ret = ctx.exec_command('%s  -stg --stylesheet=%s %s %s' % (
823         cmd, stylesheet, hacking_file, hacking_html_file))
824     if ret != 0:
825         raise WafError('Generating HACKING documentation failed')
828 def _find_program(ctx, cmd, **kw):
829     def noop(*args):
830         pass
832     if ctx is None or not isinstance(ctx, ConfigurationContext):
833         ctx = ConfigurationContext()
834         ctx.to_log = noop
835         ctx.msg = noop
836     return ctx.find_program(cmd, **kw)
839 def _find_rst2html(ctx):
840     cmds = ['rst2html', 'rst2html2']
841     for command in cmds:
842         cmd = _find_program(ctx, command, mandatory=False, exts=',.py')
843         if cmd:
844             break
845     if not cmd:
846         raise WafError(
847             'rst2html.py could not be found. Please install the Python docutils package.')
848     return cmd
851 def _add_define_to_env(conf, key):
852     value = conf.get_define(key)
853     # strip quotes
854     value = value.replace('"', '')
855     conf.env[key] = value
858 def _add_to_env_and_define(conf, key, value, quote=False):
859     conf.define(key, value, quote)
860     conf.env[key] = value
863 def _define_from_opt(conf, define_name, opt_value, default_value, quote=1):
864     value = default_value
865     if opt_value:
866         if isinstance(opt_value, bool):
867             opt_value = 1
868         value = opt_value
870     if value is not None:
871         _add_to_env_and_define(conf, define_name, value, quote)
872     else:
873         conf.undefine(define_name)
876 def _get_git_rev(conf):
877     if conf.options.no_scm:
878         return
880     if not os.path.isdir('.git'):
881         return
883     try:
884         cmd = 'git rev-parse --short --revs-only HEAD'
885         revision = conf.cmd_and_log(cmd).strip()
886     except WafError:
887         return None
888     else:
889         return revision
892 def _load_intltool_if_available(conf):
893     try:
894         conf.load('intltool')
895         if 'LINGUAS' in os.environ:
896             conf.env['LINGUAS'] = os.environ['LINGUAS']
897     except WafError:
898         # on Windows, we don't hard depend on intltool, on all other platforms raise an error
899         if not _target_is_win32(conf):
900             raise
903 def _target_is_win32(ctx):
904     if 'is_win32' in ctx.env:
905         # cached
906         return ctx.env['is_win32']
907     is_win32 = None
908     if sys.platform == 'win32':
909         is_win32 = True
910     if is_win32 is None:
911         if ctx.env and 'CC' in ctx.env:
912             env_cc = ctx.env['CC']
913             if not isinstance(env_cc, str):
914                 env_cc = ''.join(env_cc)
915             is_win32 = (env_cc.find('mingw') != -1)
916     if is_win32 is None:
917         is_win32 = False
918     # cache for future checks
919     ctx.env['is_win32'] = is_win32
920     return is_win32
923 def _uc_first(string, ctx):
924     if _target_is_win32(ctx):
925         return string.title()
926     return string
929 # Copied from Geany-Plugins
930 def _check_c99(conf):
931     # FIXME: improve some checks?
932     # TODO: look at Autoconf's C99 checks?
933     fragment = '''
934     // single-line comments
936     #include <stdbool.h>
938     struct s { int a, b; };
940     // inlines
941     static inline void fun_inline(struct s param) {}
943     int main(void) {
944         _Bool b = false;
946         // variable declaration in for body
947         for (int i = 0; i < 2; i++);
949         // compound literals
950         fun_inline((struct s) { 1, 2 });
952         // mixed declarations and code
953         int mixed = 0;
955         // named initializers
956         struct s name_inited = {
957             .a = 42,
958             .b = 64
959         };
961         return (b || mixed || ! name_inited.a);
962     }
963     '''
965     exc = None
966     # list of flags is stolen from Autoconf 2.69
967     flags = ['', '-std=gnu99', '-std=c99', '-c99', '-AC99',
968              '-D_STDC_C99=', '-qlanglvl=extc99']
969     for flag in flags:
970         try:
971             desc = ['with flag %s' % flag, 'with no flags'][not flag]
972             conf.check_cc(fragment=fragment, uselib_store='C99', cflags=flag,
973                           msg="Checking for C99 support (%s)" % desc)
974             exc = None
975             break
976         except ConfigurationError as e:
977             exc = e
978     if exc:
979         raise exc
980     return True