Merge pull request #360 from b4n/travis-test
[geany-mirror.git] / wscript
blob6ac7c541afea45e22d68936fe8260a81912ebacb
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 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.25'
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     is_win32 = _target_is_win32(conf)
193     visibility_hidden_supported = conf.check_cc(cflags=['-Werror', '-fvisibility=hidden'], mandatory=False)
194     conf.check_cc(header_name='fcntl.h', mandatory=False)
195     conf.check_cc(header_name='fnmatch.h', mandatory=False)
196     conf.check_cc(header_name='glob.h', mandatory=False)
197     conf.check_cc(header_name='sys/time.h', mandatory=False)
198     conf.check_cc(header_name='sys/types.h', mandatory=False)
199     conf.check_cc(header_name='sys/stat.h', mandatory=False)
200     conf.define('HAVE_STDLIB_H', 1)  # are there systems without stdlib.h?
201     conf.define('STDC_HEADERS', 1)  # an optimistic guess ;-)
202     _add_to_env_and_define(conf, 'HAVE_REGCOMP', 1)  # needed for CTags
204     conf.check_cc(function_name='fgetpos', header_name='stdio.h', mandatory=False)
205     conf.check_cc(function_name='fnmatch', header_name='fnmatch.h', mandatory=False)
206     conf.check_cc(function_name='ftruncate', header_name='unistd.h', mandatory=False)
207     conf.check_cc(function_name='mkstemp', header_name='stdlib.h', mandatory=False)
208     conf.check_cc(function_name='strstr', header_name='string.h')
210     conf.check_cc(function_name='pow', header_name='math.h', lib='m', uselib_store='M')
212     # check sunOS socket support
213     if Options.platform == 'sunos':
214         conf.check_cc(function_name='socket', lib='socket',
215                       header_name='sys/socket.h', uselib_store='SUNOS_SOCKET', mandatory=True)
217     # check for cxx after the header and function checks have been done to ensure they are
218     # checked with cc not cxx
219     conf.load('compiler_cxx')
220     if is_win32:
221         conf.load('winres')
222     _load_intltool_if_available(conf)
224     # GTK / GIO version check
225     gtk_package_name = 'gtk+-3.0' if conf.options.use_gtk3 else 'gtk+-2.0'
226     minimum_gtk_version = MINIMUM_GTK3_VERSION if conf.options.use_gtk3 else MINIMUM_GTK_VERSION
227     conf.check_cfg(package=gtk_package_name, atleast_version=minimum_gtk_version, uselib_store='GTK',
228         mandatory=True, args='--cflags --libs')
229     conf.check_cfg(package='glib-2.0', atleast_version=MINIMUM_GLIB_VERSION, uselib_store='GLIB',
230         mandatory=True, args='--cflags --libs')
231     conf.check_cfg(package='gmodule-2.0', uselib_store='GMODULE',
232         mandatory=True, args='--cflags --libs')
233     conf.check_cfg(package='gio-2.0', uselib_store='GIO', args='--cflags --libs', mandatory=True)
234     gtk_version = conf.check_cfg(modversion=gtk_package_name, uselib_store='GTK') or 'Unknown'
235     conf.check_cfg(package='gthread-2.0', uselib_store='GTHREAD', args='--cflags --libs')
236     if conf.options.enable_mac_integration:
237         pkgname = 'gtk-mac-integration-gtk3' if conf.options.use_gtk3 else 'gtk-mac-integration-gtk2'
238         conf.check_cfg(package=pkgname, uselib_store='MAC_INTEGRATION',
239             mandatory=True, args='--cflags --libs')
240     # remember GTK version for the build step
241     conf.env['gtk_package_name'] = gtk_package_name
242     conf.env['gtk_version'] = gtk_version
243     conf.env['minimum_gtk_version'] = minimum_gtk_version
244     conf.env['use_gtk3'] = conf.options.use_gtk3
246     revision = _get_git_rev(conf)
248     # rst2html for the HTML manual
249     if not conf.options.no_html_doc and revision is not None:
250         try:
251             conf.env['RST2HTML'] = _find_rst2html(conf)
252         except WafError:
253             error_msg = '''Documentation enabled but rst2html not found.
254 You can explicitly disable building of the HTML manual with --disable-html-docs,
255 but you then may not have a local copy of the HTML manual.'''
256             raise WafError(error_msg)
258     # Windows specials
259     if is_win32:
260         if conf.env['PREFIX'].lower() == tempfile.gettempdir().lower():
261             # overwrite default prefix on Windows (tempfile.gettempdir() is the Waf default)
262             new_prefix = os.path.join(str(conf.root), '%s-%s' % (APPNAME, VERSION))
263             _add_to_env_and_define(conf, 'PREFIX', new_prefix, quote=True)
264             _add_to_env_and_define(conf, 'BINDIR', os.path.join(new_prefix, 'bin'), quote=True)
265         _add_to_env_and_define(conf, 'DOCDIR', os.path.join(conf.env['PREFIX'], 'doc'), quote=True)
266         _add_to_env_and_define(conf, 'LIBDIR', '%s/lib' % conf.env['PREFIX'], quote=True)
267         conf.define('LOCALEDIR', os.path.join('share' 'locale'), quote=True)
268         # overwrite LOCALEDIR to install message catalogues properly
269         conf.env['LOCALEDIR'] = os.path.join(conf.env['PREFIX'], 'share', 'locale')
270         # DATADIR is defined in objidl.h, so we remove it from config.h but keep it in env
271         conf.undefine('DATADIR')
272         conf.env['DATADIR'] = os.path.join(conf.env['PREFIX'], 'data')
273         conf.env.append_value('LINKFLAGS_cprogram', [
274             '-mwindows',
275             '-static-libgcc',
276             '-static-libstdc++'])
277         conf.env.append_value('LIB_WIN32', ['wsock32', 'uuid', 'ole32', 'comdlg32'])
278     else:
279         conf.env['cshlib_PATTERN'] = '%s.so'
280         # DATADIR and LOCALEDIR are defined by the intltool tool
281         # but they are not added to the environment, so we need to
282         _add_define_to_env(conf, 'DATADIR')
283         _add_define_to_env(conf, 'LOCALEDIR')
284         docdir = os.path.join(conf.env['DATADIR'], 'doc', 'geany')
285         libdir = os.path.join(conf.env['PREFIX'], 'lib')
286         mandir = os.path.join(conf.env['DATADIR'], 'man')
287         _define_from_opt(conf, 'DOCDIR', conf.options.docdir, docdir)
288         _define_from_opt(conf, 'LIBDIR', conf.options.libdir, libdir)
289         _define_from_opt(conf, 'MANDIR', conf.options.mandir, mandir)
291     conf.define('ENABLE_NLS', 1)
292     conf.define('GEANY_LOCALEDIR', '' if is_win32 else conf.env['LOCALEDIR'], quote=True)
293     conf.define('GEANY_DATADIR', 'data' if is_win32 else conf.env['DATADIR'], quote=True)
294     conf.define('GEANY_DOCDIR', conf.env['DOCDIR'], quote=True)
295     conf.define('GEANY_LIBDIR', '' if is_win32 else conf.env['LIBDIR'], quote=True)
296     conf.define('GEANY_PREFIX', '' if is_win32 else conf.env['PREFIX'], quote=True)
297     conf.define('PACKAGE', APPNAME, quote=True)
298     conf.define('VERSION', VERSION, quote=True)
299     conf.define('REVISION', revision or '-1', quote=True)
301     conf.define('GETTEXT_PACKAGE', APPNAME, quote=True)
303     # no VTE on Windows
304     if is_win32:
305         conf.options.no_vte = True
307     _define_from_opt(conf, 'HAVE_PLUGINS', not conf.options.no_plugins, None)
308     _define_from_opt(conf, 'HAVE_SOCKET', not conf.options.no_socket, None)
309     _define_from_opt(conf, 'HAVE_VTE', not conf.options.no_vte, None)
311     conf.write_config_header('config.h', remove=False)
313     # GEANY_EXPORT_SYMBOL and GEANY_API_SYMBOL
314     if is_win32:
315         geanyexport_cflags = []
316         geanyexport_defines = ['GEANY_EXPORT_SYMBOL=__declspec(dllexport)']
317     elif visibility_hidden_supported:
318         geanyexport_cflags = ['-fvisibility=hidden']
319         geanyexport_defines = ['GEANY_EXPORT_SYMBOL=__attribute__((visibility("default")))']
320     else:  # unknown, define to nothing
321         geanyexport_cflags = []
322         geanyexport_defines = ['GEANY_EXPORT_SYMBOL=']
323     geanyexport_defines.append('GEANY_API_SYMBOL=GEANY_EXPORT_SYMBOL')
324     conf.env['DEFINES_geanyexport'] = geanyexport_defines
325     conf.env['CFLAGS_geanyexport'] = geanyexport_cflags
326     conf.env['CXXFLAGS_geanyexport'] = geanyexport_cflags
328     # some more compiler flags
329     conf.env.append_value('CFLAGS', ['-DHAVE_CONFIG_H'])
330     if conf.env['CC_NAME'] == 'gcc' and '-O' not in ''.join(conf.env['CFLAGS']):
331         conf.env.append_value('CFLAGS', ['-O2'])
332     if revision is not None:
333         conf.env.append_value('CFLAGS', ['-g', '-DGEANY_DEBUG'])
334     # Scintilla flags
335     conf.env.append_value('CFLAGS', ['-DGTK'])
336     conf.env.append_value('CXXFLAGS',
337         ['-DNDEBUG', '-DGTK', '-DSCI_LEXER', '-DG_THREADS_IMPL_NONE'])
338     if conf.env['CXX_NAME'] == 'gcc' and '-O' not in ''.join(conf.env['CXXFLAGS']):
339         conf.env.append_value('CXXFLAGS', ['-O2'])
340     if revision is not None:
341         conf.env.append_value('CXXFLAGS', ['-g'])
343     # summary
344     Logs.pprint('BLUE', 'Summary:')
345     conf.msg('Install Geany ' + VERSION + ' in', conf.env['PREFIX'])
346     conf.msg('Using GTK version', gtk_version)
347     conf.msg('Build with plugin support', conf.options.no_plugins and 'no' or 'yes')
348     conf.msg('Use virtual terminal support', conf.options.no_vte and 'no' or 'yes')
349     if revision is not None:
350         conf.msg('Compiling Git revision', revision)
353 def options(opt):
354     # Disable MSVC detection on win32: building Geany with MSVC is currently not supported
355     # If anyone wants to add support for building with MSVC, this hack should be removed.
356     c_compiler['win32'] = ['gcc']
357     cxx_compiler['win32'] = ['g++']
359     opt.load('compiler_cc')
360     opt.load('compiler_cxx')
361     opt.load('intltool')
363     # Option
364     opt.add_option('--no-scm', action='store_true', default=False,
365         help='Disable SCM detection [default: No]', dest='no_scm')
366     # Features
367     opt.add_option('--disable-plugins', action='store_true', default=False,
368         help='compile without plugin support [default: No]', dest='no_plugins')
369     opt.add_option('--disable-socket', action='store_true', default=False,
370         help='compile without support to detect a running instance [[default: No]',
371         dest='no_socket')
372     opt.add_option('--disable-vte', action='store_true', default=False,
373         help='compile without support for an embedded virtual terminal [[default: No]',
374         dest='no_vte')
375     opt.add_option('--enable-gtk3', action='store_true', default=False,
376         help='compile with GTK3 support (experimental) [[default: No]',
377         dest='use_gtk3')
378     opt.add_option('--enable-mac-integration', action='store_true', default=False,
379         help='use gtk-mac-integration to enable improved OS X integration [[default: No]',
380         dest='enable_mac_integration')
381     opt.add_option('--disable-html-docs', action='store_true', default=False,
382         help='do not generate HTML documentation using rst2html [[default: No]',
383         dest='no_html_doc')
384     # Paths
385     opt.add_option('--mandir', type='string', default='',
386         help='man documentation', dest='mandir')
387     opt.add_option('--docdir', type='string', default='',
388         help='documentation root', dest='docdir')
389     opt.add_option('--libdir', type='string', default='',
390         help='object code libraries', dest='libdir')
393 def build(bld):
394     is_win32 = _target_is_win32(bld)
396     if bld.cmd == 'clean':
397         _remove_linguas_file()
398     if bld.cmd in ('install', 'uninstall'):
399         bld.add_post_fun(_post_install)
401     def build_plugin(plugin_name, install=True, uselib_add=[]):
402         if install:
403             instpath = '${LIBDIR}/geany'
404         else:
405             instpath = None
407         bld(
408             features                = ['c', 'cshlib'],
409             source                  = 'plugins/%s.c' % plugin_name,
410             includes                = ['.', 'src/', 'scintilla/include', 'tagmanager/src'],
411             defines                 = 'G_LOG_DOMAIN="%s"' % plugin_name,
412             target                  = plugin_name,
413             uselib                  = ['GTK', 'GLIB', 'GMODULE'] + uselib_add,
414             use                     = ['geany'],
415             install_path            = instpath)
417     # CTags
418     bld.objects(
419         features        = ['c'],
420         source          = ctags_sources,
421         name            = 'ctags',
422         target          = 'ctags',
423         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
424         defines         = 'G_LOG_DOMAIN="CTags"',
425         uselib          = ['cshlib', 'GLIB', 'geanyexport'])
427     # Tagmanager
428     bld.objects(
429         features        = ['c'],
430         source          = tagmanager_sources,
431         name            = 'tagmanager',
432         target          = 'tagmanager',
433         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
434         defines         = ['GEANY_PRIVATE', 'G_LOG_DOMAIN="Tagmanager"'],
435         uselib          = ['cshlib', 'GTK', 'GLIB', 'geanyexport'])
437     # MIO
438     bld.objects(
439         features        = ['c'],
440         source          = mio_sources,
441         name            = 'mio',
442         target          = 'mio',
443         includes        = ['.', 'tagmanager/mio/'],
444         defines         = 'G_LOG_DOMAIN="MIO"',
445         uselib          = ['cshlib', 'GTK', 'GLIB', 'geanyexport'])
447     # Scintilla
448     files = bld.srcnode.ant_glob('scintilla/**/*.cxx', src=True, dir=False)
449     scintilla_sources.update([file.path_from(bld.srcnode) for file in files])
450     bld.objects(
451         features        = ['c', 'cxx'],
452         name            = 'scintilla',
453         target          = 'scintilla',
454         source          = scintilla_sources,
455         includes        = ['.', 'scintilla/include', 'scintilla/src', 'scintilla/lexlib'],
456         uselib          = ['cshlib', 'cxxshlib', 'GTK', 'GLIB', 'GMODULE', 'M', 'geanyexport'])
458     # Geany
459     if bld.env['HAVE_VTE'] == 1:
460         geany_sources.add('src/vte.c')
461     if is_win32:
462         geany_sources.add('src/win32.c')
463         geany_bin_sources.add('geany_private.rc')
465     def gen_signallist(task):
466         from xml.etree import ElementTree
468         def find_handlers(xml_filename):
469             tree = ElementTree.parse(xml_filename)
470             signals = tree.getroot().findall(".//signal")
471             return [sig.attrib["handler"] for sig in signals]
473         handlers = []
474         for node in task.inputs:
475             handlers += find_handlers(node.abspath())
476         handlers = sorted(set(handlers))
478         for node in task.outputs:
479             node.write("/* This file is auto-generated, do not edit. */\n" +
480                        ''.join(["ITEM(%s)\n" % h for h in handlers]))
482     # signallist.i
483     bld(
484         source  = 'data/geany.glade',
485         target  = bld.path.get_bld().make_node('src/signallist.i'),
486         name    = 'signallist.i',
487         rule    = gen_signallist)
489     base_uselibs = ['GTK', 'GLIB', 'GMODULE', 'GIO', 'GTHREAD', 'WIN32', 'MAC_INTEGRATION', 'SUNOS_SOCKET', 'M']
491     # libgeany
492     bld.shlib(
493         features        = ['c', 'cxx'],
494         name            = 'geany',
495         target          = 'geany',
496         source          = geany_sources,
497         includes        = ['.', 'scintilla/include', 'tagmanager/src', 'src'],
498         defines         = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
499         uselib          = base_uselibs + ['geanyexport'],
500         use             = ['scintilla', 'ctags', 'tagmanager', 'mio'],
501         linkflags       = bld.env['LINKFLAGS_cprogram'],
502         vnum            = GEANY_LIB_VERSION,
503         install_path    = '${PREFIX}/bin' if is_win32 else '${LIBDIR}')
505     # geany executable
506     t = bld.program(
507         features        = ['c', 'cxx'],
508         name            = 'geany_bin',
509         target          = 'geany',
510         source          = geany_bin_sources,
511         includes        = ['.', 'scintilla/include', 'tagmanager/src'],
512         defines         = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
513         uselib          = base_uselibs + ['geanyexport'],
514         use             = ['geany'])
515     if not is_win32:
516         # http://www.freehackers.org/~tnagy/testdoc/single.html#common_c
517         t.rpath = bld.env['LIBDIR']
519     # Plugins
520     if bld.env['HAVE_PLUGINS'] == 1:
521         build_plugin('classbuilder')
522         build_plugin('demoplugin', False)
523         build_plugin('export', uselib_add=['M'])
524         build_plugin('filebrowser')
525         build_plugin('htmlchars')
526         build_plugin('saveactions')
527         build_plugin('splitwindow')
529     # Translations
530     if bld.env['INTLTOOL']:
531         bld(
532             features        = ['linguas', 'intltool_po'],
533             podir           = 'po',
534             install_path    = '${LOCALEDIR}',
535             appname         = 'geany')
537     # HTML documentation (build if it is not part of the tree already, as it is required for install)
538     html_doc_filename = os.path.join(bld.out_dir, 'doc', 'geany.html')
539     if bld.env['RST2HTML']:
540         rst2html = bld.env['RST2HTML']
541         bld(
542             source  = ['doc/geany.txt'],
543             deps    = ['doc/geany.css'],
544             target  = bld.path.get_bld().make_node('doc/geany.html'),
545             name    = 'geany.html',
546             cwd     = os.path.join(bld.path.abspath(), 'doc'),
547             rule    = '%s  -stg --stylesheet=geany.css geany.txt %s' % (rst2html, html_doc_filename))
549     # geany.pc
550     if is_win32:
551         # replace backward slashes by forward slashes as they could be interepreted as escape
552         # characters
553         geany_pc_prefix = bld.env['PREFIX'].replace('\\', '/')
554     else:
555         geany_pc_prefix = bld.env['PREFIX']
556     bld(
557         source          = 'geany.pc.in',
558         dct             = {'VERSION': VERSION,
559                            'DEPENDENCIES': '%s >= %s glib-2.0 >= %s' % \
560                                 (bld.env['gtk_package_name'],
561                                  bld.env['minimum_gtk_version'],
562                                  MINIMUM_GLIB_VERSION),
563                            'prefix': geany_pc_prefix,
564                            'exec_prefix': '${prefix}',
565                            'libdir': '${exec_prefix}/lib',
566                            'includedir': '${prefix}/include',
567                            'datarootdir': '${prefix}/share',
568                            'datadir': '${datarootdir}',
569                            'localedir': '${datarootdir}/locale'})
571     # geany.nsi
572     bld(
573         features        = 'subst',
574         source          = 'geany.nsi.in',
575         target          = 'geany.nsi',
576         dct             = {'VERSION': VERSION,
577                            'GTK_VERSION': bld.env['gtk_version']},
578         install_path    = None)
580     if not is_win32:
581         # geany.desktop
582         if bld.env['INTLTOOL']:
583             bld(
584                 features        = 'intltool_in',
585                 source          = 'geany.desktop.in',
586                 flags           = ['-d', '-q', '-u', '-c'],
587                 install_path    = '${DATADIR}/applications')
589         # geany.1
590         bld(
591             features        = 'subst',
592             source          = 'doc/geany.1.in',
593             target          = 'geany.1',
594             dct             = {'VERSION': VERSION,
595                                 'GEANY_DATA_DIR': bld.env['DATADIR'] + '/geany'},
596             install_path    = '${MANDIR}/man1')
598         # geany.spec
599         bld(
600             features        = 'subst',
601             source          = 'geany.spec.in',
602             target          = 'geany.spec',
603             install_path    = None,
604             dct             = {'VERSION': VERSION})
606         # Doxyfile
607         bld(
608             features        = 'subst',
609             source          = 'doc/Doxyfile.in',
610             target          = 'doc/Doxyfile',
611             install_path    = None,
612             dct             = {'VERSION': VERSION,
613                                'top_builddir': bld.out_dir,
614                                'top_srcdir': bld.top_dir,})
616     # disable build/install phase interleaving
617     bld.add_group()
619     ###
620     # Install files
621     ###
622     # Headers
623     bld.install_files('${PREFIX}/include/geany', '''
624         src/app.h
625         src/build.h
626         src/dialogs.h
627         src/document.h
628         src/editor.h
629         src/encodings.h
630         src/filetypes.h
631         src/geany.h
632         src/highlighting.h
633         src/keybindings.h
634         src/main.h
635         src/msgwindow.h
636         src/navqueue.h
637         src/plugindata.h
638         src/pluginutils.h
639         src/prefs.h
640         src/project.h
641         src/sciwrappers.h
642         src/search.h
643         src/stash.h
644         src/support.h
645         src/symbols.h
646         src/templates.h
647         src/toolbar.h
648         src/ui_utils.h
649         src/utils.h
650         src/gtkcompat.h
651         plugins/geanyplugin.h
652         plugins/geanyfunctions.h
653         ''')
654     bld.install_files('${PREFIX}/include/geany/scintilla', '''
655         scintilla/include/SciLexer.h scintilla/include/Scintilla.h
656         scintilla/include/Scintilla.iface scintilla/include/ScintillaWidget.h ''')
657     bld.install_files('${PREFIX}/include/geany/tagmanager', '''
658         tagmanager/src/tm_source_file.h
659         tagmanager/src/tm_tag.h
660         tagmanager/src/tm_tagmanager.h
661         tagmanager/src/tm_workspace.h ''')
662     # Docs
663     base_dir = '${PREFIX}' if is_win32 else '${DOCDIR}'
664     ext = '.txt' if is_win32 else ''
665     for filename in 'AUTHORS ChangeLog COPYING README NEWS THANKS TODO'.split():
666         basename = _uc_first(filename, bld)
667         destination_filename = '%s%s' % (basename, ext)
668         destination = os.path.join(base_dir, destination_filename)
669         bld.install_as(destination, filename)
671     # install HTML documentation only if it exists, i.e. it was built before
672     # local_html_doc_filename supports installing HTML doc from in-tree geany.html if it exists
673     local_html_doc_filename = os.path.join(bld.path.abspath(), 'doc', 'geany.html')
674     if os.path.exists(html_doc_filename) or os.path.exists(local_html_doc_filename):
675         html_dir = '' if is_win32 else 'html/'
676         html_name = 'Manual.html' if is_win32 else 'index.html'
677         start_dir = bld.path.find_dir('doc/images')
678         bld.install_files('${DOCDIR}/%simages' % html_dir, start_dir.ant_glob('*.png'), cwd=start_dir)
679         bld.install_as('${DOCDIR}/%s%s' % (html_dir, html_name), 'doc/geany.html')
681     bld.install_as('${DOCDIR}/%s' % _uc_first('manual.txt', bld), 'doc/geany.txt')
682     bld.install_as('${DOCDIR}/ScintillaLicense.txt', 'scintilla/License.txt')
683     if is_win32:
684         bld.install_as('${DOCDIR}/ReadMe.I18n.txt', 'README.I18N')
685         bld.install_as('${DOCDIR}/Hacking.txt', 'HACKING')
686     # Data
687     data_dir = '' if is_win32 else 'geany'
688     start_dir = bld.path.find_dir('data')
689     bld.install_as('${DATADIR}/%s/GPL-2' % data_dir, 'COPYING')
690     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('filetype*'), cwd=start_dir)
691     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('*.tags'), cwd=start_dir)
692     bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.glade')
693     bld.install_files('${DATADIR}/%s' % data_dir, 'data/snippets.conf')
694     bld.install_files('${DATADIR}/%s' % data_dir, 'data/ui_toolbar.xml')
695     if bld.env['use_gtk3']:
696         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.css')
697     else:
698         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.gtkrc')
700     start_dir = bld.path.find_dir('data/colorschemes')
701     template_dest = '${DATADIR}/%s/colorschemes' % data_dir
702     bld.install_files(template_dest, start_dir.ant_glob('*'), cwd=start_dir)
703     start_dir = bld.path.find_dir('data/templates')
704     template_dest = '${DATADIR}/%s/templates' % data_dir
705     bld.install_files(template_dest, start_dir.ant_glob('**/*'), cwd=start_dir, relative_trick=True)
706     # Icons
707     for dest, srcs in geany_icons.items():
708         dest_dir = os.path.join('${PREFIX}/share/icons' if is_win32 else '${DATADIR}/icons', dest)
709         bld.install_files(dest_dir, srcs, cwd=bld.path.find_dir('icons'))
710     # install theme indexes on Windows
711     if is_win32:
712         for dest, srcs in geany_icons_indexes.items():
713             bld.install_files(os.path.join('${PREFIX}/share/icons', dest), srcs, cwd=bld.path.find_dir('icons'))
716 def distclean(ctx):
717     Scripting.distclean(ctx)
718     _remove_linguas_file()
721 def _remove_linguas_file():
722     # remove LINGUAS file as well
723     try:
724         os.unlink(LINGUAS_FILE)
725     except OSError:
726         pass
729 @feature('linguas')
730 @before_method('apply_intltool_po')
731 def write_linguas_file(self):
732     if os.path.exists(LINGUAS_FILE):
733         return
734     linguas = ''
735     if 'LINGUAS' in self.env:
736         files = self.env['LINGUAS']
737         for po_filename in files.split(' '):
738             if os.path.exists('po/%s.po' % po_filename):
739                 linguas += '%s ' % po_filename
740     else:
741         files = os.listdir('%s/po' % self.path.abspath())
742         files.sort()
743         for filename in files:
744             if filename.endswith('.po'):
745                 linguas += '%s ' % filename[:-3]
746     file_h = open(LINGUAS_FILE, 'w')
747     file_h.write('# This file is autogenerated. Do not edit.\n%s\n' % linguas)
748     file_h.close()
751 def _post_install(ctx):
752     is_win32 = _target_is_win32(ctx)
753     if is_win32:
754         return
755     for d in 'hicolor', 'Tango':
756         theme_dir = Utils.subst_vars('${DATADIR}/icons/' + d, ctx.env)
757         icon_cache_updated = False
758         if not ctx.options.destdir:
759             ctx.exec_command('gtk-update-icon-cache -q -f -t %s' % theme_dir)
760             Logs.pprint('GREEN', 'GTK icon cache updated.')
761             icon_cache_updated = True
762         if not icon_cache_updated:
763             Logs.pprint('YELLOW', 'Icon cache not updated. After install, run this:')
764             Logs.pprint('YELLOW', 'gtk-update-icon-cache -q -f -t %s' % theme_dir)
767 def updatepo(ctx):
768     """update the message catalogs for internationalization"""
769     potfile = '%s.pot' % APPNAME
770     os.chdir('%s/po' % top)
771     try:
772         try:
773             old_size = os.stat(potfile).st_size
774         except OSError:
775             old_size = 0
776         ctx.exec_command('intltool-update --pot -g %s' % APPNAME)
777         size_new = os.stat(potfile).st_size
778         if size_new != old_size:
779             Logs.pprint('CYAN', 'Updated POT file.')
780             Logs.pprint('CYAN', 'Updating translations')
781             ret = ctx.exec_command('intltool-update -r -g %s' % APPNAME)
782             if ret != 0:
783                 Logs.pprint('RED', 'Updating translations failed')
784         else:
785             Logs.pprint('CYAN', 'POT file is up to date.')
786     except OSError:
787         Logs.pprint('RED', 'Failed to generate pot file.')
790 def apidoc(ctx):
791     """generate API reference documentation"""
792     ctx = BuildContext()  # create our own context to have ctx.top_dir
793     basedir = ctx.top_dir
794     doxygen = _find_program(ctx, 'doxygen')
795     doxyfile = '%s/doc/Doxyfile' % ctx.out_dir
796     Logs.pprint('CYAN', 'Generating API documentation')
797     ret = ctx.exec_command('%s %s' % (doxygen, doxyfile))
798     if ret != 0:
799         raise WafError('Generating API documentation failed')
802 def hackingdoc(ctx):
803     """generate HACKING documentation"""
804     ctx = BuildContext()  # create our own context to have ctx.top_dir
805     Logs.pprint('CYAN', 'Generating HACKING documentation')
806     cmd = _find_rst2html(ctx)
807     hacking_file = os.path.join(ctx.top_dir, 'HACKING')
808     hacking_html_file = os.path.join(ctx.top_dir, 'doc', 'hacking.html')
809     stylesheet = os.path.join(ctx.top_dir, 'doc', 'geany.css')
810     ret = ctx.exec_command('%s  -stg --stylesheet=%s %s %s' % (
811         cmd, stylesheet, hacking_file, hacking_html_file))
812     if ret != 0:
813         raise WafError('Generating HACKING documentation failed')
816 def _find_program(ctx, cmd, **kw):
817     def noop(*args):
818         pass
820     if ctx is None or not isinstance(ctx, ConfigurationContext):
821         ctx = ConfigurationContext()
822         ctx.to_log = noop
823         ctx.msg = noop
824     return ctx.find_program(cmd, **kw)
827 def _find_rst2html(ctx):
828     cmds = ['rst2html', 'rst2html2']
829     for command in cmds:
830         cmd = _find_program(ctx, command, mandatory=False, exts=',.py')
831         if cmd:
832             break
833     if not cmd:
834         raise WafError(
835             'rst2html.py could not be found. Please install the Python docutils package.')
836     return cmd
839 def _add_define_to_env(conf, key):
840     value = conf.get_define(key)
841     # strip quotes
842     value = value.replace('"', '')
843     conf.env[key] = value
846 def _add_to_env_and_define(conf, key, value, quote=False):
847     conf.define(key, value, quote)
848     conf.env[key] = value
851 def _define_from_opt(conf, define_name, opt_value, default_value, quote=1):
852     value = default_value
853     if opt_value:
854         if isinstance(opt_value, bool):
855             opt_value = 1
856         value = opt_value
858     if value is not None:
859         _add_to_env_and_define(conf, define_name, value, quote)
860     else:
861         conf.undefine(define_name)
864 def _get_git_rev(conf):
865     if conf.options.no_scm:
866         return
868     if not os.path.isdir('.git'):
869         return
871     try:
872         cmd = 'git rev-parse --short --revs-only HEAD'
873         revision = conf.cmd_and_log(cmd).strip()
874     except WafError:
875         return None
876     else:
877         return revision
880 def _load_intltool_if_available(conf):
881     try:
882         conf.load('intltool')
883         if 'LINGUAS' in os.environ:
884             conf.env['LINGUAS'] = os.environ['LINGUAS']
885     except WafError:
886         # on Windows, we don't hard depend on intltool, on all other platforms raise an error
887         if not _target_is_win32(conf):
888             raise
891 def _target_is_win32(ctx):
892     if 'is_win32' in ctx.env:
893         # cached
894         return ctx.env['is_win32']
895     is_win32 = None
896     if sys.platform == 'win32':
897         is_win32 = True
898     if is_win32 is None:
899         if ctx.env and 'CC' in ctx.env:
900             env_cc = ctx.env['CC']
901             if not isinstance(env_cc, str):
902                 env_cc = ''.join(env_cc)
903             is_win32 = (env_cc.find('mingw') != -1)
904     if is_win32 is None:
905         is_win32 = False
906     # cache for future checks
907     ctx.env['is_win32'] = is_win32
908     return is_win32
911 def _uc_first(string, ctx):
912     if _target_is_win32(ctx):
913         return string.title()
914     return string