2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008 Colin Walters
4 # Copyright (C) 2008 Johan Dahlin
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.
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.
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.
28 from .gdumpparser import IntrospectionBinary
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.
35 _PROGRAM_TEMPLATE = """/* This file is generated, do not edit */
43 main(int argc, char **argv)
46 const char *introspect_dump_prefix = "--introspect-dump=";
48 #if !GLIB_CHECK_VERSION(2,35,0)
54 if (argc != 2 || !g_str_has_prefix (argv[1], introspect_dump_prefix))
56 g_printerr ("Usage: %%s --introspect-dump=input,output", argv[0]);
60 if (!dump_irepository (argv[1] + strlen(introspect_dump_prefix), &error))
62 g_printerr ("%%s\\n", error->message);
70 class CompilerError(Exception):
74 class LinkerError(Exception):
78 class DumpCompiler(object):
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
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)
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())
108 if self._uninst_srcdir is not None:
109 gdump_path = os.path.join(self._uninst_srcdir, 'girepository', 'gdump.c')
111 gdump_path = os.path.join(os.path.join(DATADIR), 'gobject-introspection-1.0',
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()
118 tpl_args['gdump_include'] = gdump_contents
119 tpl_args['init_sections'] = "\n".join(self._options.init_sections)
121 c_path = self._generate_tempfile(tmpdir, '.c')
122 f = open(c_path, 'w')
123 f.write(_PROGRAM_TEMPLATE % tpl_args)
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")
133 for func in self._get_type_functions:
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")
145 for func in self._error_quark_functions:
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')
159 o_path = self._generate_tempfile(tmpdir, '.o')
166 bin_path = self._generate_tempfile(tmpdir, ext)
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))
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))
182 return IntrospectionBinary([bin_path], tmpdir)
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)
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]
197 cmd = [self._pkgconfig_cmd, flag]
198 proc = subprocess.Popen(
199 cmd + self._packages,
200 stdout=subprocess.PIPE)
201 return proc.communicate()[0].split()
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:
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")
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():
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])
228 args.extend(['-c', '-o', output])
229 for source in sources:
230 if not os.path.exists(source):
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), )
239 subprocess.check_call(args)
240 except subprocess.CalledProcessError as e:
241 raise CompilerError(e)
243 def _link(self, output, *sources):
245 libtool = utils.get_libtool_command(self._options)
248 args.append('--mode=link')
249 args.append('--tag=CC')
250 if self._options.quiet:
251 args.append('--silent')
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])
259 args.extend(['-o', output])
262 args.append('-export-all-symbols')
264 args.append('-export-dynamic')
266 cflags = os.environ.get('CFLAGS', '')
267 for cflag in cflags.split():
269 ldflags = os.environ.get('LDFLAGS', '')
270 for ldflag in ldflags.split():
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)
277 for source in sources:
278 if not os.path.exists(source):
280 "Could not find object file: %s" % (source, ))
281 args.extend(list(sources))
283 if not self._options.external_library:
284 self._add_link_internal_args(args, libtool)
286 self._add_link_external_args(args)
288 if not self._options.quiet:
289 print "g-ir-scanner: link: %s" % (
290 subprocess.list2cmdline(args), )
293 subprocess.check_call(args)
294 except subprocess.CalledProcessError as e:
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.
301 # Search the current directory first
302 # (This flag is not supported nor needed for Visual C++)
303 if self._pkgconfig_msvc_flags == '':
306 # https://bugzilla.gnome.org/show_bug.cgi?id=625195
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) +
316 self._options.namespace_version + '.lib')
318 args.append(str.lower(self._options.namespace_name) + '.lib')
320 args.append('-Wl,-rpath=.')
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')
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
335 args.append('-l' + library)
337 for library_path in self._options.library_paths:
338 # Not used/needed on Visual C++, and -Wl,-rpath options
340 if self._pkgconfig_msvc_flags == '':
341 args.append('-L' + library_path)
342 if os.path.isabs(library_path):
344 args.append('-rpath')
345 args.append(library_path)
347 args.append('-Wl,-rpath=' + library_path)
349 args.extend(self._run_pkgconfig('--libs'))
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.
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
364 args.append('-l' + library)
367 def compile_introspection_binary(options, get_type_functions,
368 error_quark_functions):
369 dc = DumpCompiler(options, get_type_functions, error_quark_functions)