Merge pull request #733 from peter-scholtens/master
[geany-mirror.git] / wscript
blob23ac14551b7bd535609f91309bf0567781e377ca
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)
354 def options(opt):
355     # Disable MSVC detection on win32: building Geany with MSVC is currently not supported
356     # If anyone wants to add support for building with MSVC, this hack should be removed.
357     c_compiler['win32'] = ['gcc']
358     cxx_compiler['win32'] = ['g++']
360     opt.load('compiler_cc')
361     opt.load('compiler_cxx')
362     opt.load('intltool')
364     # Option
365     opt.add_option('--no-scm', action='store_true', default=False,
366         help='Disable SCM detection [default: No]', dest='no_scm')
367     # Features
368     opt.add_option('--disable-plugins', action='store_true', default=False,
369         help='compile without plugin support [default: No]', dest='no_plugins')
370     opt.add_option('--disable-socket', action='store_true', default=False,
371         help='compile without support to detect a running instance [[default: No]',
372         dest='no_socket')
373     opt.add_option('--disable-vte', action='store_true', default=False,
374         help='compile without support for an embedded virtual terminal [[default: No]',
375         dest='no_vte')
376     opt.add_option('--enable-gtk3', action='store_true', default=False,
377         help='compile with GTK3 support (experimental) [[default: No]',
378         dest='use_gtk3')
379     opt.add_option('--enable-mac-integration', action='store_true', default=False,
380         help='use gtk-mac-integration to enable improved OS X integration [[default: No]',
381         dest='enable_mac_integration')
382     opt.add_option('--disable-html-docs', action='store_true', default=False,
383         help='do not generate HTML documentation using rst2html [[default: No]',
384         dest='no_html_doc')
385     # Paths
386     opt.add_option('--mandir', type='string', default='',
387         help='man documentation', dest='mandir')
388     opt.add_option('--docdir', type='string', default='',
389         help='documentation root', dest='docdir')
390     opt.add_option('--libdir', type='string', default='',
391         help='object code libraries', dest='libdir')
394 def build(bld):
395     is_win32 = _target_is_win32(bld)
397     if bld.cmd == 'clean':
398         _remove_linguas_file()
399     if bld.cmd in ('install', 'uninstall'):
400         bld.add_post_fun(_post_install)
402     def build_plugin(plugin_name, install=True, uselib_add=[]):
403         if install:
404             instpath = '${LIBDIR}/geany'
405         else:
406             instpath = None
408         bld(
409             features                = ['c', 'cshlib'],
410             source                  = 'plugins/%s.c' % plugin_name,
411             includes                = ['.', 'src/', 'scintilla/include', 'tagmanager/src'],
412             defines                 = 'G_LOG_DOMAIN="%s"' % plugin_name,
413             target                  = plugin_name,
414             uselib                  = ['GTK', 'GLIB', 'GMODULE', 'C99'] + uselib_add,
415             use                     = ['geany'],
416             install_path            = instpath)
418     # CTags
419     bld.objects(
420         features        = ['c'],
421         source          = ctags_sources,
422         name            = 'ctags',
423         target          = 'ctags',
424         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
425         defines         = 'G_LOG_DOMAIN="CTags"',
426         uselib          = ['cshlib', 'GLIB', 'geanyexport', 'C99'])
428     # Tagmanager
429     bld.objects(
430         features        = ['c'],
431         source          = tagmanager_sources,
432         name            = 'tagmanager',
433         target          = 'tagmanager',
434         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
435         defines         = ['GEANY_PRIVATE', 'G_LOG_DOMAIN="Tagmanager"'],
436         uselib          = ['cshlib', 'GTK', 'GLIB', 'geanyexport', 'C99'])
438     # MIO
439     bld.objects(
440         features        = ['c'],
441         source          = mio_sources,
442         name            = 'mio',
443         target          = 'mio',
444         includes        = ['.', 'tagmanager/mio/'],
445         defines         = 'G_LOG_DOMAIN="MIO"',
446         uselib          = ['cshlib', 'GTK', 'GLIB', 'geanyexport'])
448     # Scintilla
449     files = bld.srcnode.ant_glob('scintilla/**/*.cxx', src=True, dir=False)
450     scintilla_sources.update([file.path_from(bld.srcnode) for file in files])
451     bld.objects(
452         features        = ['c', 'cxx'],
453         name            = 'scintilla',
454         target          = 'scintilla',
455         source          = scintilla_sources,
456         includes        = ['.', 'scintilla/include', 'scintilla/src', 'scintilla/lexlib'],
457         uselib          = ['cshlib', 'cxxshlib', 'GTK', 'GLIB', 'GMODULE', 'M', 'geanyexport'])
459     # Geany
460     if bld.env['HAVE_VTE'] == 1:
461         geany_sources.add('src/vte.c')
462     if is_win32:
463         geany_sources.add('src/win32.c')
464         geany_bin_sources.add('geany_private.rc')
466     def gen_signallist(task):
467         from xml.etree import ElementTree
469         def find_handlers(xml_filename):
470             tree = ElementTree.parse(xml_filename)
471             signals = tree.getroot().findall(".//signal")
472             return [sig.attrib["handler"] for sig in signals]
474         handlers = []
475         for node in task.inputs:
476             handlers += find_handlers(node.abspath())
477         handlers = sorted(set(handlers))
479         for node in task.outputs:
480             node.write("/* This file is auto-generated, do not edit. */\n" +
481                        ''.join(["ITEM(%s)\n" % h for h in handlers]))
483     # signallist.i
484     bld(
485         source  = 'data/geany.glade',
486         target  = bld.path.get_bld().make_node('src/signallist.i'),
487         name    = 'signallist.i',
488         rule    = gen_signallist)
490     base_uselibs = ['GTK', 'GLIB', 'GMODULE', 'GIO', 'GTHREAD', 'WIN32', 'MAC_INTEGRATION', 'SUNOS_SOCKET', 'M', 'C99']
492     # libgeany
493     bld.shlib(
494         features        = ['c', 'cxx'],
495         name            = 'geany',
496         target          = 'geany',
497         source          = geany_sources,
498         includes        = ['.', 'scintilla/include', 'tagmanager/src', 'src'],
499         defines         = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
500         uselib          = base_uselibs + ['geanyexport'],
501         use             = ['scintilla', 'ctags', 'tagmanager', 'mio'],
502         linkflags       = bld.env['LINKFLAGS_cprogram'],
503         vnum            = GEANY_LIB_VERSION,
504         install_path    = '${PREFIX}/bin' if is_win32 else '${LIBDIR}')
506     # geany executable
507     t = bld.program(
508         features        = ['c', 'cxx'],
509         name            = 'geany_bin',
510         target          = 'geany',
511         source          = geany_bin_sources,
512         includes        = ['.', 'scintilla/include', 'tagmanager/src'],
513         defines         = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
514         uselib          = base_uselibs + ['geanyexport'],
515         use             = ['geany'])
516     if not is_win32:
517         # http://www.freehackers.org/~tnagy/testdoc/single.html#common_c
518         t.rpath = bld.env['LIBDIR']
520     # Plugins
521     if bld.env['HAVE_PLUGINS'] == 1:
522         build_plugin('classbuilder')
523         build_plugin('demoplugin', False)
524         build_plugin('export', uselib_add=['M'])
525         build_plugin('filebrowser')
526         build_plugin('htmlchars')
527         build_plugin('saveactions')
528         build_plugin('splitwindow')
530     # Translations
531     if bld.env['INTLTOOL']:
532         bld(
533             features        = ['linguas', 'intltool_po'],
534             podir           = 'po',
535             install_path    = '${LOCALEDIR}',
536             appname         = 'geany')
538     # HTML documentation (build if it is not part of the tree already, as it is required for install)
539     html_doc_filename = os.path.join(bld.out_dir, 'doc', 'geany.html')
540     if bld.env['RST2HTML']:
541         rst2html = bld.env['RST2HTML']
542         bld(
543             source  = ['doc/geany.txt'],
544             deps    = ['doc/geany.css'],
545             target  = bld.path.get_bld().make_node('doc/geany.html'),
546             name    = 'geany.html',
547             cwd     = os.path.join(bld.path.abspath(), 'doc'),
548             rule    = '%s  -stg --stylesheet=geany.css geany.txt %s' % (rst2html, html_doc_filename))
550     # geany.pc
551     if is_win32:
552         # replace backward slashes by forward slashes as they could be interepreted as escape
553         # characters
554         geany_pc_prefix = bld.env['PREFIX'].replace('\\', '/')
555     else:
556         geany_pc_prefix = bld.env['PREFIX']
557     bld(
558         source          = 'geany.pc.in',
559         dct             = {'VERSION': VERSION,
560                            'DEPENDENCIES': '%s >= %s glib-2.0 >= %s' % \
561                                 (bld.env['gtk_package_name'],
562                                  bld.env['minimum_gtk_version'],
563                                  MINIMUM_GLIB_VERSION),
564                            'prefix': geany_pc_prefix,
565                            'exec_prefix': '${prefix}',
566                            'libdir': '${exec_prefix}/lib',
567                            'includedir': '${prefix}/include',
568                            'datarootdir': '${prefix}/share',
569                            'datadir': '${datarootdir}',
570                            'localedir': '${datarootdir}/locale'})
572     # geany.nsi
573     bld(
574         features        = 'subst',
575         source          = 'geany.nsi.in',
576         target          = 'geany.nsi',
577         dct             = {'VERSION': VERSION,
578                            'GTK_VERSION': bld.env['gtk_version']},
579         install_path    = None)
581     if not is_win32:
582         # geany.desktop
583         if bld.env['INTLTOOL']:
584             bld(
585                 features        = 'intltool_in',
586                 source          = 'geany.desktop.in',
587                 flags           = ['-d', '-q', '-u', '-c'],
588                 install_path    = '${DATADIR}/applications')
590         # geany.1
591         bld(
592             features        = 'subst',
593             source          = 'doc/geany.1.in',
594             target          = 'geany.1',
595             dct             = {'VERSION': VERSION,
596                                 'GEANY_DATA_DIR': bld.env['DATADIR'] + '/geany'},
597             install_path    = '${MANDIR}/man1')
599         # geany.spec
600         bld(
601             features        = 'subst',
602             source          = 'geany.spec.in',
603             target          = 'geany.spec',
604             install_path    = None,
605             dct             = {'VERSION': VERSION})
607         # Doxyfile
608         bld(
609             features        = 'subst',
610             source          = 'doc/Doxyfile.in',
611             target          = 'doc/Doxyfile',
612             install_path    = None,
613             dct             = {'VERSION': VERSION,
614                                'top_builddir': bld.out_dir,
615                                'top_srcdir': bld.top_dir,})
617     # disable build/install phase interleaving
618     bld.add_group()
620     ###
621     # Install files
622     ###
623     # Headers
624     bld.install_files('${PREFIX}/include/geany', '''
625         src/app.h
626         src/build.h
627         src/dialogs.h
628         src/document.h
629         src/editor.h
630         src/encodings.h
631         src/filetypes.h
632         src/geany.h
633         src/highlighting.h
634         src/keybindings.h
635         src/main.h
636         src/msgwindow.h
637         src/navqueue.h
638         src/plugindata.h
639         src/pluginutils.h
640         src/prefs.h
641         src/project.h
642         src/sciwrappers.h
643         src/search.h
644         src/spawn.h
645         src/stash.h
646         src/support.h
647         src/symbols.h
648         src/templates.h
649         src/toolbar.h
650         src/ui_utils.h
651         src/utils.h
652         src/gtkcompat.h
653         plugins/geanyplugin.h
654         plugins/geanyfunctions.h
655         ''')
656     bld.install_files('${PREFIX}/include/geany/scintilla', '''
657         scintilla/include/SciLexer.h scintilla/include/Scintilla.h
658         scintilla/include/Scintilla.iface scintilla/include/ScintillaWidget.h
659         scintilla/include/Sci_Position.h ''')
660     bld.install_files('${PREFIX}/include/geany/tagmanager', '''
661         tagmanager/src/tm_source_file.h
662         tagmanager/src/tm_tag.h
663         tagmanager/src/tm_tagmanager.h
664         tagmanager/src/tm_workspace.h ''')
665     # Docs
666     base_dir = '${PREFIX}' if is_win32 else '${DOCDIR}'
667     ext = '.txt' if is_win32 else ''
668     for filename in 'AUTHORS ChangeLog COPYING README NEWS THANKS TODO'.split():
669         basename = _uc_first(filename, bld)
670         destination_filename = '%s%s' % (basename, ext)
671         destination = os.path.join(base_dir, destination_filename)
672         bld.install_as(destination, filename)
674     # install HTML documentation only if it exists, i.e. it was built before
675     # local_html_doc_filename supports installing HTML doc from in-tree geany.html if it exists
676     local_html_doc_filename = os.path.join(bld.path.abspath(), 'doc', 'geany.html')
677     if os.path.exists(html_doc_filename) or os.path.exists(local_html_doc_filename):
678         html_dir = '' if is_win32 else 'html/'
679         html_name = 'Manual.html' if is_win32 else 'index.html'
680         start_dir = bld.path.find_dir('doc/images')
681         bld.install_files('${DOCDIR}/%simages' % html_dir, start_dir.ant_glob('*.png'), cwd=start_dir)
682         bld.install_as('${DOCDIR}/%s%s' % (html_dir, html_name), 'doc/geany.html')
684     bld.install_as('${DOCDIR}/%s' % _uc_first('manual.txt', bld), 'doc/geany.txt')
685     bld.install_as('${DOCDIR}/ScintillaLicense.txt', 'scintilla/License.txt')
686     if is_win32:
687         bld.install_as('${DOCDIR}/ReadMe.I18n.txt', 'README.I18N')
688         bld.install_as('${DOCDIR}/Hacking.txt', 'HACKING')
689     # Data
690     data_dir = '' if is_win32 else 'geany'
691     start_dir = bld.path.find_dir('data')
692     bld.install_as('${DATADIR}/%s/GPL-2' % data_dir, 'COPYING')
693     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('filetype*'), cwd=start_dir)
694     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('*.tags'), cwd=start_dir)
695     bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.glade')
696     bld.install_files('${DATADIR}/%s' % data_dir, 'data/snippets.conf')
697     bld.install_files('${DATADIR}/%s' % data_dir, 'data/ui_toolbar.xml')
698     if bld.env['use_gtk3']:
699         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.css')
700     else:
701         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.gtkrc')
703     start_dir = bld.path.find_dir('data/colorschemes')
704     template_dest = '${DATADIR}/%s/colorschemes' % data_dir
705     bld.install_files(template_dest, start_dir.ant_glob('*'), cwd=start_dir)
706     start_dir = bld.path.find_dir('data/templates')
707     template_dest = '${DATADIR}/%s/templates' % data_dir
708     bld.install_files(template_dest, start_dir.ant_glob('**/*'), cwd=start_dir, relative_trick=True)
709     # Icons
710     for dest, srcs in geany_icons.items():
711         dest_dir = os.path.join('${PREFIX}/share/icons' if is_win32 else '${DATADIR}/icons', dest)
712         bld.install_files(dest_dir, srcs, cwd=bld.path.find_dir('icons'))
713     # install theme indexes on Windows
714     if is_win32:
715         for dest, srcs in geany_icons_indexes.items():
716             bld.install_files(os.path.join('${PREFIX}/share/icons', dest), srcs, cwd=bld.path.find_dir('icons'))
719 def distclean(ctx):
720     Scripting.distclean(ctx)
721     _remove_linguas_file()
724 def _remove_linguas_file():
725     # remove LINGUAS file as well
726     try:
727         os.unlink(LINGUAS_FILE)
728     except OSError:
729         pass
732 @feature('linguas')
733 @before_method('apply_intltool_po')
734 def write_linguas_file(self):
735     if os.path.exists(LINGUAS_FILE):
736         return
737     linguas = ''
738     if 'LINGUAS' in self.env:
739         files = self.env['LINGUAS']
740         for po_filename in files.split(' '):
741             if os.path.exists('po/%s.po' % po_filename):
742                 linguas += '%s ' % po_filename
743     else:
744         files = os.listdir('%s/po' % self.path.abspath())
745         files.sort()
746         for filename in files:
747             if filename.endswith('.po'):
748                 linguas += '%s ' % filename[:-3]
749     file_h = open(LINGUAS_FILE, 'w')
750     file_h.write('# This file is autogenerated. Do not edit.\n%s\n' % linguas)
751     file_h.close()
754 def _post_install(ctx):
755     is_win32 = _target_is_win32(ctx)
756     if is_win32:
757         return
758     for d in 'hicolor', 'Tango':
759         theme_dir = Utils.subst_vars('${DATADIR}/icons/' + d, ctx.env)
760         icon_cache_updated = False
761         if not ctx.options.destdir:
762             ctx.exec_command('gtk-update-icon-cache -q -f -t %s' % theme_dir)
763             Logs.pprint('GREEN', 'GTK icon cache updated.')
764             icon_cache_updated = True
765         if not icon_cache_updated:
766             Logs.pprint('YELLOW', 'Icon cache not updated. After install, run this:')
767             Logs.pprint('YELLOW', 'gtk-update-icon-cache -q -f -t %s' % theme_dir)
770 def updatepo(ctx):
771     """update the message catalogs for internationalization"""
772     potfile = '%s.pot' % APPNAME
773     os.chdir('%s/po' % top)
774     try:
775         try:
776             old_size = os.stat(potfile).st_size
777         except OSError:
778             old_size = 0
779         ctx.exec_command('intltool-update --pot -g %s' % APPNAME)
780         size_new = os.stat(potfile).st_size
781         if size_new != old_size:
782             Logs.pprint('CYAN', 'Updated POT file.')
783             Logs.pprint('CYAN', 'Updating translations')
784             ret = ctx.exec_command('intltool-update -r -g %s' % APPNAME)
785             if ret != 0:
786                 Logs.pprint('RED', 'Updating translations failed')
787         else:
788             Logs.pprint('CYAN', 'POT file is up to date.')
789     except OSError:
790         Logs.pprint('RED', 'Failed to generate pot file.')
793 def apidoc(ctx):
794     """generate API reference documentation"""
795     ctx = BuildContext()  # create our own context to have ctx.top_dir
796     basedir = ctx.top_dir
797     doxygen = _find_program(ctx, 'doxygen')
798     doxyfile = '%s/doc/Doxyfile' % ctx.out_dir
799     Logs.pprint('CYAN', 'Generating API documentation')
800     ret = ctx.exec_command('%s %s' % (doxygen, doxyfile))
801     if ret != 0:
802         raise WafError('Generating API documentation failed')
805 def hackingdoc(ctx):
806     """generate HACKING documentation"""
807     ctx = BuildContext()  # create our own context to have ctx.top_dir
808     Logs.pprint('CYAN', 'Generating HACKING documentation')
809     cmd = _find_rst2html(ctx)
810     hacking_file = os.path.join(ctx.top_dir, 'HACKING')
811     hacking_html_file = os.path.join(ctx.top_dir, 'doc', 'hacking.html')
812     stylesheet = os.path.join(ctx.top_dir, 'doc', 'geany.css')
813     ret = ctx.exec_command('%s  -stg --stylesheet=%s %s %s' % (
814         cmd, stylesheet, hacking_file, hacking_html_file))
815     if ret != 0:
816         raise WafError('Generating HACKING documentation failed')
819 def _find_program(ctx, cmd, **kw):
820     def noop(*args):
821         pass
823     if ctx is None or not isinstance(ctx, ConfigurationContext):
824         ctx = ConfigurationContext()
825         ctx.to_log = noop
826         ctx.msg = noop
827     return ctx.find_program(cmd, **kw)
830 def _find_rst2html(ctx):
831     cmds = ['rst2html', 'rst2html2']
832     for command in cmds:
833         cmd = _find_program(ctx, command, mandatory=False, exts=',.py')
834         if cmd:
835             break
836     if not cmd:
837         raise WafError(
838             'rst2html.py could not be found. Please install the Python docutils package.')
839     return cmd
842 def _add_define_to_env(conf, key):
843     value = conf.get_define(key)
844     # strip quotes
845     value = value.replace('"', '')
846     conf.env[key] = value
849 def _add_to_env_and_define(conf, key, value, quote=False):
850     conf.define(key, value, quote)
851     conf.env[key] = value
854 def _define_from_opt(conf, define_name, opt_value, default_value, quote=1):
855     value = default_value
856     if opt_value:
857         if isinstance(opt_value, bool):
858             opt_value = 1
859         value = opt_value
861     if value is not None:
862         _add_to_env_and_define(conf, define_name, value, quote)
863     else:
864         conf.undefine(define_name)
867 def _get_git_rev(conf):
868     if conf.options.no_scm:
869         return
871     if not os.path.isdir('.git'):
872         return
874     try:
875         cmd = 'git rev-parse --short --revs-only HEAD'
876         revision = conf.cmd_and_log(cmd).strip()
877     except WafError:
878         return None
879     else:
880         return revision
883 def _load_intltool_if_available(conf):
884     try:
885         conf.load('intltool')
886         if 'LINGUAS' in os.environ:
887             conf.env['LINGUAS'] = os.environ['LINGUAS']
888     except WafError:
889         # on Windows, we don't hard depend on intltool, on all other platforms raise an error
890         if not _target_is_win32(conf):
891             raise
894 def _target_is_win32(ctx):
895     if 'is_win32' in ctx.env:
896         # cached
897         return ctx.env['is_win32']
898     is_win32 = None
899     if sys.platform == 'win32':
900         is_win32 = True
901     if is_win32 is None:
902         if ctx.env and 'CC' in ctx.env:
903             env_cc = ctx.env['CC']
904             if not isinstance(env_cc, str):
905                 env_cc = ''.join(env_cc)
906             is_win32 = (env_cc.find('mingw') != -1)
907     if is_win32 is None:
908         is_win32 = False
909     # cache for future checks
910     ctx.env['is_win32'] = is_win32
911     return is_win32
914 def _uc_first(string, ctx):
915     if _target_is_win32(ctx):
916         return string.title()
917     return string
920 # Copied from Geany-Plugins
921 def _check_c99(conf):
922     # FIXME: improve some checks?
923     # TODO: look at Autoconf's C99 checks?
924     fragment = '''
925     // single-line comments
927     #include <stdbool.h>
929     struct s { int a, b; };
931     // inlines
932     static inline void fun_inline(struct s param) {}
934     int main(void) {
935         _Bool b = false;
937         // variable declaration in for body
938         for (int i = 0; i < 2; i++);
940         // compound literals
941         fun_inline((struct s) { 1, 2 });
943         // mixed declarations and code
944         int mixed = 0;
946         // named initializers
947         struct s name_inited = {
948             .a = 42,
949             .b = 64
950         };
952         return (b || mixed || ! name_inited.a);
953     }
954     '''
956     exc = None
957     # list of flags is stolen from Autoconf 2.69
958     flags = ['', '-std=gnu99', '-std=c99', '-c99', '-AC99',
959              '-D_STDC_C99=', '-qlanglvl=extc99']
960     for flag in flags:
961         try:
962             desc = ['with flag %s' % flag, 'with no flags'][not flag]
963             conf.check_cc(fragment=fragment, uselib_store='C99', cflags=flag,
964                           msg="Checking for C99 support (%s)" % desc)
965             exc = None
966             break
967         except ConfigurationError as e:
968             exc = e
969     if exc:
970         raise exc
971     return True