Bump API version for new plugin entry points (oops)
[geany-mirror.git] / wscript
blobb66af42736eceb1ae7451b759fc25f00988901e1
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.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     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/spawn.h
644         src/stash.h
645         src/support.h
646         src/symbols.h
647         src/templates.h
648         src/toolbar.h
649         src/ui_utils.h
650         src/utils.h
651         src/gtkcompat.h
652         plugins/geanyplugin.h
653         plugins/geanyfunctions.h
654         ''')
655     bld.install_files('${PREFIX}/include/geany/scintilla', '''
656         scintilla/include/SciLexer.h scintilla/include/Scintilla.h
657         scintilla/include/Scintilla.iface scintilla/include/ScintillaWidget.h ''')
658     bld.install_files('${PREFIX}/include/geany/tagmanager', '''
659         tagmanager/src/tm_source_file.h
660         tagmanager/src/tm_tag.h
661         tagmanager/src/tm_tagmanager.h
662         tagmanager/src/tm_workspace.h ''')
663     # Docs
664     base_dir = '${PREFIX}' if is_win32 else '${DOCDIR}'
665     ext = '.txt' if is_win32 else ''
666     for filename in 'AUTHORS ChangeLog COPYING README NEWS THANKS TODO'.split():
667         basename = _uc_first(filename, bld)
668         destination_filename = '%s%s' % (basename, ext)
669         destination = os.path.join(base_dir, destination_filename)
670         bld.install_as(destination, filename)
672     # install HTML documentation only if it exists, i.e. it was built before
673     # local_html_doc_filename supports installing HTML doc from in-tree geany.html if it exists
674     local_html_doc_filename = os.path.join(bld.path.abspath(), 'doc', 'geany.html')
675     if os.path.exists(html_doc_filename) or os.path.exists(local_html_doc_filename):
676         html_dir = '' if is_win32 else 'html/'
677         html_name = 'Manual.html' if is_win32 else 'index.html'
678         start_dir = bld.path.find_dir('doc/images')
679         bld.install_files('${DOCDIR}/%simages' % html_dir, start_dir.ant_glob('*.png'), cwd=start_dir)
680         bld.install_as('${DOCDIR}/%s%s' % (html_dir, html_name), 'doc/geany.html')
682     bld.install_as('${DOCDIR}/%s' % _uc_first('manual.txt', bld), 'doc/geany.txt')
683     bld.install_as('${DOCDIR}/ScintillaLicense.txt', 'scintilla/License.txt')
684     if is_win32:
685         bld.install_as('${DOCDIR}/ReadMe.I18n.txt', 'README.I18N')
686         bld.install_as('${DOCDIR}/Hacking.txt', 'HACKING')
687     # Data
688     data_dir = '' if is_win32 else 'geany'
689     start_dir = bld.path.find_dir('data')
690     bld.install_as('${DATADIR}/%s/GPL-2' % data_dir, 'COPYING')
691     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('filetype*'), cwd=start_dir)
692     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('*.tags'), cwd=start_dir)
693     bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.glade')
694     bld.install_files('${DATADIR}/%s' % data_dir, 'data/snippets.conf')
695     bld.install_files('${DATADIR}/%s' % data_dir, 'data/ui_toolbar.xml')
696     if bld.env['use_gtk3']:
697         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.css')
698     else:
699         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.gtkrc')
701     start_dir = bld.path.find_dir('data/colorschemes')
702     template_dest = '${DATADIR}/%s/colorschemes' % data_dir
703     bld.install_files(template_dest, start_dir.ant_glob('*'), cwd=start_dir)
704     start_dir = bld.path.find_dir('data/templates')
705     template_dest = '${DATADIR}/%s/templates' % data_dir
706     bld.install_files(template_dest, start_dir.ant_glob('**/*'), cwd=start_dir, relative_trick=True)
707     # Icons
708     for dest, srcs in geany_icons.items():
709         dest_dir = os.path.join('${PREFIX}/share/icons' if is_win32 else '${DATADIR}/icons', dest)
710         bld.install_files(dest_dir, srcs, cwd=bld.path.find_dir('icons'))
711     # install theme indexes on Windows
712     if is_win32:
713         for dest, srcs in geany_icons_indexes.items():
714             bld.install_files(os.path.join('${PREFIX}/share/icons', dest), srcs, cwd=bld.path.find_dir('icons'))
717 def distclean(ctx):
718     Scripting.distclean(ctx)
719     _remove_linguas_file()
722 def _remove_linguas_file():
723     # remove LINGUAS file as well
724     try:
725         os.unlink(LINGUAS_FILE)
726     except OSError:
727         pass
730 @feature('linguas')
731 @before_method('apply_intltool_po')
732 def write_linguas_file(self):
733     if os.path.exists(LINGUAS_FILE):
734         return
735     linguas = ''
736     if 'LINGUAS' in self.env:
737         files = self.env['LINGUAS']
738         for po_filename in files.split(' '):
739             if os.path.exists('po/%s.po' % po_filename):
740                 linguas += '%s ' % po_filename
741     else:
742         files = os.listdir('%s/po' % self.path.abspath())
743         files.sort()
744         for filename in files:
745             if filename.endswith('.po'):
746                 linguas += '%s ' % filename[:-3]
747     file_h = open(LINGUAS_FILE, 'w')
748     file_h.write('# This file is autogenerated. Do not edit.\n%s\n' % linguas)
749     file_h.close()
752 def _post_install(ctx):
753     is_win32 = _target_is_win32(ctx)
754     if is_win32:
755         return
756     for d in 'hicolor', 'Tango':
757         theme_dir = Utils.subst_vars('${DATADIR}/icons/' + d, ctx.env)
758         icon_cache_updated = False
759         if not ctx.options.destdir:
760             ctx.exec_command('gtk-update-icon-cache -q -f -t %s' % theme_dir)
761             Logs.pprint('GREEN', 'GTK icon cache updated.')
762             icon_cache_updated = True
763         if not icon_cache_updated:
764             Logs.pprint('YELLOW', 'Icon cache not updated. After install, run this:')
765             Logs.pprint('YELLOW', 'gtk-update-icon-cache -q -f -t %s' % theme_dir)
768 def updatepo(ctx):
769     """update the message catalogs for internationalization"""
770     potfile = '%s.pot' % APPNAME
771     os.chdir('%s/po' % top)
772     try:
773         try:
774             old_size = os.stat(potfile).st_size
775         except OSError:
776             old_size = 0
777         ctx.exec_command('intltool-update --pot -g %s' % APPNAME)
778         size_new = os.stat(potfile).st_size
779         if size_new != old_size:
780             Logs.pprint('CYAN', 'Updated POT file.')
781             Logs.pprint('CYAN', 'Updating translations')
782             ret = ctx.exec_command('intltool-update -r -g %s' % APPNAME)
783             if ret != 0:
784                 Logs.pprint('RED', 'Updating translations failed')
785         else:
786             Logs.pprint('CYAN', 'POT file is up to date.')
787     except OSError:
788         Logs.pprint('RED', 'Failed to generate pot file.')
791 def apidoc(ctx):
792     """generate API reference documentation"""
793     ctx = BuildContext()  # create our own context to have ctx.top_dir
794     basedir = ctx.top_dir
795     doxygen = _find_program(ctx, 'doxygen')
796     doxyfile = '%s/doc/Doxyfile' % ctx.out_dir
797     Logs.pprint('CYAN', 'Generating API documentation')
798     ret = ctx.exec_command('%s %s' % (doxygen, doxyfile))
799     if ret != 0:
800         raise WafError('Generating API documentation failed')
803 def hackingdoc(ctx):
804     """generate HACKING documentation"""
805     ctx = BuildContext()  # create our own context to have ctx.top_dir
806     Logs.pprint('CYAN', 'Generating HACKING documentation')
807     cmd = _find_rst2html(ctx)
808     hacking_file = os.path.join(ctx.top_dir, 'HACKING')
809     hacking_html_file = os.path.join(ctx.top_dir, 'doc', 'hacking.html')
810     stylesheet = os.path.join(ctx.top_dir, 'doc', 'geany.css')
811     ret = ctx.exec_command('%s  -stg --stylesheet=%s %s %s' % (
812         cmd, stylesheet, hacking_file, hacking_html_file))
813     if ret != 0:
814         raise WafError('Generating HACKING documentation failed')
817 def _find_program(ctx, cmd, **kw):
818     def noop(*args):
819         pass
821     if ctx is None or not isinstance(ctx, ConfigurationContext):
822         ctx = ConfigurationContext()
823         ctx.to_log = noop
824         ctx.msg = noop
825     return ctx.find_program(cmd, **kw)
828 def _find_rst2html(ctx):
829     cmds = ['rst2html', 'rst2html2']
830     for command in cmds:
831         cmd = _find_program(ctx, command, mandatory=False, exts=',.py')
832         if cmd:
833             break
834     if not cmd:
835         raise WafError(
836             'rst2html.py could not be found. Please install the Python docutils package.')
837     return cmd
840 def _add_define_to_env(conf, key):
841     value = conf.get_define(key)
842     # strip quotes
843     value = value.replace('"', '')
844     conf.env[key] = value
847 def _add_to_env_and_define(conf, key, value, quote=False):
848     conf.define(key, value, quote)
849     conf.env[key] = value
852 def _define_from_opt(conf, define_name, opt_value, default_value, quote=1):
853     value = default_value
854     if opt_value:
855         if isinstance(opt_value, bool):
856             opt_value = 1
857         value = opt_value
859     if value is not None:
860         _add_to_env_and_define(conf, define_name, value, quote)
861     else:
862         conf.undefine(define_name)
865 def _get_git_rev(conf):
866     if conf.options.no_scm:
867         return
869     if not os.path.isdir('.git'):
870         return
872     try:
873         cmd = 'git rev-parse --short --revs-only HEAD'
874         revision = conf.cmd_and_log(cmd).strip()
875     except WafError:
876         return None
877     else:
878         return revision
881 def _load_intltool_if_available(conf):
882     try:
883         conf.load('intltool')
884         if 'LINGUAS' in os.environ:
885             conf.env['LINGUAS'] = os.environ['LINGUAS']
886     except WafError:
887         # on Windows, we don't hard depend on intltool, on all other platforms raise an error
888         if not _target_is_win32(conf):
889             raise
892 def _target_is_win32(ctx):
893     if 'is_win32' in ctx.env:
894         # cached
895         return ctx.env['is_win32']
896     is_win32 = None
897     if sys.platform == 'win32':
898         is_win32 = True
899     if is_win32 is None:
900         if ctx.env and 'CC' in ctx.env:
901             env_cc = ctx.env['CC']
902             if not isinstance(env_cc, str):
903                 env_cc = ''.join(env_cc)
904             is_win32 = (env_cc.find('mingw') != -1)
905     if is_win32 is None:
906         is_win32 = False
907     # cache for future checks
908     ctx.env['is_win32'] = is_win32
909     return is_win32
912 def _uc_first(string, ctx):
913     if _target_is_win32(ctx):
914         return string.title()
915     return string