Initial packaging for Tizen
[profile/ivi/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   g_type_init ();
49
50   %(init_sections)s
51
52   if (argc != 2 || !g_str_has_prefix (argv[1], introspect_dump_prefix))
53     {
54       g_printerr ("Usage: %%s --introspect-dump=input,output", argv[0]);
55       exit (1);
56     }
57
58   if (!dump_irepository (argv[1] + strlen(introspect_dump_prefix), &error))
59     {
60       g_printerr ("%%s\\n", error->message);
61       exit (1);
62     }
63   exit (0);
64 }
65 """
66
67
68 class CompilerError(Exception):
69     pass
70
71
72 class LinkerError(Exception):
73     pass
74
75
76 class DumpCompiler(object):
77
78     def __init__(self, options, get_type_functions, error_quark_functions):
79         self._options = options
80         self._get_type_functions = get_type_functions
81         self._error_quark_functions = error_quark_functions
82
83         self._compiler_cmd = os.environ.get('CC', 'gcc')
84         self._linker_cmd = os.environ.get('CC', self._compiler_cmd)
85         self._pkgconfig_cmd = os.environ.get('PKG_CONFIG', 'pkg-config')
86
87         self._uninst_srcdir = os.environ.get(
88             'UNINSTALLED_INTROSPECTION_SRCDIR')
89         self._packages = ['gio-2.0 gthread-2.0 gmodule-2.0']
90         self._packages.extend(options.packages)
91
92     # Public API
93
94     def run(self):
95         # We have to use the current directory to work around Unix
96         # sysadmins who mount /tmp noexec
97         tmpdir = tempfile.mkdtemp('', 'tmp-introspect', dir=os.getcwd())
98
99         tpl_args = {}
100         if self._uninst_srcdir is not None:
101             gdump_path = os.path.join(self._uninst_srcdir, 'girepository', 'gdump.c')
102         else:
103             gdump_path = os.path.join(os.path.join(DATADIR), 'gobject-introspection-1.0',
104                                       'gdump.c')
105         if not os.path.isfile(gdump_path):
106             raise SystemExit("Couldn't find %r" % (gdump_path, ))
107         gdump_file = open(gdump_path)
108         gdump_contents = gdump_file.read()
109         gdump_file.close()
110         tpl_args['gdump_include'] = gdump_contents
111         tpl_args['init_sections'] = "\n".join(self._options.init_sections)
112
113         c_path = self._generate_tempfile(tmpdir, '.c')
114         f = open(c_path, 'w')
115         f.write(_PROGRAM_TEMPLATE % tpl_args)
116
117         # We need to reference our get_type and error_quark functions
118         # to make sure they are pulled in at the linking stage if the
119         # library is a static library rather than a shared library.
120         if len(self._get_type_functions) > 0:
121             for func in self._get_type_functions:
122                 f.write("extern GType " + func + "(void);\n")
123             f.write("GType (*GI_GET_TYPE_FUNCS_[])(void) = {\n")
124             first = True
125             for func in self._get_type_functions:
126                 if first:
127                     first = False
128                 else:
129                     f.write(",\n")
130                 f.write("  " + func)
131             f.write("\n};\n")
132         if len(self._error_quark_functions) > 0:
133             for func in self._error_quark_functions:
134                 f.write("extern GQuark " + func + "(void);\n")
135             f.write("GQuark (*GI_ERROR_QUARK_FUNCS_[])(void) = {\n")
136             first = True
137             for func in self._error_quark_functions:
138                 if first:
139                     first = False
140                 else:
141                     f.write(",\n")
142                 f.write("  " + func)
143             f.write("\n};\n")
144         f.close()
145
146         o_path = self._generate_tempfile(tmpdir, '.o')
147
148         if os.name == 'nt':
149             ext = 'exe'
150         else:
151             ext = ''
152
153         bin_path = self._generate_tempfile(tmpdir, ext)
154
155         try:
156             self._compile(o_path, c_path)
157         except CompilerError, e:
158             if not utils.have_debug_flag('save-temps'):
159                 shutil.rmtree(tmpdir)
160             raise SystemExit('compilation of temporary binary failed:' + str(e))
161
162         try:
163             self._link(bin_path, o_path)
164         except LinkerError, e:
165             if not utils.have_debug_flag('save-temps'):
166                 shutil.rmtree(tmpdir)
167             raise SystemExit('linking of temporary binary failed: ' + str(e))
168
169         return IntrospectionBinary([bin_path], tmpdir)
170
171     # Private API
172
173     def _generate_tempfile(self, tmpdir, suffix=''):
174         tmpl = '%s-%s%s' % (self._options.namespace_name,
175                             self._options.namespace_version, suffix)
176         return os.path.join(tmpdir, tmpl)
177
178     def _run_pkgconfig(self, flag):
179         proc = subprocess.Popen(
180             [self._pkgconfig_cmd, flag] + self._packages,
181             stdout=subprocess.PIPE)
182         return proc.communicate()[0].split()
183
184     def _compile(self, output, *sources):
185         # Not strictly speaking correct, but easier than parsing shell
186         args = self._compiler_cmd.split()
187         # Do not add -Wall when using init code as we do not include any
188         # header of the library being introspected
189         if self._compiler_cmd == 'gcc' and not self._options.init_sections:
190             args.append('-Wall')
191         pkgconfig_flags = self._run_pkgconfig('--cflags')
192         args.extend(pkgconfig_flags)
193         cflags = os.environ.get('CFLAGS')
194         if (cflags):
195             for iflag in cflags.split():
196                 args.append(iflag)
197         for include in self._options.cpp_includes:
198             args.append('-I' + include)
199         args.extend(['-c', '-o', output])
200         for source in sources:
201             if not os.path.exists(source):
202                 raise CompilerError(
203                     "Could not find c source file: %s" % (source, ))
204         args.extend(list(sources))
205         if not self._options.quiet:
206             print "g-ir-scanner: compile: %s" % (
207                 subprocess.list2cmdline(args), )
208             sys.stdout.flush()
209         try:
210             subprocess.check_call(args)
211         except subprocess.CalledProcessError, e:
212             raise CompilerError(e)
213
214     def _link(self, output, *sources):
215         args = []
216         libtool = utils.get_libtool_command(self._options)
217         if libtool:
218             args.extend(libtool)
219             args.append('--mode=link')
220             args.append('--tag=CC')
221             if self._options.quiet:
222                 args.append('--silent')
223
224         args.extend(self._linker_cmd.split())
225         args.extend(['-o', output])
226         if libtool:
227             if os.name == 'nt':
228                 args.append('-export-all-symbols')
229             else:
230                 args.append('-export-dynamic')
231
232         cflags = os.environ.get('CFLAGS')
233         if (cflags):
234             for iflag in cflags.split():
235                 args.append(iflag)
236
237         # Make sure to list the library to be introspected first since it's
238         # likely to be uninstalled yet and we want the uninstalled RPATHs have
239         # priority (or we might run with installed library that is older)
240
241         for source in sources:
242             if not os.path.exists(source):
243                 raise CompilerError(
244                     "Could not find object file: %s" % (source, ))
245         args.extend(list(sources))
246
247         if not self._options.external_library:
248             self._add_link_internal_args(args, libtool)
249         else:
250             self._add_link_external_args(args)
251
252         if not self._options.quiet:
253             print "g-ir-scanner: link: %s" % (
254                 subprocess.list2cmdline(args), )
255             sys.stdout.flush()
256         try:
257             subprocess.check_call(args)
258         except subprocess.CalledProcessError, e:
259             raise LinkerError(e)
260
261     def _add_link_internal_args(self, args, libtool):
262         # An "internal" link is where the library to be introspected
263         # is being built in the current directory.
264
265         # Search the current directory first
266         args.append('-L.')
267
268         # https://bugzilla.gnome.org/show_bug.cgi?id=625195
269         if not libtool:
270             args.append('-Wl,-rpath=.')
271
272         for library in self._options.libraries:
273             if library.endswith(".la"): # explicitly specified libtool library
274                 args.append(library)
275             else:
276                 args.append('-l' + library)
277
278         for library_path in self._options.library_paths:
279             args.append('-L' + library_path)
280             if os.path.isabs(library_path):
281                 if libtool:
282                     args.append('-rpath')
283                     args.append(library_path)
284                 else:
285                     args.append('-Wl,-rpath=' + library_path)
286
287         args.extend(self._run_pkgconfig('--libs'))
288
289     def _add_link_external_args(self, args):
290         # An "external" link is where the library to be introspected
291         # is installed on the system; this case is used for the scanning
292         # of GLib in gobject-introspection itself.
293
294         args.extend(self._run_pkgconfig('--libs'))
295         for library in self._options.libraries:
296             if library.endswith(".la"): # explicitly specified libtool library
297                 args.append(library)
298             else:
299                 args.append('-l' + library)
300
301 def compile_introspection_binary(options, get_type_functions,
302                                  error_quark_functions):
303     dc = DumpCompiler(options, get_type_functions, error_quark_functions)
304     return dc.run()