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