Merge PR#356 from 'b4n/techee/tm'
[geany-mirror.git] / wscript
blob889e8342c77fbd1d4a2413f28830a3db7599c880
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.16.0'
58 MINIMUM_GTK3_VERSION = '3.0.0'
59 MINIMUM_GLIB_VERSION = '2.20.0'
61 top = '.'
62 out = '_build_'
65 mio_sources = set(['tagmanager/mio/mio.c'])
67 ctags_sources = set([
68     'tagmanager/ctags/abaqus.c',
69     'tagmanager/ctags/args.c',
70     'tagmanager/ctags/abc.c',
71     'tagmanager/ctags/actionscript.c',
72     'tagmanager/ctags/asciidoc.c',
73     'tagmanager/ctags/asm.c',
74     'tagmanager/ctags/basic.c',
75     'tagmanager/ctags/c.c',
76     'tagmanager/ctags/cobol.c',
77     'tagmanager/ctags/conf.c',
78     'tagmanager/ctags/css.c',
79     'tagmanager/ctags/ctags.c',
80     'tagmanager/ctags/diff.c',
81     'tagmanager/ctags/docbook.c',
82     'tagmanager/ctags/entry.c',
83     'tagmanager/ctags/fortran.c',
84     'tagmanager/ctags/get.c',
85     'tagmanager/ctags/haskell.c',
86     'tagmanager/ctags/haxe.c',
87     'tagmanager/ctags/html.c',
88     'tagmanager/ctags/js.c',
89     'tagmanager/ctags/keyword.c',
90     'tagmanager/ctags/latex.c',
91     'tagmanager/ctags/lregex.c',
92     'tagmanager/ctags/lua.c',
93     'tagmanager/ctags/make.c',
94     'tagmanager/ctags/markdown.c',
95     'tagmanager/ctags/matlab.c',
96     'tagmanager/ctags/nsis.c',
97     'tagmanager/ctags/nestlevel.c',
98     'tagmanager/ctags/objc.c',
99     'tagmanager/ctags/options.c',
100     'tagmanager/ctags/parse.c',
101     'tagmanager/ctags/pascal.c',
102     'tagmanager/ctags/r.c',
103     'tagmanager/ctags/perl.c',
104     'tagmanager/ctags/php.c',
105     'tagmanager/ctags/python.c',
106     'tagmanager/ctags/read.c',
107     'tagmanager/ctags/rest.c',
108     'tagmanager/ctags/ruby.c',
109     'tagmanager/ctags/rust.c',
110     'tagmanager/ctags/sh.c',
111     'tagmanager/ctags/sort.c',
112     'tagmanager/ctags/sql.c',
113     'tagmanager/ctags/strlist.c',
114     'tagmanager/ctags/txt2tags.c',
115     'tagmanager/ctags/tcl.c',
116     'tagmanager/ctags/vhdl.c',
117     'tagmanager/ctags/verilog.c',
118     'tagmanager/ctags/vstring.c'])
120 tagmanager_sources = set([
121     'tagmanager/src/tm_source_file.c',
122     'tagmanager/src/tm_tag.c',
123     'tagmanager/src/tm_workspace.c'])
125 scintilla_sources = set(['scintilla/gtk/scintilla-marshal.c'])
127 geany_sources = set([
128     'src/about.c', 'src/build.c', 'src/callbacks.c', 'src/dialogs.c', 'src/document.c',
129     'src/editor.c', 'src/encodings.c', 'src/filetypes.c', 'src/geanyentryaction.c',
130     'src/geanymenubuttonaction.c', 'src/geanyobject.c', 'src/geanywraplabel.c',
131     'src/highlighting.c', 'src/keybindings.c',
132     'src/keyfile.c', 'src/log.c', 'src/main.c', 'src/msgwindow.c', 'src/navqueue.c', 'src/notebook.c',
133     'src/plugins.c', 'src/pluginutils.c', 'src/prefix.c', 'src/prefs.c', 'src/printing.c', 'src/project.c',
134     'src/sciwrappers.c', 'src/search.c', 'src/socket.c', 'src/stash.c',
135     'src/symbols.c',
136     'src/templates.c', 'src/toolbar.c', 'src/tools.c', 'src/sidebar.c',
137     'src/ui_utils.c', 'src/utils.c'])
139 geany_icons = {
140     'hicolor/16x16/apps':       ['16x16/classviewer-class.png',
141                                  '16x16/classviewer-macro.png',
142                                  '16x16/classviewer-member.png',
143                                  '16x16/classviewer-method.png',
144                                  '16x16/classviewer-namespace.png',
145                                  '16x16/classviewer-other.png',
146                                  '16x16/classviewer-struct.png',
147                                  '16x16/classviewer-var.png',
148                                  '16x16/geany.png'],
149     'hicolor/16x16/actions':    ['16x16/geany-build.png',
150                                  '16x16/geany-close-all.png',
151                                  '16x16/geany-save-all.png'],
152     'hicolor/24x24/actions':    ['24x24/geany-build.png',
153                                  '24x24/geany-close-all.png',
154                                  '24x24/geany-save-all.png'],
155     'hicolor/32x32/actions':    ['32x32/geany-build.png',
156                                  '32x32/geany-close-all.png',
157                                  '32x32/geany-save-all.png'],
158     'hicolor/48x48/actions':    ['48x48/geany-build.png',
159                                  '48x48/geany-close-all.png',
160                                  '48x48/geany-save-all.png'],
161     'hicolor/48x48/apps':       ['48x48/geany.png'],
162     'hicolor/scalable/apps':    ['scalable/geany.svg'],
163     'hicolor/scalable/actions': ['scalable/geany-build.svg',
164                                  'scalable/geany-close-all.svg',
165                                  'scalable/geany-save-all.svg'],
166     'Tango/16x16/actions':      ['tango/16x16/geany-save-all.png'],
167     'Tango/24x24/actions':      ['tango/24x24/geany-save-all.png'],
168     'Tango/32x32/actions':      ['tango/32x32/geany-save-all.png'],
169     'Tango/48x48/actions':      ['tango/48x48/geany-save-all.png'],
170     'Tango/scalable/actions':   ['tango/scalable/geany-save-all.svg']
172 geany_icons_indexes = {
173     'hicolor':  ['index.theme'],
174     'Tango':    ['tango/index.theme']
178 def configure(conf):
180     conf.check_waf_version(mini='1.6.1')
182     conf.load('compiler_c')
183     is_win32 = _target_is_win32(conf)
185     conf.check_cc(header_name='fcntl.h', mandatory=False)
186     conf.check_cc(header_name='fnmatch.h', mandatory=False)
187     conf.check_cc(header_name='glob.h', mandatory=False)
188     conf.check_cc(header_name='sys/time.h', mandatory=False)
189     conf.check_cc(header_name='sys/types.h', mandatory=False)
190     conf.check_cc(header_name='sys/stat.h', mandatory=False)
191     conf.define('HAVE_STDLIB_H', 1)  # are there systems without stdlib.h?
192     conf.define('STDC_HEADERS', 1)  # an optimistic guess ;-)
193     _add_to_env_and_define(conf, 'HAVE_REGCOMP', 1)  # needed for CTags
195     conf.check_cc(function_name='fgetpos', header_name='stdio.h', mandatory=False)
196     conf.check_cc(function_name='ftruncate', header_name='unistd.h', mandatory=False)
197     conf.check_cc(function_name='mkstemp', header_name='stdlib.h', mandatory=False)
198     conf.check_cc(function_name='strstr', header_name='string.h')
200     conf.check_cc(function_name='pow', header_name='math.h', lib='m', uselib_store='M')
202     # check sunOS socket support
203     if Options.platform == 'sunos':
204         conf.check_cc(function_name='socket', lib='socket',
205                       header_name='sys/socket.h', uselib_store='SUNOS_SOCKET', mandatory=True)
207     # check for cxx after the header and function checks have been done to ensure they are
208     # checked with cc not cxx
209     conf.load('compiler_cxx')
210     if is_win32:
211         conf.load('winres')
212     _load_intltool_if_available(conf)
214     # GTK / GIO version check
215     gtk_package_name = 'gtk+-3.0' if conf.options.use_gtk3 else 'gtk+-2.0'
216     minimum_gtk_version = MINIMUM_GTK3_VERSION if conf.options.use_gtk3 else MINIMUM_GTK_VERSION
217     conf.check_cfg(package=gtk_package_name, atleast_version=minimum_gtk_version, uselib_store='GTK',
218         mandatory=True, args='--cflags --libs')
219     conf.check_cfg(package='glib-2.0', atleast_version=MINIMUM_GLIB_VERSION, uselib_store='GLIB',
220         mandatory=True, args='--cflags --libs')
221     conf.check_cfg(package='gmodule-2.0', uselib_store='GMODULE',
222         mandatory=True, args='--cflags --libs')
223     conf.check_cfg(package='gio-2.0', uselib_store='GIO', args='--cflags --libs', mandatory=True)
224     gtk_version = conf.check_cfg(modversion=gtk_package_name, uselib_store='GTK') or 'Unknown'
225     conf.check_cfg(package='gthread-2.0', uselib_store='GTHREAD', args='--cflags --libs')
226     # remember GTK version for the build step
227     conf.env['gtk_package_name'] = gtk_package_name
228     conf.env['minimum_gtk_version'] = minimum_gtk_version
229     conf.env['use_gtk3'] = conf.options.use_gtk3
231     revision = _get_git_rev(conf)
233     # rst2html for the HTML manual
234     if not conf.options.no_html_doc and revision is not None:
235         try:
236             conf.env['RST2HTML'] = _find_rst2html(conf)
237         except WafError:
238             error_msg = '''Documentation enabled but rst2html not found.
239 You can explicitly disable building of the HTML manual with --disable-html-docs,
240 but you then may not have a local copy of the HTML manual.'''
241             raise WafError(error_msg)
243     # Windows specials
244     if is_win32:
245         if conf.env['PREFIX'].lower() == tempfile.gettempdir().lower():
246             # overwrite default prefix on Windows (tempfile.gettempdir() is the Waf default)
247             new_prefix = os.path.join(str(conf.root), '%s-%s' % (APPNAME, VERSION))
248             _add_to_env_and_define(conf, 'PREFIX', new_prefix, quote=True)
249             _add_to_env_and_define(conf, 'BINDIR', os.path.join(new_prefix, 'bin'), quote=True)
250         _add_to_env_and_define(conf, 'DOCDIR', os.path.join(conf.env['PREFIX'], 'doc'), quote=True)
251         _add_to_env_and_define(conf, 'LIBDIR', '%s/lib' % conf.env['PREFIX'], quote=True)
252         conf.define('LOCALEDIR', os.path.join('share' 'locale'), quote=True)
253         # overwrite LOCALEDIR to install message catalogues properly
254         conf.env['LOCALEDIR'] = os.path.join(conf.env['PREFIX'], 'share', 'locale')
255         # DATADIR is defined in objidl.h, so we remove it from config.h but keep it in env
256         conf.undefine('DATADIR')
257         conf.env['DATADIR'] = os.path.join(conf.env['PREFIX'], 'data')
258         conf.env.append_value('LINKFLAGS_cprogram', [
259             '-mwindows',
260             '-static-libgcc',
261             '-static-libstdc++'])
262         conf.env.append_value('LIB_WIN32', ['wsock32', 'uuid', 'ole32', 'iberty'])
263         # explicitly define Windows version for older Mingw environments
264         conf.define('WINVER', '0x0501', quote=False)  # for SHGetFolderPathAndSubDirW
265         conf.define('_WIN32_IE', '0x0500', quote=False)  # for SHGFP_TYPE
266     else:
267         conf.env['cshlib_PATTERN'] = '%s.so'
268         # DATADIR and LOCALEDIR are defined by the intltool tool
269         # but they are not added to the environment, so we need to
270         _add_define_to_env(conf, 'DATADIR')
271         _add_define_to_env(conf, 'LOCALEDIR')
272         docdir = os.path.join(conf.env['DATADIR'], 'doc', 'geany')
273         libdir = os.path.join(conf.env['PREFIX'], 'lib')
274         mandir = os.path.join(conf.env['DATADIR'], 'man')
275         _define_from_opt(conf, 'DOCDIR', conf.options.docdir, docdir)
276         _define_from_opt(conf, 'LIBDIR', conf.options.libdir, libdir)
277         _define_from_opt(conf, 'MANDIR', conf.options.mandir, mandir)
279     conf.define('ENABLE_NLS', 1)
280     conf.define('GEANY_LOCALEDIR', '' if is_win32 else conf.env['LOCALEDIR'], quote=True)
281     conf.define('GEANY_DATADIR', 'data' if is_win32 else conf.env['DATADIR'], quote=True)
282     conf.define('GEANY_DOCDIR', conf.env['DOCDIR'], quote=True)
283     conf.define('GEANY_LIBDIR', '' if is_win32 else conf.env['LIBDIR'], quote=True)
284     conf.define('GEANY_PREFIX', '' if is_win32 else conf.env['PREFIX'], quote=True)
285     conf.define('PACKAGE', APPNAME, quote=True)
286     conf.define('VERSION', VERSION, quote=True)
287     conf.define('REVISION', revision or '-1', quote=True)
289     conf.define('GETTEXT_PACKAGE', APPNAME, quote=True)
291     # no VTE on Windows
292     if is_win32:
293         conf.options.no_vte = True
295     _define_from_opt(conf, 'HAVE_PLUGINS', not conf.options.no_plugins, None)
296     _define_from_opt(conf, 'HAVE_SOCKET', not conf.options.no_socket, None)
297     _define_from_opt(conf, 'HAVE_VTE', not conf.options.no_vte, None)
299     conf.write_config_header('config.h', remove=False)
301     # some more compiler flags
302     conf.env.append_value('CFLAGS', ['-DHAVE_CONFIG_H'])
303     if conf.env['CC_NAME'] == 'gcc' and '-O' not in ''.join(conf.env['CFLAGS']):
304         conf.env.append_value('CFLAGS', ['-O2'])
305     if revision is not None:
306         conf.env.append_value('CFLAGS', ['-g', '-DGEANY_DEBUG'])
307     # Scintilla flags
308     conf.env.append_value('CFLAGS', ['-DGTK'])
309     conf.env.append_value('CXXFLAGS',
310         ['-DNDEBUG', '-DGTK', '-DSCI_LEXER', '-DG_THREADS_IMPL_NONE'])
312     # summary
313     Logs.pprint('BLUE', 'Summary:')
314     conf.msg('Install Geany ' + VERSION + ' in', conf.env['PREFIX'])
315     conf.msg('Using GTK version', gtk_version)
316     conf.msg('Build with plugin support', conf.options.no_plugins and 'no' or 'yes')
317     conf.msg('Use virtual terminal support', conf.options.no_vte and 'no' or 'yes')
318     if revision is not None:
319         conf.msg('Compiling Git revision', revision)
322 def options(opt):
323     # Disable MSVC detection on win32: building Geany with MSVC is currently not supported
324     # If anyone wants to add support for building with MSVC, this hack should be removed.
325     c_compiler['win32'] = ['gcc']
326     cxx_compiler['win32'] = ['g++']
328     opt.load('compiler_cc')
329     opt.load('compiler_cxx')
330     opt.load('intltool')
332     # Option
333     opt.add_option('--no-scm', action='store_true', default=False,
334         help='Disable SCM detection [default: No]', dest='no_scm')
335     # Features
336     opt.add_option('--disable-plugins', action='store_true', default=False,
337         help='compile without plugin support [default: No]', dest='no_plugins')
338     opt.add_option('--disable-socket', action='store_true', default=False,
339         help='compile without support to detect a running instance [[default: No]',
340         dest='no_socket')
341     opt.add_option('--disable-vte', action='store_true', default=False,
342         help='compile without support for an embedded virtual terminal [[default: No]',
343         dest='no_vte')
344     opt.add_option('--enable-gtk3', action='store_true', default=False,
345         help='compile with GTK3 support (experimental) [[default: No]',
346         dest='use_gtk3')
347     opt.add_option('--disable-html-docs', action='store_true', default=False,
348         help='do not generate HTML documentation using rst2html [[default: No]',
349         dest='no_html_doc')
350     # Paths
351     opt.add_option('--mandir', type='string', default='',
352         help='man documentation', dest='mandir')
353     opt.add_option('--docdir', type='string', default='',
354         help='documentation root', dest='docdir')
355     opt.add_option('--libdir', type='string', default='',
356         help='object code libraries', dest='libdir')
359 def build(bld):
360     is_win32 = _target_is_win32(bld)
362     if bld.cmd == 'clean':
363         _remove_linguas_file()
364     if bld.cmd in ('install', 'uninstall'):
365         bld.add_post_fun(_post_install)
367     def build_plugin(plugin_name, install=True, uselib_add=[]):
368         if install:
369             instpath = '${PREFIX}/lib' if is_win32 else '${LIBDIR}/geany'
370         else:
371             instpath = None
373         bld(
374             features                = ['c', 'cshlib'],
375             source                  = 'plugins/%s.c' % plugin_name,
376             includes                = ['.', 'src/', 'scintilla/include', 'tagmanager/src'],
377             defines                 = 'G_LOG_DOMAIN="%s"' % plugin_name,
378             target                  = plugin_name,
379             uselib                  = ['GTK', 'GLIB', 'GMODULE'] + uselib_add,
380             install_path            = instpath)
382     # CTags
383     bld(
384         features        = ['c', 'cstlib'],
385         source          = ctags_sources,
386         name            = 'ctags',
387         target          = 'ctags',
388         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
389         defines         = 'G_LOG_DOMAIN="CTags"',
390         uselib          = ['GLIB'],
391         install_path    = None)  # do not install this library
393     # Tagmanager
394     bld(
395         features        = ['c', 'cstlib'],
396         source          = tagmanager_sources,
397         name            = 'tagmanager',
398         target          = 'tagmanager',
399         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
400         defines         = ['GEANY_PRIVATE', 'G_LOG_DOMAIN="Tagmanager"'],
401         uselib          = ['GTK', 'GLIB'],
402         install_path    = None)  # do not install this library
404     # MIO
405     bld(
406         features        = ['c', 'cstlib'],
407         source          = mio_sources,
408         name            = 'mio',
409         target          = 'mio',
410         includes        = ['.', 'tagmanager/mio/'],
411         defines         = 'G_LOG_DOMAIN="MIO"',
412         uselib          = ['GTK', 'GLIB'],
413         install_path    = None)  # do not install this library
415     # Scintilla
416     files = bld.srcnode.ant_glob('scintilla/**/*.cxx', src=True, dir=False)
417     scintilla_sources.update(files)
418     bld(
419         features        = ['c', 'cxx', 'cxxstlib'],
420         name            = 'scintilla',
421         target          = 'scintilla',
422         source          = scintilla_sources,
423         includes        = ['.', 'scintilla/include', 'scintilla/src', 'scintilla/lexlib'],
424         uselib          = ['GTK', 'GLIB', 'GMODULE', 'M'],
425         install_path    = None)  # do not install this library
427     # Geany
428     if bld.env['HAVE_VTE'] == 1:
429         geany_sources.add('src/vte.c')
430     if is_win32:
431         geany_sources.add('src/win32.c')
432         geany_sources.add('geany_private.rc')
434     bld(
435         features        = ['c', 'cxx', 'cprogram'],
436         name            = 'geany',
437         target          = 'geany',
438         source          = geany_sources,
439         includes        = ['.', 'scintilla/include', 'tagmanager/src'],
440         defines         = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
441         uselib          = ['GTK', 'GLIB', 'GMODULE', 'GIO', 'GTHREAD', 'WIN32', 'SUNOS_SOCKET', 'M'],
442         use             = ['scintilla', 'ctags', 'tagmanager', 'mio'])
444     # geanyfunctions.h
445     bld(
446         source  = ['plugins/genapi.py', 'src/plugins.c'],
447         name    = 'geanyfunctions.h',
448         before  = ['c', 'cxx'],
449         cwd     = '%s/plugins' % bld.path.abspath(),
450         rule    = '%s genapi.py -q' % sys.executable)
452     # Plugins
453     if bld.env['HAVE_PLUGINS'] == 1:
454         build_plugin('classbuilder')
455         build_plugin('demoplugin', False)
456         build_plugin('export', uselib_add=['M'])
457         build_plugin('filebrowser')
458         build_plugin('htmlchars')
459         build_plugin('saveactions')
460         build_plugin('splitwindow')
462     # Translations
463     if bld.env['INTLTOOL']:
464         bld(
465             features        = ['linguas', 'intltool_po'],
466             podir           = 'po',
467             install_path    = '${LOCALEDIR}',
468             appname         = 'geany')
470     # HTML documentation (build if it is not part of the tree already, as it is required for install)
471     html_doc_filename = os.path.join(bld.out_dir, 'doc', 'geany.html')
472     if bld.env['RST2HTML']:
473         rst2html = bld.env['RST2HTML']
474         bld(
475             source  = ['doc/geany.txt'],
476             deps    = ['doc/geany.css'],
477             target  = 'doc/geany.html',
478             name    = 'geany.html',
479             cwd     = os.path.join(bld.path.abspath(), 'doc'),
480             rule    = '%s  -stg --stylesheet=geany.css geany.txt %s' % (rst2html, html_doc_filename))
482     # geany.pc
483     if is_win32:
484         # replace backward slashes by forward slashes as they could be interepreted as escape
485         # characters
486         geany_pc_prefix = bld.env['PREFIX'].replace('\\', '/')
487     else:
488         geany_pc_prefix = bld.env['PREFIX']
489     bld(
490         source          = 'geany.pc.in',
491         dct             = {'VERSION': VERSION,
492                            'DEPENDENCIES': '%s >= %s glib-2.0 >= %s' % \
493                                 (bld.env['gtk_package_name'],
494                                  bld.env['minimum_gtk_version'],
495                                  MINIMUM_GLIB_VERSION),
496                            'prefix': geany_pc_prefix,
497                            'exec_prefix': '${prefix}',
498                            'libdir': '${exec_prefix}/lib',
499                            'includedir': '${prefix}/include',
500                            'datarootdir': '${prefix}/share',
501                            'datadir': '${datarootdir}',
502                            'localedir': '${datarootdir}/locale'})
504     if not is_win32:
505         # geany.desktop
506         if bld.env['INTLTOOL']:
507             bld(
508                 features        = 'intltool_in',
509                 source          = 'geany.desktop.in',
510                 flags           = ['-d', '-q', '-u', '-c'],
511                 install_path    = '${DATADIR}/applications')
513         # geany.1
514         bld(
515             features        = 'subst',
516             source          = 'doc/geany.1.in',
517             target          = 'geany.1',
518             dct             = {'VERSION': VERSION,
519                                 'GEANY_DATA_DIR': bld.env['DATADIR'] + '/geany'},
520             install_path    = '${MANDIR}/man1')
522         # geany.spec
523         bld(
524             features        = 'subst',
525             source          = 'geany.spec.in',
526             target          = 'geany.spec',
527             install_path    = None,
528             dct             = {'VERSION': VERSION})
530         # Doxyfile
531         bld(
532             features        = 'subst',
533             source          = 'doc/Doxyfile.in',
534             target          = 'doc/Doxyfile',
535             install_path    = None,
536             dct             = {'VERSION': VERSION,
537                                'top_builddir': bld.out_dir,
538                                'top_srcdir': bld.top_dir,})
540     ###
541     # Install files
542     ###
543     # Headers
544     bld.install_files('${PREFIX}/include/geany', '''
545         src/app.h src/document.h src/editor.h src/encodings.h src/filetypes.h src/geany.h
546         src/highlighting.h src/keybindings.h src/msgwindow.h src/plugindata.h
547         src/prefs.h src/project.h src/search.h src/stash.h src/support.h
548         src/templates.h src/toolbar.h src/ui_utils.h src/utils.h src/build.h src/gtkcompat.h
549         plugins/geanyplugin.h plugins/geanyfunctions.h''')
550     bld.install_files('${PREFIX}/include/geany/scintilla', '''
551         scintilla/include/SciLexer.h scintilla/include/Scintilla.h
552         scintilla/include/Scintilla.iface scintilla/include/ScintillaWidget.h ''')
553     bld.install_files('${PREFIX}/include/geany/tagmanager', '''
554         tagmanager/src/tm_source_file.h
555         tagmanager/src/tm_tag.h
556         tagmanager/src/tm_tagmanager.h
557         tagmanager/src/tm_workspace.h ''')
558     # Docs
559     base_dir = '${PREFIX}' if is_win32 else '${DOCDIR}'
560     ext = '.txt' if is_win32 else ''
561     for filename in 'AUTHORS ChangeLog COPYING README NEWS THANKS TODO'.split():
562         basename = _uc_first(filename, bld)
563         destination_filename = '%s%s' % (basename, ext)
564         destination = os.path.join(base_dir, destination_filename)
565         bld.install_as(destination, filename)
567     # install HTML documentation only if it exists, i.e. it was built before
568     # local_html_doc_filename supports installing HTML doc from in-tree geany.html if it exists
569     local_html_doc_filename = os.path.join(bld.path.abspath(), 'doc', 'geany.html')
570     if os.path.exists(html_doc_filename) or os.path.exists(local_html_doc_filename):
571         html_dir = '' if is_win32 else 'html/'
572         html_name = 'Manual.html' if is_win32 else 'index.html'
573         start_dir = bld.path.find_dir('doc/images')
574         bld.install_files('${DOCDIR}/%simages' % html_dir, start_dir.ant_glob('*.png'), cwd=start_dir)
575         bld.install_as('${DOCDIR}/%s%s' % (html_dir, html_name), 'doc/geany.html')
577     bld.install_as('${DOCDIR}/%s' % _uc_first('manual.txt', bld), 'doc/geany.txt')
578     bld.install_as('${DOCDIR}/ScintillaLicense.txt', 'scintilla/License.txt')
579     if is_win32:
580         bld.install_as('${DOCDIR}/ReadMe.I18n.txt', 'README.I18N')
581         bld.install_as('${DOCDIR}/Hacking.txt', 'HACKING')
582     # Data
583     data_dir = '' if is_win32 else 'geany'
584     start_dir = bld.path.find_dir('data')
585     bld.install_as('${DATADIR}/%s/GPL-2' % data_dir, 'COPYING')
586     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('filetype*'), cwd=start_dir)
587     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('*.tags'), cwd=start_dir)
588     bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.glade')
589     bld.install_files('${DATADIR}/%s' % data_dir, 'data/snippets.conf')
590     bld.install_files('${DATADIR}/%s' % data_dir, 'data/ui_toolbar.xml')
591     if bld.env['use_gtk3']:
592         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.css')
593     else:
594         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.gtkrc')
596     start_dir = bld.path.find_dir('data/colorschemes')
597     template_dest = '${DATADIR}/%s/colorschemes' % data_dir
598     bld.install_files(template_dest, start_dir.ant_glob('*'), cwd=start_dir)
599     start_dir = bld.path.find_dir('data/templates')
600     template_dest = '${DATADIR}/%s/templates' % data_dir
601     bld.install_files(template_dest, start_dir.ant_glob('**/*'), cwd=start_dir, relative_trick=True)
602     # Icons
603     for dest, srcs in geany_icons.items():
604         dest_dir = os.path.join('${PREFIX}/share/icons' if is_win32 else '${DATADIR}/icons', dest)
605         bld.install_files(dest_dir, srcs, cwd=bld.path.find_dir('icons'))
606     # install theme indexes on Windows
607     if is_win32:
608         for dest, srcs in geany_icons_indexes.items():
609             bld.install_files(os.path.join('${PREFIX}/share/icons', dest), srcs, cwd=bld.path.find_dir('icons'))
612 def distclean(ctx):
613     Scripting.distclean(ctx)
614     _remove_linguas_file()
617 def _remove_linguas_file():
618     # remove LINGUAS file as well
619     try:
620         os.unlink(LINGUAS_FILE)
621     except OSError:
622         pass
625 @feature('linguas')
626 @before_method('apply_intltool_po')
627 def write_linguas_file(self):
628     if os.path.exists(LINGUAS_FILE):
629         return
630     linguas = ''
631     if 'LINGUAS' in self.env:
632         files = self.env['LINGUAS']
633         for po_filename in files.split(' '):
634             if os.path.exists('po/%s.po' % po_filename):
635                 linguas += '%s ' % po_filename
636     else:
637         files = os.listdir('%s/po' % self.path.abspath())
638         files.sort()
639         for filename in files:
640             if filename.endswith('.po'):
641                 linguas += '%s ' % filename[:-3]
642     file_h = open(LINGUAS_FILE, 'w')
643     file_h.write('# This file is autogenerated. Do not edit.\n%s\n' % linguas)
644     file_h.close()
647 def _post_install(ctx):
648     is_win32 = _target_is_win32(ctx)
649     if is_win32:
650         return
651     for d in 'hicolor', 'Tango':
652         theme_dir = Utils.subst_vars('${DATADIR}/icons/' + d, ctx.env)
653         icon_cache_updated = False
654         if not ctx.options.destdir:
655             ctx.exec_command('gtk-update-icon-cache -q -f -t %s' % theme_dir)
656             Logs.pprint('GREEN', 'GTK icon cache updated.')
657             icon_cache_updated = True
658         if not icon_cache_updated:
659             Logs.pprint('YELLOW', 'Icon cache not updated. After install, run this:')
660             Logs.pprint('YELLOW', 'gtk-update-icon-cache -q -f -t %s' % theme_dir)
663 def updatepo(ctx):
664     """update the message catalogs for internationalization"""
665     potfile = '%s.pot' % APPNAME
666     os.chdir('%s/po' % top)
667     try:
668         try:
669             old_size = os.stat(potfile).st_size
670         except OSError:
671             old_size = 0
672         ctx.exec_command('intltool-update --pot -g %s' % APPNAME)
673         size_new = os.stat(potfile).st_size
674         if size_new != old_size:
675             Logs.pprint('CYAN', 'Updated POT file.')
676             Logs.pprint('CYAN', 'Updating translations')
677             ret = ctx.exec_command('intltool-update -r -g %s' % APPNAME)
678             if ret != 0:
679                 Logs.pprint('RED', 'Updating translations failed')
680         else:
681             Logs.pprint('CYAN', 'POT file is up to date.')
682     except OSError:
683         Logs.pprint('RED', 'Failed to generate pot file.')
686 def apidoc(ctx):
687     """generate API reference documentation"""
688     ctx = BuildContext()  # create our own context to have ctx.top_dir
689     basedir = ctx.top_dir
690     doxygen = _find_program(ctx, 'doxygen')
691     doxyfile = '%s/doc/Doxyfile' % ctx.out_dir
692     Logs.pprint('CYAN', 'Generating API documentation')
693     ret = ctx.exec_command('%s %s' % (doxygen, doxyfile))
694     if ret != 0:
695         raise WafError('Generating API documentation failed')
698 def hackingdoc(ctx):
699     """generate HACKING documentation"""
700     ctx = BuildContext()  # create our own context to have ctx.top_dir
701     Logs.pprint('CYAN', 'Generating HACKING documentation')
702     cmd = _find_rst2html(ctx)
703     hacking_file = os.path.join(ctx.top_dir, 'HACKING')
704     hacking_html_file = os.path.join(ctx.top_dir, 'doc', 'hacking.html')
705     stylesheet = os.path.join(ctx.top_dir, 'doc', 'geany.css')
706     ret = ctx.exec_command('%s  -stg --stylesheet=%s %s %s' % (
707         cmd, stylesheet, hacking_file, hacking_html_file))
708     if ret != 0:
709         raise WafError('Generating HACKING documentation failed')
712 def _find_program(ctx, cmd, **kw):
713     def noop(*args):
714         pass
716     if ctx is None or not isinstance(ctx, ConfigurationContext):
717         ctx = ConfigurationContext()
718         ctx.to_log = noop
719         ctx.msg = noop
720     return ctx.find_program(cmd, **kw)
723 def _find_rst2html(ctx):
724     cmds = ['rst2html', 'rst2html2']
725     for command in cmds:
726         cmd = _find_program(ctx, command, mandatory=False, exts=',.py')
727         if cmd:
728             break
729     if not cmd:
730         raise WafError(
731             'rst2html.py could not be found. Please install the Python docutils package.')
732     return cmd
735 def _add_define_to_env(conf, key):
736     value = conf.get_define(key)
737     # strip quotes
738     value = value.replace('"', '')
739     conf.env[key] = value
742 def _add_to_env_and_define(conf, key, value, quote=False):
743     conf.define(key, value, quote)
744     conf.env[key] = value
747 def _define_from_opt(conf, define_name, opt_value, default_value, quote=1):
748     value = default_value
749     if opt_value:
750         if isinstance(opt_value, bool):
751             opt_value = 1
752         value = opt_value
754     if value is not None:
755         _add_to_env_and_define(conf, define_name, value, quote)
756     else:
757         conf.undefine(define_name)
760 def _get_git_rev(conf):
761     if conf.options.no_scm:
762         return
764     if not os.path.isdir('.git'):
765         return
767     try:
768         cmd = 'git rev-parse --short --revs-only HEAD'
769         revision = conf.cmd_and_log(cmd).strip()
770     except WafError:
771         return None
772     else:
773         return revision
776 def _load_intltool_if_available(conf):
777     try:
778         conf.load('intltool')
779         if 'LINGUAS' in os.environ:
780             conf.env['LINGUAS'] = os.environ['LINGUAS']
781     except WafError:
782         # on Windows, we don't hard depend on intltool, on all other platforms raise an error
783         if not _target_is_win32(conf):
784             raise
787 def _target_is_win32(ctx):
788     if 'is_win32' in ctx.env:
789         # cached
790         return ctx.env['is_win32']
791     is_win32 = None
792     if sys.platform == 'win32':
793         is_win32 = True
794     if is_win32 is None:
795         if ctx.env and 'CC' in ctx.env:
796             env_cc = ctx.env['CC']
797             if not isinstance(env_cc, str):
798                 env_cc = ''.join(env_cc)
799             is_win32 = (env_cc.find('mingw') != -1)
800     if is_win32 is None:
801         is_win32 = False
802     # cache for future checks
803     ctx.env['is_win32'] = is_win32
804     return is_win32
807 def _uc_first(string, ctx):
808     if _target_is_win32(ctx):
809         return string.title()
810     return string