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