3 # GObject-Introspection - a framework for introspecting GObject libraries
4 # Copyright (C) 2008 Johan Dahlin
5 # Copyright (C) 2009 Red Hat, Inc.
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
29 from giscanner.annotationparser import AnnotationParser
30 from giscanner.ast import Include
31 from giscanner.cachestore import CacheStore
32 from giscanner.dumper import compile_introspection_binary
33 from giscanner.gdumpparser import GDumpParser, IntrospectionBinary
34 from giscanner.minixpath import xpath_assert
35 from giscanner.sourcescanner import SourceScanner
36 from giscanner.shlibs import resolve_shlibs
37 from giscanner.transformer import Transformer
38 from giscanner.maintransformer import MainTransformer
39 from giscanner.introspectablepass import IntrospectablePass
40 from giscanner.girparser import GIRParser
41 from giscanner.girwriter import GIRWriter
42 from giscanner.utils import files_are_identical
44 def _get_option_parser():
45 parser = optparse.OptionParser('%prog [options] sources')
46 parser.add_option('', "--quiet",
47 action="store_true", dest="quiet",
49 help="If passed, do not print details of normal" \
51 parser.add_option("", "--format",
52 action="store", dest="format",
54 help="format to use, one of gidl, gir")
55 parser.add_option("-i", "--include",
56 action="append", dest="includes", default=[],
57 help="include types for other gidls")
58 parser.add_option('', "--generate-typelib-tests",
59 action="store", dest="test_codegen", default=None,
60 help="Generate test code for given namespace,output.h,output.c")
61 parser.add_option('', "--passthrough-gir",
62 action="store", dest="passthrough_gir", default=None,
63 help="Parse and re-output the specified GIR")
64 parser.add_option('', "--reparse-validate",
65 action="store_true", dest="reparse_validate_gir", default=False,
66 help="After generating the GIR, re-parse it to ensure validity")
67 parser.add_option("", "--add-include-path",
68 action="append", dest="include_paths", default=[],
69 help="include paths for other GIR files")
70 parser.add_option("", "--program",
71 action="store", dest="program", default=None,
72 help="program to execute")
73 parser.add_option("", "--program-arg",
74 action="append", dest="program_args", default=[],
75 help="extra arguments to program")
76 parser.add_option("", "--libtool",
77 action="store", dest="libtool_path", default=None,
78 help="full path to libtool")
79 parser.add_option("", "--no-libtool",
80 action="store_true", dest="nolibtool", default=False,
81 help="do not use libtool")
82 parser.add_option("-l", "--library",
83 action="append", dest="libraries", default=[],
84 help="libraries of this unit")
85 parser.add_option("-L", "--library-path",
86 action="append", dest="library_paths", default=[],
87 help="directories to search for libraries")
88 parser.add_option("-n", "--namespace",
89 action="store", dest="namespace_name",
90 help=("name of namespace for this unit, also "
91 "used to compute --identifier-prefix and --symbol-prefix"))
92 parser.add_option("", "--nsversion",
93 action="store", dest="namespace_version",
94 help="version of namespace for this unit")
95 parser.add_option("", "--identifier-prefix",
96 action="append", dest="identifier_prefixes", default=[],
97 help="""Remove this prefix from C identifiers (structure typedefs, etc.).
98 May be specified multiple times. This is also used as the default for --symbol-prefix if
99 the latter is not specified.""")
100 parser.add_option("", "--symbol-prefix",
101 action="append", dest="symbol_prefixes", default=[],
102 help="Remove this prefix from C symbols (function names)")
103 parser.add_option("", "--accept-unprefixed",
104 action="store_true", dest="accept_unprefixed", default=False,
105 help="If specified, accept symbols and identifiers that do not match the namespace prefix.")
106 parser.add_option("", "--add-init-section",
107 action="append", dest="init_sections", default=[],
108 help="add extra initialization code in the introspection program")
109 parser.add_option("-o", "--output",
110 action="store", dest="output",
111 help="output to writeout, defaults to stdout")
112 parser.add_option("", "--pkg",
113 action="append", dest="packages", default=[],
114 help="pkg-config packages to get cflags from")
115 parser.add_option("", "--pkg-export",
116 action="append", dest="packages_export", default=[],
117 help="Associated pkg-config packages for this library")
118 parser.add_option('', "--warn-all",
119 action="store_true", dest="warn_all", default=False,
120 help="If true, enable all warnings for introspection")
121 parser.add_option('', "--warn-error",
122 action="store_true", dest="warn_fatal",
123 help="Turn warnings into fatal errors")
124 parser.add_option("-v", "--verbose",
125 action="store_true", dest="verbose",
127 parser.add_option("", "--typelib-xml",
128 action="store_true", dest="typelib_xml",
129 help="Just convert GIR to typelib XML")
130 parser.add_option("", "--xpath-assertions",
131 action="store", dest="xpath_assertions",
132 help="Use given file to create assertions on GIR content")
133 parser.add_option("", "--c-include",
134 action="append", dest="c_includes", default=[],
135 help="headers which should be included in C programs")
137 group = optparse.OptionGroup(parser, "Preprocessor options")
138 group.add_option("-I", help="Pre-processor include file",
139 action="append", dest="cpp_includes",
141 group.add_option("-D", help="Pre-processor define",
142 action="append", dest="cpp_defines",
144 group.add_option("-U", help="Pre-processor undefine",
145 action="append", dest="cpp_undefines",
147 group.add_option("-p", dest="", help="Ignored")
148 parser.add_option_group(group)
154 raise SystemExit('ERROR: %s' % (msg, ))
156 def passthrough_gir(path, f):
160 writer = GIRWriter(parser.get_namespace(),
161 parser.get_shared_libraries(),
162 parser.get_includes(),
163 parser.get_pkgconfig_packages(),
164 parser.get_c_includes())
165 f.write(writer.get_xml())
167 def test_codegen(optstring):
168 (namespace, out_h_filename, out_c_filename) = optstring.split(',')
169 if namespace == 'Everything':
170 from .testcodegen import EverythingCodeGenerator
171 gen = EverythingCodeGenerator(out_h_filename, out_c_filename)
174 raise ValueError("Invaild namespace %r" % (namespace, ))
177 def validate(assertions, path):
178 from xml.etree.cElementTree import parse
179 doc = parse(open(path))
182 assertions_list = f.readlines()
183 for assertion in assertions_list:
184 assertion = assertion.strip()
185 xpath_assert(root, assertion)
189 def process_options(output, allowed_flags):
190 for option in output.split():
191 for flag in allowed_flags:
192 if not option.startswith(flag):
197 def process_packages(parser, options, packages):
198 args = ['pkg-config', '--cflags']
199 args.extend(packages)
200 output = subprocess.Popen(args,
201 stdout=subprocess.PIPE).communicate()[0]
203 # the error output should have already appeared on our stderr,
206 # Some pkg-config files on Windows have options we don't understand,
207 # so we explicitly filter to only the ones we need.
208 options_whitelist = ['-I', '-D', '-U', '-l', '-L']
209 filtered_output = list(process_options(output, options_whitelist))
210 pkg_options, unused = parser.parse_args(filtered_output)
211 options.cpp_includes.extend(pkg_options.cpp_includes)
212 options.cpp_defines.extend(pkg_options.cpp_defines)
213 options.cpp_undefines.extend(pkg_options.cpp_undefines)
215 args = ['pkg-config', '--libs-only-L']
216 args.extend(packages)
217 output = subprocess.Popen(args,
218 stdout=subprocess.PIPE).communicate()[0]
221 filtered_output = list(process_options(output, options_whitelist))
222 pkg_options, unused = parser.parse_args(filtered_output)
223 options.library_paths.extend(pkg_options.library_paths)
225 def scanner_main(args):
226 parser = _get_option_parser()
227 (options, args) = parser.parse_args(args)
229 if options.passthrough_gir:
230 passthrough_gir(options.passthrough_gir, sys.stdout)
231 if options.test_codegen:
232 return test_codegen(options.test_codegen)
235 _error('Need at least one filename')
237 if options.xpath_assertions:
238 return validate(options.xpath_assertions, args[1])
240 if not options.namespace_name:
241 _error('Namespace name missing')
243 if options.format == 'gir':
244 from giscanner.girwriter import GIRWriter as Writer
246 _error("Unknown format: %s" % (options.format, ))
248 if not (options.libraries or options.program):
249 _error("Must specify --program or --library")
250 libraries = options.libraries
254 # We don't support real C++ parsing yet, but we should be able
255 # to understand C API implemented in C++ files.
256 if (arg.endswith('.c') or arg.endswith('.cpp') or
257 arg.endswith('.cc') or arg.endswith('.cxx') or
258 arg.endswith('.h') or arg.endswith('.hpp') or
259 arg.endswith('.hxx')):
260 if not os.path.exists(arg):
261 _error('%s: no such a file or directory' % (arg, ))
262 # Make absolute, because we do comparisons inside scannerparser.c
263 # against the absolute path that cpp will give us
264 filenames.append(os.path.abspath(arg))
266 # We do this dance because the empty list has different semantics from
267 # None; if the user didn't specify the options, we want to use None so
268 # the Namespace constructor picks the defaults.
269 if options.identifier_prefixes:
270 identifier_prefixes = options.identifier_prefixes
272 identifier_prefixes = None
273 if options.symbol_prefixes:
274 symbol_prefixes = options.symbol_prefixes
276 symbol_prefixes = None
278 cachestore = CacheStore()
279 transformer = Transformer(cachestore,
280 options.namespace_name,
281 options.namespace_version,
282 identifier_prefixes=identifier_prefixes,
283 symbol_prefixes=symbol_prefixes,
284 accept_unprefixed=options.accept_unprefixed)
286 transformer.enable_warnings(True)
287 transformer.set_include_paths(options.include_paths)
288 shown_include_warning = False
289 for include in options.includes:
290 if os.sep in include:
291 raise ValueError("Invalid include path %r" % (include, ))
293 include_obj = Include.from_string(include)
295 sys.stderr.write("Malformed include %r\n" % (include, ))
297 transformer.register_include(include_obj)
299 packages = set(options.packages)
300 packages.update(transformer.get_pkgconfig_packages())
301 process_packages(parser, options, packages)
303 # Run the preprocessor, tokenize and construct simple
304 # objects representing the raw C symbols
306 ss.set_cpp_options(options.cpp_includes,
308 options.cpp_undefines)
309 ss.parse_files(filenames)
310 ss.parse_macros(filenames)
312 # Transform the C symbols into AST nodes
313 transformer.set_source_ast(ss)
315 # Transform the C AST nodes into higher level
317 gdump_parser = GDumpParser(transformer)
319 # Do enough parsing that we have the get_type() functions to reference
320 # when creating the introspection binary
321 gdump_parser.init_parse()
324 args=[options.program]
325 args.extend(options.program_args)
326 binary = IntrospectionBinary(args)
328 binary = compile_introspection_binary(options,
329 gdump_parser.get_get_type_functions())
331 shlibs = resolve_shlibs(options, binary, libraries)
333 gdump_parser.set_introspection_binary(binary)
336 ap = AnnotationParser(ss)
339 main = MainTransformer(transformer, blocks)
342 final = IntrospectablePass(transformer)
345 if options.warn_fatal and transformer.did_warn():
346 transformer.log_warning("warnings configured as fatal", fatal=True)
347 # Redundant sys.exit here, just in case
351 if options.packages_export:
352 exported_packages = options.packages_export
354 exported_packages = options.packages
355 writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
356 exported_packages, options.c_includes)
357 data = writer.get_xml()
359 tempdir = os.path.dirname(options.output) or os.getcwd()
360 main_f = tempfile.NamedTemporaryFile(suffix='.gir', dir=tempdir, delete=False)
363 if options.reparse_validate_gir:
364 temp_f = tempfile.NamedTemporaryFile(suffix='.gir', dir=tempdir, delete=False)
365 passthrough_gir(main_f.name, temp_f)
367 if not files_are_identical(main_f.name, temp_f.name):
369 "Failed to re-parse .gir file; scanned=%r passthrough=%r" % (main_f.name, temp_f.name))
370 os.unlink(temp_f.name)
371 os.rename(main_f.name, options.output)
373 sys.stdout.write(data)