Imported Upstream version 1.35.9
[platform/upstream/gobject-introspection.git] / giscanner / dumper.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008 Colin Walters
4 # Copyright (C) 2008 Johan Dahlin
5 #
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2 of the License, or (at your option) any later version.
10 #
11 # This library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # Lesser General Public License for more details.
15 #
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this library; if not, write to the
18 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 # Boston, MA 02111-1307, USA.
20 #
21
22 import os
23 import sys
24 import subprocess
25 import shutil
26 import tempfile
27
28 from .gdumpparser import IntrospectionBinary
29 from . import utils
30
31 # bugzilla.gnome.org/558436
32 # Compile a binary program which is then linked to a library
33 # we want to introspect, in order to call its get_type functions.
34
35 _PROGRAM_TEMPLATE = """/* This file is generated, do not edit */
36 #include <glib.h>
37 #include <string.h>
38 #include <stdlib.h>
39
40 %(gdump_include)s
41
42 int
43 main(int argc, char **argv)
44 {
45   GError *error = NULL;
46   const char *introspect_dump_prefix = "--introspect-dump=";
47
48 #if !GLIB_CHECK_VERSION(2,35,0)
49   g_type_init ();
50 #endif
51
52   %(init_sections)s
53
54   if (argc != 2 || !g_str_has_prefix (argv[1], introspect_dump_prefix))
55     {
56       g_printerr ("Usage: %%s --introspect-dump=input,output", argv[0]);
57       exit (1);
58     }
59
60   if (!dump_irepository (argv[1] + strlen(introspect_dump_prefix), &error))
61     {
62       g_printerr ("%%s\\n", error->message);
63       exit (1);
64     }
65   exit (0);
66 }
67 """
68
69
70 class CompilerError(Exception):
71     pass
72
73
74 class LinkerError(Exception):
75     pass
76
77
78 class DumpCompiler(object):
79
80     def __init__(self, options, get_type_functions, error_quark_functions):
81         self._options = options
82         self._get_type_functions = get_type_functions
83         self._error_quark_functions = error_quark_functions
84
85         self._compiler_cmd = os.environ.get('CC', 'gcc')
86         self._linker_cmd = os.environ.get('CC', self._compiler_cmd)
87         self._pkgconfig_cmd = os.environ.get('PKG_CONFIG', 'pkg-config')
88         self._pkgconfig_msvc_flags = ''
89         # Enable the --msvc-syntax pkg-config flag when
90         # the Microsoft compiler is used
91         # (This is the other way to check whether Visual C++ is used subsequently)
92         if 'cl' in self._compiler_cmd:
93             self._pkgconfig_msvc_flags = '--msvc-syntax'
94         self._uninst_srcdir = os.environ.get(
95             'UNINSTALLED_INTROSPECTION_SRCDIR')
96         self._packages = ['gio-2.0 gmodule-2.0']
97         self._packages.extend(options.packages)
98
99     # Public API
100
101     def run(self):
102         # We have to use the current directory to work around Unix
103         # sysadmins who mount /tmp noexec
104         tmpdir = tempfile.mkdtemp('', 'tmp-introspect', dir=os.getcwd())
105
106         tpl_args = {}
107         if self._uninst_srcdir is not None:
108             gdump_path = os.path.join(self._uninst_srcdir, 'girepository', 'gdump.c')
109         else:
110             gdump_path = os.path.join(os.path.join(DATADIR), 'gobject-introspection-1.0',
111                                       'gdump.c')
112         if not os.path.isfile(gdump_path):
113             raise SystemExit("Couldn't find %r" % (gdump_path, ))
114         gdump_file = open(gdump_path)
115         gdump_contents = gdump_file.read()
116         gdump_file.close()
117         tpl_args['gdump_include'] = gdump_contents
118         tpl_args['init_sections'] = "\n".join(self._options.init_sections)
119
120         c_path = self._generate_tempfile(tmpdir, '.c')
121         f = open(c_path, 'w')
122         f.write(_PROGRAM_TEMPLATE % tpl_args)
123
124         # We need to reference our get_type and error_quark functions
125         # to make sure they are pulled in at the linking stage if the
126         # library is a static library rather than a shared library.
127         if len(self._get_type_functions) > 0:
128             for func in self._get_type_functions:
129                 f.write("extern GType " + func + "(void);\n")
130             f.write("GType (*GI_GET_TYPE_FUNCS_[])(void) = {\n")
131             first = True
132             for func in self._get_type_functions:
133                 if first:
134                     first = False
135                 else:
136                     f.write(",\n")
137                 f.write("  " + func)
138             f.write("\n};\n")
139         if len(self._error_quark_functions) > 0:
140             for func in self._error_quark_functions:
141                 f.write("extern GQuark " + func + "(void);\n")
142             f.write("GQuark (*GI_ERROR_QUARK_FUNCS_[])(void) = {\n")
143             first = True
144             for func in self._error_quark_functions:
145                 if first:
146                     first = False
147                 else:
148                     f.write(",\n")
149                 f.write("  " + func)
150             f.write("\n};\n")
151         f.close()
152
153         # Microsoft compilers generate intermediate .obj files
154         # during compilation, unlike .o files like GCC and others
155         if self._pkgconfig_msvc_flags:
156             o_path = self._generate_tempfile(tmpdir, '.obj')
157         else:
158             o_path = self._generate_tempfile(tmpdir, '.o')
159
160         if os.name == 'nt':
161             ext = 'exe'
162         else:
163             ext = ''
164
165         bin_path = self._generate_tempfile(tmpdir, ext)
166
167         try:
168             self._compile(o_path, c_path)
169         except CompilerError, e:
170             if not utils.have_debug_flag('save-temps'):
171                 shutil.rmtree(tmpdir)
172             raise SystemExit('compilation of temporary binary failed:' + str(e))
173
174         try:
175             self._link(bin_path, o_path)
176         except LinkerError, e:
177             if not utils.have_debug_flag('save-temps'):
178                 shutil.rmtree(tmpdir)
179             raise SystemExit('linking of temporary binary failed: ' + str(e))
180
181         return IntrospectionBinary([bin_path], tmpdir)
182
183     # Private API
184
185     def _generate_tempfile(self, tmpdir, suffix=''):
186         tmpl = '%s-%s%s' % (self._options.namespace_name,
187                             self._options.namespace_version, suffix)
188         return os.path.join(tmpdir, tmpl)
189
190     def _run_pkgconfig(self, flag):
191         # Enable the --msvc-syntax pkg-config flag when
192         # the Microsoft compiler is used
193         if self._pkgconfig_msvc_flags:
194             cmd = [self._pkgconfig_cmd, self._pkgconfig_msvc_flags, flag]
195         else:
196             cmd = [self._pkgconfig_cmd, flag]
197         proc = subprocess.Popen(
198             cmd + self._packages,
199             stdout=subprocess.PIPE)
200         return proc.communicate()[0].split()
201
202     def _compile(self, output, *sources):
203         # Not strictly speaking correct, but easier than parsing shell
204         args = self._compiler_cmd.split()
205         # Do not add -Wall when using init code as we do not include any
206         # header of the library being introspected
207         if self._compiler_cmd == 'gcc' and not self._options.init_sections:
208             args.append('-Wall')
209         # The Microsoft compiler uses different option flags for
210         # silencing warnings on deprecated function usage
211         if self._pkgconfig_msvc_flags:
212             args.append("-wd4996")
213         else:
214             args.append("-Wno-deprecated-declarations")
215         pkgconfig_flags = self._run_pkgconfig('--cflags')
216         args.extend(pkgconfig_flags)
217         cflags = os.environ.get('CFLAGS', '')
218         for cflag in cflags.split():
219             args.append(cflag)
220         for include in self._options.cpp_includes:
221             args.append('-I' + include)
222         # The Microsoft compiler uses different option flags for
223         # compilation result output
224         if self._pkgconfig_msvc_flags:
225             args.extend(['-c', '-Fe'+output, '-Fo'+output])
226         else:
227             args.extend(['-c', '-o', output])
228         for source in sources:
229             if not os.path.exists(source):
230                 raise CompilerError(
231                     "Could not find c source file: %s" % (source, ))
232         args.extend(list(sources))
233         if not self._options.quiet:
234             print "g-ir-scanner: compile: %s" % (
235                 subprocess.list2cmdline(args), )
236             sys.stdout.flush()
237         try:
238             subprocess.check_call(args)
239         except subprocess.CalledProcessError, e:
240             raise CompilerError(e)
241
242     def _link(self, output, *sources):
243         args = []
244         libtool = utils.get_libtool_command(self._options)
245         if libtool:
246             args.extend(libtool)
247             args.append('--mode=link')
248             args.append('--tag=CC')
249             if self._options.quiet:
250                 args.append('--silent')
251
252         args.extend(self._linker_cmd.split())
253         # We can use -o for the Microsoft compiler/linker,
254         # but it is considered deprecated usage with that
255         if self._pkgconfig_msvc_flags:
256             args.extend(['-Fe'+output])
257         else:
258             args.extend(['-o', output])
259         if libtool:
260             if os.name == 'nt':
261                 args.append('-export-all-symbols')
262             else:
263                 args.append('-export-dynamic')
264
265         cflags = os.environ.get('CFLAGS', '')
266         for cflag in cflags.split():
267             args.append(cflag)
268         ldflags = os.environ.get('LDFLAGS', '')
269         for ldflag in ldflags.split():
270             args.append(ldflag)
271
272         # Make sure to list the library to be introspected first since it's
273         # likely to be uninstalled yet and we want the uninstalled RPATHs have
274         # priority (or we might run with installed library that is older)
275
276         for source in sources:
277             if not os.path.exists(source):
278                 raise CompilerError(
279                     "Could not find object file: %s" % (source, ))
280         args.extend(list(sources))
281
282         if not self._options.external_library:
283             self._add_link_internal_args(args, libtool)
284         else:
285             self._add_link_external_args(args)
286
287         if not self._options.quiet:
288             print "g-ir-scanner: link: %s" % (
289                 subprocess.list2cmdline(args), )
290             sys.stdout.flush()
291         try:
292             subprocess.check_call(args)
293         except subprocess.CalledProcessError, e:
294             raise LinkerError(e)
295
296     def _add_link_internal_args(self, args, libtool):
297         # An "internal" link is where the library to be introspected
298         # is being built in the current directory.
299
300         # Search the current directory first
301         # (This flag is not supported nor needed for Visual C++)
302         if self._pkgconfig_msvc_flags == '':
303             args.append('-L.')
304
305         # https://bugzilla.gnome.org/show_bug.cgi?id=625195
306         if not libtool:
307             # We don't have -Wl,-rpath for Visual C++, and that's
308             # going to cause a problem.  Instead, link to internal
309             # libraries by deducing the .lib file name using
310             # the namespace name and version
311             if self._pkgconfig_msvc_flags:
312                 if self._options.namespace_version:
313                     args.append(str.lower(self._options.namespace_name) +
314                                 '-' +
315                                 self._options.namespace_version+'.lib')
316                 else:
317                     args.append(str.lower(self._options.namespace_name)+'.lib')
318             else:
319                 args.append('-Wl,-rpath=.')
320
321         for library in self._options.libraries:
322             # Visual C++: We have the needed .lib files now, and we need to link
323             # to .lib files, not the .dll as the --library option specifies the
324             # .dll(s) the .gir file refers to
325             if self._pkgconfig_msvc_flags == '':
326                 if library.endswith(".la"): # explicitly specified libtool library
327                     args.append(library)
328                 else:
329                     args.append('-l' + library)
330
331         for library_path in self._options.library_paths:
332             # Not used/needed on Visual C++, and -Wl,-rpath options
333             # will cause grief
334             if self._pkgconfig_msvc_flags == '':
335                 args.append('-L' + library_path)
336                 if os.path.isabs(library_path):
337                     if libtool:
338                         args.append('-rpath')
339                         args.append(library_path)
340                     else:
341                         args.append('-Wl,-rpath=' + library_path)
342
343         args.extend(self._run_pkgconfig('--libs'))
344
345     def _add_link_external_args(self, args):
346         # An "external" link is where the library to be introspected
347         # is installed on the system; this case is used for the scanning
348         # of GLib in gobject-introspection itself.
349
350         args.extend(self._run_pkgconfig('--libs'))
351         for library in self._options.libraries:
352             # The --library option on Windows pass in the .dll file(s) the
353             # .gir files refer to, so don't link to them on Visual C++
354             if self._pkgconfig_msvc_flags == '':
355                 if library.endswith(".la"): # explicitly specified libtool library
356                     args.append(library)
357                 else:
358                     args.append('-l' + library)
359
360 def compile_introspection_binary(options, get_type_functions,
361                                  error_quark_functions):
362     dc = DumpCompiler(options, get_type_functions, error_quark_functions)
363     return dc.run()