Imported Upstream version 1.38.0
[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         args = self._compiler_cmd.split()
93         if 'cl.exe' in args or 'cl' in args:
94             self._pkgconfig_msvc_flags = '--msvc-syntax'
95         self._uninst_srcdir = os.environ.get(
96             'UNINSTALLED_INTROSPECTION_SRCDIR')
97         self._packages = ['gio-2.0 gmodule-2.0']
98         self._packages.extend(options.packages)
99
100     # Public API
101
102     def run(self):
103         # We have to use the current directory to work around Unix
104         # sysadmins who mount /tmp noexec
105         tmpdir = tempfile.mkdtemp('', 'tmp-introspect', dir=os.getcwd())
106
107         tpl_args = {}
108         if self._uninst_srcdir is not None:
109             gdump_path = os.path.join(self._uninst_srcdir, 'girepository', 'gdump.c')
110         else:
111             gdump_path = os.path.join(os.path.join(DATADIR), 'gobject-introspection-1.0',
112                                       'gdump.c')
113         if not os.path.isfile(gdump_path):
114             raise SystemExit("Couldn't find %r" % (gdump_path, ))
115         gdump_file = open(gdump_path)
116         gdump_contents = gdump_file.read()
117         gdump_file.close()
118         tpl_args['gdump_include'] = gdump_contents
119         tpl_args['init_sections'] = "\n".join(self._options.init_sections)
120
121         c_path = self._generate_tempfile(tmpdir, '.c')
122         f = open(c_path, 'w')
123         f.write(_PROGRAM_TEMPLATE % tpl_args)
124
125         # We need to reference our get_type and error_quark functions
126         # to make sure they are pulled in at the linking stage if the
127         # library is a static library rather than a shared library.
128         if len(self._get_type_functions) > 0:
129             for func in self._get_type_functions:
130                 f.write("extern GType " + func + "(void);\n")
131             f.write("GType (*GI_GET_TYPE_FUNCS_[])(void) = {\n")
132             first = True
133             for func in self._get_type_functions:
134                 if first:
135                     first = False
136                 else:
137                     f.write(",\n")
138                 f.write("  " + func)
139             f.write("\n};\n")
140         if len(self._error_quark_functions) > 0:
141             for func in self._error_quark_functions:
142                 f.write("extern GQuark " + func + "(void);\n")
143             f.write("GQuark (*GI_ERROR_QUARK_FUNCS_[])(void) = {\n")
144             first = True
145             for func in self._error_quark_functions:
146                 if first:
147                     first = False
148                 else:
149                     f.write(",\n")
150                 f.write("  " + func)
151             f.write("\n};\n")
152         f.close()
153
154         # Microsoft compilers generate intermediate .obj files
155         # during compilation, unlike .o files like GCC and others
156         if self._pkgconfig_msvc_flags:
157             o_path = self._generate_tempfile(tmpdir, '.obj')
158         else:
159             o_path = self._generate_tempfile(tmpdir, '.o')
160
161         if os.name == 'nt':
162             ext = 'exe'
163         else:
164             ext = ''
165
166         bin_path = self._generate_tempfile(tmpdir, ext)
167
168         try:
169             self._compile(o_path, c_path)
170         except CompilerError as e:
171             if not utils.have_debug_flag('save-temps'):
172                 shutil.rmtree(tmpdir)
173             raise SystemExit('compilation of temporary binary failed:' + str(e))
174
175         try:
176             self._link(bin_path, o_path)
177         except LinkerError as e:
178             if not utils.have_debug_flag('save-temps'):
179                 shutil.rmtree(tmpdir)
180             raise SystemExit('linking of temporary binary failed: ' + str(e))
181
182         return IntrospectionBinary([bin_path], tmpdir)
183
184     # Private API
185
186     def _generate_tempfile(self, tmpdir, suffix=''):
187         tmpl = '%s-%s%s' % (self._options.namespace_name,
188                             self._options.namespace_version, suffix)
189         return os.path.join(tmpdir, tmpl)
190
191     def _run_pkgconfig(self, flag):
192         # Enable the --msvc-syntax pkg-config flag when
193         # the Microsoft compiler is used
194         if self._pkgconfig_msvc_flags:
195             cmd = [self._pkgconfig_cmd, self._pkgconfig_msvc_flags, flag]
196         else:
197             cmd = [self._pkgconfig_cmd, flag]
198         proc = subprocess.Popen(
199             cmd + self._packages,
200             stdout=subprocess.PIPE)
201         return proc.communicate()[0].split()
202
203     def _compile(self, output, *sources):
204         # Not strictly speaking correct, but easier than parsing shell
205         args = self._compiler_cmd.split()
206         # Do not add -Wall when using init code as we do not include any
207         # header of the library being introspected
208         if self._compiler_cmd == 'gcc' and not self._options.init_sections:
209             args.append('-Wall')
210         # The Microsoft compiler uses different option flags for
211         # silencing warnings on deprecated function usage
212         if self._pkgconfig_msvc_flags:
213             args.append("-wd4996")
214         else:
215             args.append("-Wno-deprecated-declarations")
216         pkgconfig_flags = self._run_pkgconfig('--cflags')
217         args.extend(pkgconfig_flags)
218         cflags = os.environ.get('CFLAGS', '')
219         for cflag in cflags.split():
220             args.append(cflag)
221         for include in self._options.cpp_includes:
222             args.append('-I' + include)
223         # The Microsoft compiler uses different option flags for
224         # compilation result output
225         if self._pkgconfig_msvc_flags:
226             args.extend(['-c', '-Fe' + output, '-Fo' + output])
227         else:
228             args.extend(['-c', '-o', output])
229         for source in sources:
230             if not os.path.exists(source):
231                 raise CompilerError(
232                     "Could not find c source file: %s" % (source, ))
233         args.extend(list(sources))
234         if not self._options.quiet:
235             print "g-ir-scanner: compile: %s" % (
236                 subprocess.list2cmdline(args), )
237             sys.stdout.flush()
238         try:
239             subprocess.check_call(args)
240         except subprocess.CalledProcessError as e:
241             raise CompilerError(e)
242
243     def _link(self, output, *sources):
244         args = []
245         libtool = utils.get_libtool_command(self._options)
246         if libtool:
247             args.extend(libtool)
248             args.append('--mode=link')
249             args.append('--tag=CC')
250             if self._options.quiet:
251                 args.append('--silent')
252
253         args.extend(self._linker_cmd.split())
254         # We can use -o for the Microsoft compiler/linker,
255         # but it is considered deprecated usage with that
256         if self._pkgconfig_msvc_flags:
257             args.extend(['-Fe' + output])
258         else:
259             args.extend(['-o', output])
260         if libtool:
261             if os.name == 'nt':
262                 args.append('-export-all-symbols')
263             else:
264                 args.append('-export-dynamic')
265
266         cflags = os.environ.get('CFLAGS', '')
267         for cflag in cflags.split():
268             args.append(cflag)
269         ldflags = os.environ.get('LDFLAGS', '')
270         for ldflag in ldflags.split():
271             args.append(ldflag)
272
273         # Make sure to list the library to be introspected first since it's
274         # likely to be uninstalled yet and we want the uninstalled RPATHs have
275         # priority (or we might run with installed library that is older)
276
277         for source in sources:
278             if not os.path.exists(source):
279                 raise CompilerError(
280                     "Could not find object file: %s" % (source, ))
281         args.extend(list(sources))
282
283         if not self._options.external_library:
284             self._add_link_internal_args(args, libtool)
285         else:
286             self._add_link_external_args(args)
287
288         if not self._options.quiet:
289             print "g-ir-scanner: link: %s" % (
290                 subprocess.list2cmdline(args), )
291             sys.stdout.flush()
292         try:
293             subprocess.check_call(args)
294         except subprocess.CalledProcessError as e:
295             raise LinkerError(e)
296
297     def _add_link_internal_args(self, args, libtool):
298         # An "internal" link is where the library to be introspected
299         # is being built in the current directory.
300
301         # Search the current directory first
302         # (This flag is not supported nor needed for Visual C++)
303         if self._pkgconfig_msvc_flags == '':
304             args.append('-L.')
305
306         # https://bugzilla.gnome.org/show_bug.cgi?id=625195
307         if not libtool:
308             # We don't have -Wl,-rpath for Visual C++, and that's
309             # going to cause a problem.  Instead, link to internal
310             # libraries by deducing the .lib file name using
311             # the namespace name and version
312             if self._pkgconfig_msvc_flags:
313                 if self._options.namespace_version:
314                     args.append(str.lower(self._options.namespace_name) +
315                                 '-' +
316                                 self._options.namespace_version + '.lib')
317                 else:
318                     args.append(str.lower(self._options.namespace_name) + '.lib')
319             else:
320                 args.append('-Wl,-rpath=.')
321
322         # Ensure libraries are always linked as we are going to use ldd to work
323         # out their names later
324         if not libtool and self._pkgconfig_msvc_flags == '':
325             args.append('-Wl,--no-as-needed')
326
327         for library in self._options.libraries:
328             # Visual C++: We have the needed .lib files now, and we need to link
329             # to .lib files, not the .dll as the --library option specifies the
330             # .dll(s) the .gir file refers to
331             if self._pkgconfig_msvc_flags == '':
332                 if library.endswith(".la"):  # explicitly specified libtool library
333                     args.append(library)
334                 else:
335                     args.append('-l' + library)
336
337         for library_path in self._options.library_paths:
338             # Not used/needed on Visual C++, and -Wl,-rpath options
339             # will cause grief
340             if self._pkgconfig_msvc_flags == '':
341                 args.append('-L' + library_path)
342                 if os.path.isabs(library_path):
343                     if libtool:
344                         args.append('-rpath')
345                         args.append(library_path)
346                     else:
347                         args.append('-Wl,-rpath=' + library_path)
348
349         args.extend(self._run_pkgconfig('--libs'))
350
351     def _add_link_external_args(self, args):
352         # An "external" link is where the library to be introspected
353         # is installed on the system; this case is used for the scanning
354         # of GLib in gobject-introspection itself.
355
356         args.extend(self._run_pkgconfig('--libs'))
357         for library in self._options.libraries:
358             # The --library option on Windows pass in the .dll file(s) the
359             # .gir files refer to, so don't link to them on Visual C++
360             if self._pkgconfig_msvc_flags == '':
361                 if library.endswith(".la"):  # explicitly specified libtool library
362                     args.append(library)
363                 else:
364                     args.append('-l' + library)
365
366
367 def compile_introspection_binary(options, get_type_functions,
368                                  error_quark_functions):
369     dc = DumpCompiler(options, get_type_functions, error_quark_functions)
370     return dc.run()