diff options
author | Jinkun Jang <jinkun.jang@samsung.com> | 2013-03-13 02:21:24 +0900 |
---|---|---|
committer | Jinkun Jang <jinkun.jang@samsung.com> | 2013-03-13 02:21:24 +0900 |
commit | c6b9e84f2f7f8c85939a8e5ff5d8a5aa067cecf3 (patch) | |
tree | 1436172370a45714687122f914354ad167a2f528 /codegen | |
parent | f7d643cbb2184346b6f8d26091eb7eb38c6bbfe1 (diff) | |
download | pygobject2-c6b9e84f2f7f8c85939a8e5ff5d8a5aa067cecf3.tar.gz pygobject2-c6b9e84f2f7f8c85939a8e5ff5d8a5aa067cecf3.tar.bz2 pygobject2-c6b9e84f2f7f8c85939a8e5ff5d8a5aa067cecf3.zip |
Tizen 2.1 basesubmit/tizen_2.1/20130425.060530submit/tizen_2.1/20130424.235900submit/tizen/20130517.051955accepted/tizen_2.1/20130425.021253accepted/tizen/20130520.1018302.1b_releasetizen_2.1
Diffstat (limited to 'codegen')
-rw-r--r-- | codegen/Makefile.am | 31 | ||||
-rw-r--r-- | codegen/Makefile.in | 555 | ||||
-rw-r--r-- | codegen/README.defs | 351 | ||||
-rw-r--r-- | codegen/__init__.py | 16 | ||||
-rw-r--r-- | codegen/argtypes.py | 1043 | ||||
-rwxr-xr-x | codegen/code-coverage.py | 44 | ||||
-rwxr-xr-x | codegen/codegen.py | 1722 | ||||
-rwxr-xr-x | codegen/createdefs.py | 17 | ||||
-rw-r--r-- | codegen/definitions.py | 575 | ||||
-rwxr-xr-x | codegen/defsconvert.py | 132 | ||||
-rwxr-xr-x | codegen/defsgen.py | 737 | ||||
-rw-r--r-- | codegen/defsparser.py | 153 | ||||
-rw-r--r-- | codegen/docextract.py | 448 | ||||
-rwxr-xr-x | codegen/docextract_to_xml.py | 139 | ||||
-rwxr-xr-x | codegen/docgen.py | 766 | ||||
-rwxr-xr-x | codegen/h2def.py | 631 | ||||
-rwxr-xr-x | codegen/mergedefs.py | 26 | ||||
-rwxr-xr-x | codegen/missingdefs.py | 17 | ||||
-rwxr-xr-x | codegen/mkskel.py | 89 | ||||
-rw-r--r-- | codegen/override.py | 285 | ||||
-rw-r--r-- | codegen/pygobject-codegen-2.0.in | 11 | ||||
-rw-r--r-- | codegen/reversewrapper.py | 912 | ||||
-rwxr-xr-x | codegen/scanvirtuals.py | 54 | ||||
-rwxr-xr-x | codegen/scmexpr.py | 143 |
24 files changed, 8897 insertions, 0 deletions
diff --git a/codegen/Makefile.am b/codegen/Makefile.am new file mode 100644 index 0000000..cdb1e99 --- /dev/null +++ b/codegen/Makefile.am @@ -0,0 +1,31 @@ +PLATFORM_VERSION = 2.0 + +bin_SCRIPTS = pygobject-codegen-$(PLATFORM_VERSION) + +codegendir = $(pkgdatadir)/$(PLATFORM_VERSION)/codegen + +codegen_SCRIPTS = \ + code-coverage.py \ + codegen.py \ + createdefs.py \ + defsconvert.py \ + defsgen.py \ + docextract_to_xml.py \ + docgen.py \ + h2def.py \ + mergedefs.py \ + missingdefs.py \ + mkskel.py \ + scanvirtuals.py \ + scmexpr.py + +codegen_PYTHON = \ + __init__.py \ + argtypes.py \ + definitions.py \ + defsparser.py \ + docextract.py \ + override.py \ + reversewrapper.py + +EXTRA_DIST = $(codegen_SCRIPTS) README.defs pygobject-codegen-$(PLATFORM_VERSION).in diff --git a/codegen/Makefile.in b/codegen/Makefile.in new file mode 100644 index 0000000..c9193fc --- /dev/null +++ b/codegen/Makefile.in @@ -0,0 +1,555 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = codegen +DIST_COMMON = $(codegen_PYTHON) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/pygobject-codegen-2.0.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ + $(top_srcdir)/m4/jhflags.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/python.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = pygobject-codegen-2.0 +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(codegendir)" \ + "$(DESTDIR)$(codegendir)" +SCRIPTS = $(bin_SCRIPTS) $(codegen_SCRIPTS) +SOURCES = +DIST_SOURCES = +py_compile = $(top_srcdir)/py-compile +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMTAR = @AMTAR@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIR = @DATADIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FFI_CFLAGS = @FFI_CFLAGS@ +FFI_LIBS = @FFI_LIBS@ +FGREP = @FGREP@ +GIOUNIX_CFLAGS = @GIOUNIX_CFLAGS@ +GIOUNIX_LIBS = @GIOUNIX_LIBS@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GOBJECT_QUERY = @GOBJECT_QUERY@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBFFI_PC = @LIBFFI_PC@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PLATFORM = @PLATFORM@ +PYGOBJECT_MAJOR_VERSION = @PYGOBJECT_MAJOR_VERSION@ +PYGOBJECT_MICRO_VERSION = @PYGOBJECT_MICRO_VERSION@ +PYGOBJECT_MINOR_VERSION = @PYGOBJECT_MINOR_VERSION@ +PYTHON = @PYTHON@ +PYTHON_BASENAME = @PYTHON_BASENAME@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_INCLUDES = @PYTHON_INCLUDES@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +THREADING_CFLAGS = @THREADING_CFLAGS@ +VERSION = @VERSION@ +XSLTPROC = @XSLTPROC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pygobject_CODEGEN_DEFINES = @pygobject_CODEGEN_DEFINES@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +PLATFORM_VERSION = 2.0 +bin_SCRIPTS = pygobject-codegen-$(PLATFORM_VERSION) +codegendir = $(pkgdatadir)/$(PLATFORM_VERSION)/codegen +codegen_SCRIPTS = \ + code-coverage.py \ + codegen.py \ + createdefs.py \ + defsconvert.py \ + defsgen.py \ + docextract_to_xml.py \ + docgen.py \ + h2def.py \ + mergedefs.py \ + missingdefs.py \ + mkskel.py \ + scanvirtuals.py \ + scmexpr.py + +codegen_PYTHON = \ + __init__.py \ + argtypes.py \ + definitions.py \ + defsparser.py \ + docextract.py \ + override.py \ + reversewrapper.py + +EXTRA_DIST = $(codegen_SCRIPTS) README.defs pygobject-codegen-$(PLATFORM_VERSION).in +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu codegen/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu codegen/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +pygobject-codegen-2.0: $(top_builddir)/config.status $(srcdir)/pygobject-codegen-2.0.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-binSCRIPTS: $(bin_SCRIPTS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files +install-codegenSCRIPTS: $(codegen_SCRIPTS) + @$(NORMAL_INSTALL) + test -z "$(codegendir)" || $(MKDIR_P) "$(DESTDIR)$(codegendir)" + @list='$(codegen_SCRIPTS)'; test -n "$(codegendir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(codegendir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(codegendir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-codegenSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(codegen_SCRIPTS)'; test -n "$(codegendir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(codegendir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(codegendir)" && rm -f $$files + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-codegenPYTHON: $(codegen_PYTHON) + @$(NORMAL_INSTALL) + test -z "$(codegendir)" || $(MKDIR_P) "$(DESTDIR)$(codegendir)" + @list='$(codegen_PYTHON)'; dlist=; list2=; test -n "$(codegendir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then b=; else b="$(srcdir)/"; fi; \ + if test -f $$b$$p; then \ + $(am__strip_dir) \ + dlist="$$dlist $$f"; \ + list2="$$list2 $$b$$p"; \ + else :; fi; \ + done; \ + for file in $$list2; do echo $$file; done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(codegendir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(codegendir)" || exit $$?; \ + done || exit $$?; \ + if test -n "$$dlist"; then \ + if test -z "$(DESTDIR)"; then \ + PYTHON=$(PYTHON) $(py_compile) --basedir "$(codegendir)" $$dlist; \ + else \ + PYTHON=$(PYTHON) $(py_compile) --destdir "$(DESTDIR)" --basedir "$(codegendir)" $$dlist; \ + fi; \ + else :; fi + +uninstall-codegenPYTHON: + @$(NORMAL_UNINSTALL) + @list='$(codegen_PYTHON)'; test -n "$(codegendir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + filesc=`echo "$$files" | sed 's|$$|c|'`; \ + fileso=`echo "$$files" | sed 's|$$|o|'`; \ + echo " ( cd '$(DESTDIR)$(codegendir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(codegendir)" && rm -f $$files || exit $$?; \ + echo " ( cd '$(DESTDIR)$(codegendir)' && rm -f" $$filesc ")"; \ + cd "$(DESTDIR)$(codegendir)" && rm -f $$filesc || exit $$?; \ + echo " ( cd '$(DESTDIR)$(codegendir)' && rm -f" $$fileso ")"; \ + cd "$(DESTDIR)$(codegendir)" && rm -f $$fileso +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(SCRIPTS) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(codegendir)" "$(DESTDIR)$(codegendir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-codegenPYTHON install-codegenSCRIPTS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binSCRIPTS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binSCRIPTS uninstall-codegenPYTHON \ + uninstall-codegenSCRIPTS + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + distclean distclean-generic distclean-libtool distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binSCRIPTS install-codegenPYTHON \ + install-codegenSCRIPTS install-data install-data-am \ + install-dvi install-dvi-am install-exec install-exec-am \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am uninstall uninstall-am uninstall-binSCRIPTS \ + uninstall-codegenPYTHON uninstall-codegenSCRIPTS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/codegen/README.defs b/codegen/README.defs new file mode 100644 index 0000000..5f7957e --- /dev/null +++ b/codegen/README.defs @@ -0,0 +1,351 @@ + ChangeLog for this draft: + - caller-owns-return attribute on functions/methods + - include gtk-type-id in the type alias system + - c-name for types converted to in-c-name out-c-name inout-c-name + - merge unref-func and destroy-func for boxed types into release-func + + - split structs out of boxed types + - c-declaration field + - special "native" type alias; bail and use C declaration when + necessary + - defined objects and boxeds automatically create a module + - make constructors functions and not methods, in appropriate + object/boxed module + + Draft ========================= + + The overall syntax is: + + (type-of-thing-being-defined name-used-to-refer-to-this-thing + (attribute-name attribute-value-depending-on-the-attribute) + (attribute-name attribute-value-depending-on-the-attribute) + (attribute-name attribute-value-depending-on-the-attribute)) + + Some definitions can have a c-declaration field that gives the C code + we parsed to arrive at the definition. The c-declaration is a quoted + string because it can contain parentheses and such. + + Defined types and their attributes: + + === + (module module-name + (submodule-of module-name)) ;; submodule is optional + + Ex: (module Gtk) + Ex: (module Rgb + (submodule-of Gdk)) + + modules are later referred to with a list of module names, like + (Gdk Rgb) or (Gtk) + + Object and boxed type definitions automatically create a submodule. + For example, GtkCList creates the module (module CList (submodule-of + (Gtk))) which is referred to as module (Gtk CList). + + === + + (type + (alias some-unique-identifier) + (in-module module-name) ;; optional, gchar* is not in a module + (gtk-type-id gtk-type-system-id) ;; optional, absent if this is not + ;; in the type system + (in-c-name name-of-symbol-in-C) + (out-c-name name-of-symbol-in-C) + (inout-c-name name-of-symbol-in-C)) + + Ex: (type + (alias string) + (gtk-type-id GTK_TYPE_STRING) + (in-c-name const-gchar*) + (out-c-name gchar**) ;; actually I'm not sure how strings work + out/inout + (inout-c-name gchar*)) + + ;; This one would be implied by the (object) def for GtkWidget I + ;; think - (type) is only required for types that are not implied + ;; by other definitions, such as int/boolean/etc. + + (type + (alias GtkWidget) + (in-module (Gtk)) + (gtk-type-id GTK_TYPE_WIDGET) + (in-c-name GtkWidget*) + (inout-c-name GtkWidget*) + (out-c-name GtkWidget**)) + + "Type" bindings are automatically assumed for objects, boxed types, + etc. as defined below. + + The alias field is used to refer to the type later on. + + If the C type has spaces they are converted to hyphens after + compressing all syntactically significant whitespace to a single + space: + (type + (alias const-gchar* + (c-name const-gchar*))) + + So hyphens have to go back to spaces for binding generators that + output C code. + + Whenever a type alias can be used, it is also possible to use the + keyword "native", which implies that the type in question is too + C-specific to represent. Then a c-declaration will typically be + available for use. + + C types containing [] or () are function pointers or arrays. For + arrays that don't specify a size, we just treat them as pointers. For + function pointers, we need special (type) syntax/attributes of some + kind, but since there basically aren't any of these right now in the + libs we care about we can just ignore them. For arrays that specify a + size ditto, you would handle them by adding an (array-size) attribute + or something or using the "native" keyword and skipping the (type) + stuff. + + === + (object object-name + (in-module module-name-list) + (parent object-name optional-module-name-if-different) + (abstract boolean-is-abstract-class) ;; omit for default of #f + (c-name name-of-the-object-in-C) + (field (type-and-name type-alias-of-struct-field + name-of-struct-field) + (access read-or-write-or-readwrite))) + + + Ex: (object Widget + (in-module (Gtk)) + (parent Object) ;; could say (parent Object (Gtk)) + (abstract #t) + (c-name GtkWidget) + (field (type-and-name GdkWindow* window) (access read))) + + An "object" declaration automatically implies the type definition: + + (type + (alias concat-module-elements-and-object-name) + (in-c-name pointer-to-c-name) + (out-c-name pointer-to-pointer-to-c-name) + (inout-c-name pointer-to-c-name)) + + Ex: + (type (alias GtkWidget) + (in-c-name GtkWidget*) + (out-c-name GtkWidget**) + (inout-c-name GtkWidget*)) + + It also implies a module that is the name broken into parts: + (module CTree + (submodule-of Gtk)) + + === + + (function function-name + (in-module module-name-list) ;; "static methods" go in their + ;; object's module + (is-constructor-of object-type-alias) ;; optional, marks a + constructor + (c-name function-name) + (return-type return-value-type) ;; defaults to void + (caller-owns-return boolean-value) ;; defaults to #f + (parameter in-or-out-or-inout + (type-and-name parameter-type-alias parameter-name) + (c-declaration "c-type-and-name")) ;; c-declaration only + required + ;; if the type alias is + "native" + (varargs #t) ;; has varargs at the end + ) + + Ex: + (function init + (in-module (Gdk Rgb) + (c-name gdk_rgb_init))) + + Ex: + (function new + (in-module (Gdk Rgb Cmap)) + (is-constructor-of GdkRgbCmap) + (c-name gdk_rgb_cmap_new) + (return-type GdkRgbCmap) + (caller-owns-return #t) ;; perhaps this could be implied by + is-constructor-of + (parameter in (type-and-name array-of-guint32 colors)) + (parameter in (type-and-name gint n_colors))) + + Ex: + (function config_set_set_handler + (in-module (Gnome)) + (c-name gnome_config_set_set_handler) + (parameter in (type-and-name native func) + (c-declaration "void (*func)(void*)")) + (parameter in (type-and-name gpointer data))) + + === + (method method-name + (of-object object-name module-name) + ;; retval/arg attributes as for (function), but with first parameter + + ;; omitted for non-constructors + ) + + Ex: + (method set_text + (of-object Label (Gtk)) + (parameter (type-and-name const-gchar* str))) + + === + (object-argument arg-name + (of-object object-we-are-an-argument-of optional-objects-module) + (type argument-type) ;; not sure what to put for type + ;; flags all default to #f + (readable bool-value) + (writeable bool-value) + (run-action bool-value) + (construct-only bool-value)) + + Ex: + (object-argument label + (of-object Label (Gtk)) + (type gchar*) ;; ???? + (readable #t) + (writeable #t)) + + === + (signal signal-name + (of-object object-we-are-a-signal-of optional-objects-module) + ;; return value and parameters as for a function, omitting the + object + ;; and user data parameters + + ;; what other properties matter for a signal? + ) + + Ex: + (signal select_row + (of-object CList (Gtk)) + ;; return type defaults to void + (parameter in (type-and-name gint row)) + (parameter in (type-and-name gint column)) + (parameter in (type-and-name GdkEvent* event))) + + === + (enum enum-name + (in-module modname) + (c-name name-in-c) + (value (name value-name-noprefixes-hyphen-lowercase) (c-name + value-c-name))) + + Ex: + + (enum DirectionType + (in-module Gtk) + (c-name GtkDirectionType) + (value (name tab-forward) (c-name GTK_DIR_TAB_FORWARD)) + (value (name tab-backward) (c-name GTK_DIR_TAB_BACKWARD)) + (value (name up) (c-name GTK_DIR_UP)) + (value (name down) (c-name GTK_DIR_DOWN)) + (value (name left) (c-name GTK_DIR_LEFT)) + (value (name right) (c-name GTK_DIR_RIGHT))) + + (enum Pos + (in-module (Gtk CTree)) + (c-name GtkCTreePos) + (value (name before) (c-name GTK_CTREE_POS_BEFORE)) + (value (name as-child) (c-name GTK_CTREE_POS_AS_CHILD)) + (value (name after) (c-name GTK_CTREE_POS_AFTER))) + + === + (flags) is just like enum, but some bindings may wrap enums and flags + differently. + + === + + (boxed boxed-name + (in-module modname) + (c-name c-name) + (ref-func func-to-increase-refcount) + (copy-func func-to-copy) + (release-func func-to-destroy-or-decrement-refcount) + (field (type-and-name type-alias-of-struct-field + name-of-struct-field) (access access-rule))) + + It is never OK to use memcpy() to copy a boxed type, or use + malloc()/free() to alloc/free one. + + Ex: + + (boxed Pixmap + (in-module (Gdk)) + (c-name GdkPixmap) + (ref-func pixmap_ref) + (unref-func pixmap_unref)) + + An "object" declaration automatically implies the type definition: + + (type + (alias concat-module-elements-and-boxed-name) + (in-c-name pointer-to-c-name) + (out-c-name pointer-to-pointer-to-c-name) + (inout-c-name pointer-to-c-name)) + + Ex: + (type (alias GdkPixmap) + (in-c-name GdkPixmap*) + (out-c-name GdkPixmap**) + (inout-c-name GdkPixmap*)) + + === + + (struct struct-name + (in-module modname) + (c-name c-name) + (field (type-and-name type-alias-of-struct-field + name-of-struct-field) (access access-rule))) + + Ex: + (struct Rectangle + (in-module (Gdk)) + (c-name GdkRectangle) + (field (type-and-name gint16 x) (access readwrite)) + (field (type-and-name gint16 y) (access readwrite)) + (field (type-and-name guint16 width) (access readwrite)) + (field (type-and-name guint16 height) (access readwrite))) + + Implies GdkRectangle type alias: + + (type (alias GdkRectangle) + (in-c-name GdkRectangle*) + (out-c-name GdkRectangle*) ;; note - not the same as boxed + types + (inout-c-name GdkRectangle*)) + + === + + (user-function name + (in-module module) + (c-name c-typedef-name) + ;; return-type and parameters as for (function) + ) + + Ex: + + (user-function PrintFunc + (in-module (Gtk)) + (parameter in (type-and-name gpointer func_data)) + (parameter in (type-and-name gchar* str))) + + === + + (typedef new-name + (in-module module) + (c-name c-full-name) + (orig-type alias-of-orig-type)) + + Ex: + + (typedef Type + (in-module (Gtk)) + (c-name GtkType) + (orig-type guint)) + diff --git a/codegen/__init__.py b/codegen/__init__.py new file mode 100644 index 0000000..86188f9 --- /dev/null +++ b/codegen/__init__.py @@ -0,0 +1,16 @@ +# -*- Mode: Python; py-indent-offset: 4 -*- + +__all__ = [ + 'argtypes', + 'codegen', + 'definitions', + 'defsparser', + 'docextract', + 'docgen', + 'h2def', + 'defsgen' + 'mergedefs', + 'mkskel', + 'override', + 'scmexpr' +] diff --git a/codegen/argtypes.py b/codegen/argtypes.py new file mode 100644 index 0000000..b35f6ef --- /dev/null +++ b/codegen/argtypes.py @@ -0,0 +1,1043 @@ +# -*- Mode: Python; py-indent-offset: 4 -*- +import string +import keyword +import struct + +py_ssize_t_clean = False + +class ArgTypeError(Exception): + pass + +class ArgTypeNotFoundError(ArgTypeError): + pass + +class ArgTypeConfigurationError(ArgTypeError): + pass + + +class VarList: + """Nicely format a C variable list""" + def __init__(self): + self.vars = {} + def add(self, ctype, name): + if self.vars.has_key(ctype): + self.vars[ctype] = self.vars[ctype] + (name,) + else: + self.vars[ctype] = (name,) + def __str__(self): + ret = [] + for type in self.vars.keys(): + ret.append(' ') + ret.append(type) + ret.append(' ') + ret.append(string.join(self.vars[type], ', ')) + ret.append(';\n') + if ret: + ret.append('\n') + return string.join(ret, '') + return '' + +class WrapperInfo: + """A class that holds information about variable defs, code + snippets, etcd for use in writing out the function/method + wrapper.""" + def __init__(self): + self.varlist = VarList() + self.parsestr = '' + self.parselist = ['', 'kwlist'] + self.codebefore = [] + self.codeafter = [] + self.arglist = [] + self.kwlist = [] + def get_parselist(self): + return string.join(self.parselist, ', ') + def get_codebefore(self): + return string.join(self.codebefore, '') + def get_codeafter(self): + return string.join(self.codeafter, '') + def get_arglist(self): + return string.join(self.arglist, ', ') + def get_varlist(self): + return str(self.varlist) + def get_kwlist(self): + ret = ' static char *kwlist[] = { %s };\n' % \ + string.join(self.kwlist + [ 'NULL' ], ', ') + if not self.get_varlist(): + ret = ret + '\n' + return ret + + def add_parselist(self, codes, parseargs, keywords): + self.parsestr = self.parsestr + codes + for arg in parseargs: + self.parselist.append(arg) + for kw in keywords: + if keyword.iskeyword(kw): + kw = kw + '_' + self.kwlist.append('"%s"' % kw) + +class ArgType: + def write_param(self, ptype, pname, pdflt, pnull, info): + """Add code to the WrapperInfo instance to handle + parameter.""" + raise RuntimeError, "write_param not implemented for %s" % \ + self.__class__.__name__ + def write_return(self, ptype, ownsreturn, info): + """Adds a variable named ret of the return type to + info.varlist, and add any required code to info.codeafter to + convert the return value to a python object.""" + raise RuntimeError, "write_return not implemented for %s" % \ + self.__class__.__name__ + +class NoneArg(ArgType): + def write_return(self, ptype, ownsreturn, info): + info.codeafter.append(' Py_INCREF(Py_None);\n' + + ' return Py_None;') + +class StringArg(ArgType): + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt != None: + if pdflt != 'NULL': pdflt = '"' + pdflt + '"' + info.varlist.add('char', '*' + pname + ' = ' + pdflt) + else: + info.varlist.add('char', '*' + pname) + info.arglist.append(pname) + if pnull: + info.add_parselist('z', ['&' + pname], [pname]) + else: + info.add_parselist('s', ['&' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + if ownsreturn: + # have to free result ... + info.varlist.add('gchar', '*ret') + info.codeafter.append(' if (ret) {\n' + + ' PyObject *py_ret = PyString_FromString(ret);\n' + + ' g_free(ret);\n' + + ' return py_ret;\n' + + ' }\n' + + ' Py_INCREF(Py_None);\n' + + ' return Py_None;') + else: + info.varlist.add('const gchar', '*ret') + info.codeafter.append(' if (ret)\n' + + ' return PyString_FromString(ret);\n'+ + ' Py_INCREF(Py_None);\n' + + ' return Py_None;') + +class UCharArg(ArgType): + # allows strings with embedded NULLs. + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add('guchar', '*' + pname + ' = "' + pdflt + '"') + else: + info.varlist.add('guchar', '*' + pname) + if py_ssize_t_clean: + info.varlist.add('Py_ssize_t', pname + '_len') + else: + info.varlist.add('int', pname + '_len') + info.arglist.append(pname) + if pnull: + info.add_parselist('z#', ['&' + pname, '&' + pname + '_len'], + [pname]) + else: + info.add_parselist('s#', ['&' + pname, '&' + pname + '_len'], + [pname]) + +class CharArg(ArgType): + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add('char', pname + " = '" + pdflt + "'") + else: + info.varlist.add('char', pname) + info.arglist.append(pname) + info.add_parselist('c', ['&' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('gchar', 'ret') + info.codeafter.append(' return PyString_FromStringAndSize(&ret, 1);') +class GUniCharArg(ArgType): + ret_tmpl = ('#if !defined(Py_UNICODE_SIZE) || Py_UNICODE_SIZE == 2\n' + ' if (ret > 0xffff) {\n' + ' PyErr_SetString(PyExc_RuntimeError, "returned character can not be represented in 16-bit unicode");\n' + ' return NULL;\n' + ' }\n' + '#endif\n' + ' py_ret = (Py_UNICODE)ret;\n' + ' return PyUnicode_FromUnicode(&py_ret, 1);\n') + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add('gunichar', pname + " = '" + pdflt + "'") + else: + info.varlist.add('gunichar', pname) + info.arglist.append(pname) + info.add_parselist('O&', ['pyg_pyobj_to_unichar_conv', '&' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('gunichar', 'ret') + info.varlist.add('Py_UNICODE', 'py_ret') + info.codeafter.append(self.ret_tmpl) + + +class IntArg(ArgType): + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add('int', pname + ' = ' + pdflt) + else: + info.varlist.add('int', pname) + info.arglist.append(pname) + info.add_parselist('i', ['&' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('int', 'ret') + info.codeafter.append(' return PyInt_FromLong(ret);') + +class UIntArg(ArgType): + dflt = (' if (py_%(name)s) {\n' + ' if (PyLong_Check(py_%(name)s))\n' + ' %(name)s = PyLong_AsUnsignedLong(py_%(name)s);\n' + ' else if (PyInt_Check(py_%(name)s))\n' + ' %(name)s = PyInt_AsLong(py_%(name)s);\n' + ' else\n' + ' PyErr_SetString(PyExc_TypeError, "Parameter \'%(name)s\' must be an int or a long");\n' + ' if (PyErr_Occurred())\n' + ' return NULL;\n' + ' }\n') + before = (' if (PyLong_Check(py_%(name)s))\n' + ' %(name)s = PyLong_AsUnsignedLong(py_%(name)s);\n' + ' else if (PyInt_Check(py_%(name)s))\n' + ' %(name)s = PyInt_AsLong(py_%(name)s);\n' + ' else\n' + ' PyErr_SetString(PyExc_TypeError, "Parameter \'%(name)s\' must be an int or a long");\n' + ' if (PyErr_Occurred())\n' + ' return NULL;\n') + def write_param(self, ptype, pname, pdflt, pnull, info): + if not pdflt: + pdflt = '0'; + + info.varlist.add(ptype, pname + ' = ' + pdflt) + info.codebefore.append(self.dflt % {'name':pname}) + info.varlist.add('PyObject', "*py_" + pname + ' = NULL') + info.arglist.append(pname) + info.add_parselist('O', ['&py_' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add(ptype, 'ret') + info.codeafter.append(' return PyLong_FromUnsignedLong(ret);') + +class SizeArg(ArgType): + + if struct.calcsize('P') <= struct.calcsize('l'): + llp64 = True + else: + llp64 = False + + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add(ptype, pname + ' = ' + pdflt) + else: + info.varlist.add(ptype, pname) + info.arglist.append(pname) + if self.llp64: + info.add_parselist('k', ['&' + pname], [pname]) + else: + info.add_parselist('K', ['&' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add(ptype, 'ret') + if self.llp64: + info.codeafter.append(' return PyLong_FromUnsignedLongLong(ret);\n') + else: + info.codeafter.append(' return PyLong_FromUnsignedLong(ret);\n') + +class SSizeArg(ArgType): + + if struct.calcsize('P') <= struct.calcsize('l'): + llp64 = True + else: + llp64 = False + + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add(ptype, pname + ' = ' + pdflt) + else: + info.varlist.add(ptype, pname) + info.arglist.append(pname) + if self.llp64: + info.add_parselist('l', ['&' + pname], [pname]) + else: + info.add_parselist('L', ['&' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add(ptype, 'ret') + if self.llp64: + info.codeafter.append(' return PyLong_FromLongLong(ret);\n') + else: + info.codeafter.append(' return PyLong_FromLong(ret);\n') + +class LongArg(ArgType): + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add(ptype, pname + ' = ' + pdflt) + else: + info.varlist.add(ptype, pname) + info.arglist.append(pname) + info.add_parselist('l', ['&' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add(ptype, 'ret') + info.codeafter.append(' return PyInt_FromLong(ret);\n') + +class BoolArg(IntArg): + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('int', 'ret') + info.codeafter.append(' return PyBool_FromLong(ret);\n') + +class TimeTArg(ArgType): + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add('time_t', pname + ' = ' + pdflt) + else: + info.varlist.add('time_t', pname) + info.arglist.append(pname) + info.add_parselist('i', ['&' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('time_t', 'ret') + info.codeafter.append(' return PyInt_FromLong(ret);') + +class ULongArg(ArgType): + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add('unsigned long', pname + ' = ' + pdflt) + else: + info.varlist.add('unsigned long', pname) + info.arglist.append(pname) + info.add_parselist('k', ['&' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add(ptype, 'ret') + info.codeafter.append(' return PyLong_FromUnsignedLong(ret);\n') + +class UInt32Arg(ULongArg): + def write_param(self, ptype, pname, pdflt, pnull, info): + ULongArg.write_param(self, ptype, pname, pdflt, pnull, info) + ## if sizeof(unsigned long) > sizeof(unsigned int), we need to + ## check the value is within guint32 range + if struct.calcsize('L') > struct.calcsize('I'): + info.codebefore.append(( + ' if (%(pname)s > G_MAXUINT32) {\n' + ' PyErr_SetString(PyExc_ValueError,\n' + ' "Value out of range in conversion of"\n' + ' " %(pname)s parameter to unsigned 32 bit integer");\n' + ' return NULL;\n' + ' }\n') % vars()) + +class Int64Arg(ArgType): + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add('gint64', pname + ' = ' + pdflt) + else: + info.varlist.add('gint64', pname) + info.arglist.append(pname) + info.add_parselist('L', ['&' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('gint64', 'ret') + info.codeafter.append(' return PyLong_FromLongLong(ret);') + +class UInt64Arg(ArgType): + dflt = ' if (py_%(name)s)\n' \ + ' %(name)s = PyLong_AsUnsignedLongLong(py_%(name)s);\n' + before = ' %(name)s = PyLong_AsUnsignedLongLong(py_%(name)s);\n' + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add('guint64', pname + ' = ' + pdflt) + info.codebefore.append(self.dflt % {'name':pname}) + else: + info.varlist.add('guint64', pname) + info.codebefore.append(self.before % {'name':pname}) + info.varlist.add('PyObject', "*py_" + pname + ' = NULL') + info.arglist.append(pname) + info.add_parselist('O!', ['&PyLong_Type', '&py_' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('guint64', 'ret') + info.codeafter.append(' return PyLong_FromUnsignedLongLong(ret);') + + +class DoubleArg(ArgType): + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add('double', pname + ' = ' + pdflt) + else: + info.varlist.add('double', pname) + info.arglist.append(pname) + info.add_parselist('d', ['&' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('double', 'ret') + info.codeafter.append(' return PyFloat_FromDouble(ret);') + +class FileArg(ArgType): + nulldflt = (' if (py_%(name)s == Py_None)\n' + ' %(name)s = NULL;\n' + ' else if (py_%(name)s && PyFile_Check(py_%(name)s)\n' + ' %s = PyFile_AsFile(py_%(name)s);\n' + ' else if (py_%(name)s) {\n' + ' PyErr_SetString(PyExc_TypeError, "%(name)s should be a file object or None");\n' + ' return NULL;\n' + ' }') + null = (' if (py_%(name)s && PyFile_Check(py_%(name)s)\n' + ' %(name)s = PyFile_AsFile(py_%(name)s);\n' + ' else if (py_%(name)s != Py_None) {\n' + ' PyErr_SetString(PyExc_TypeError, "%(name)s should be a file object or None");\n' + ' return NULL;\n' + ' }\n') + dflt = (' if (py_%(name)s)\n' + ' %(name)s = PyFile_AsFile(py_%(name)s);\n') + def write_param(self, ptype, pname, pdflt, pnull, info): + if pnull: + if pdflt: + info.varlist.add('FILE', '*' + pname + ' = ' + pdflt) + info.varlist.add('PyObject', '*py_' + pname + ' = NULL') + info.codebefore.append(self.nulldflt % {'name':pname}) + else: + info.varlist.add('FILE', '*' + pname + ' = NULL') + info.varlist.add('PyObject', '*py_' + pname) + info.codebefore.append(self.null & {'name':pname}) + info.arglist.appned(pname) + info.add_parselist('O', ['&py_' + pname], [pname]) + else: + if pdflt: + info.varlist.add('FILE', '*' + pname + ' = ' + pdflt) + info.varlist.add('PyObject', '*py_' + pname + ' = NULL') + info.codebefore.append(self.dflt % {'name':pname}) + info.arglist.append(pname) + else: + info.varlist.add('PyObject', '*' + pname) + info.arglist.append('PyFile_AsFile(' + pname + ')') + info.add_parselist('O!', ['&PyFile_Type', '&' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('FILE', '*ret') + info.codeafter.append(' if (ret)\n' + + ' return PyFile_FromFile(ret, "", "", fclose);\n' + + ' Py_INCREF(Py_None);\n' + + ' return Py_None;') + +class EnumArg(ArgType): + enum = (' if (pyg_enum_get_value(%(typecode)s, py_%(name)s, (gpointer)&%(name)s))\n' + ' return NULL;\n') + def __init__(self, enumname, typecode): + self.enumname = enumname + self.typecode = typecode + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add(self.enumname, pname + ' = ' + pdflt) + else: + info.varlist.add(self.enumname, pname) + info.varlist.add('PyObject', '*py_' + pname + ' = NULL') + info.codebefore.append(self.enum % { 'typecode': self.typecode, + 'name': pname}) + info.arglist.append(pname) + info.add_parselist('O', ['&py_' + pname], [pname]); + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('gint', 'ret') + info.codeafter.append(' return pyg_enum_from_gtype(%s, ret);' % self.typecode) + +class FlagsArg(ArgType): + flag = (' if (%(default)spyg_flags_get_value(%(typecode)s, py_%(name)s, (gpointer)&%(name)s))\n' + ' return NULL;\n') + def __init__(self, flagname, typecode): + self.flagname = flagname + self.typecode = typecode + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add(self.flagname, pname + ' = ' + pdflt) + default = "py_%s && " % (pname,) + else: + info.varlist.add(self.flagname, pname) + default = "" + info.varlist.add('PyObject', '*py_' + pname + ' = NULL') + info.codebefore.append(self.flag % {'default':default, + 'typecode':self.typecode, + 'name':pname}) + info.arglist.append(pname) + info.add_parselist('O', ['&py_' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('guint', 'ret') + info.codeafter.append(' return pyg_flags_from_gtype(%s, ret);' % self.typecode) + +class ObjectArg(ArgType): + # should change these checks to more typesafe versions that check + # a little further down in the class heirachy. + nulldflt = (' if ((PyObject *)py_%(name)s == Py_None)\n' + ' %(name)s = NULL;\n' + ' else if (py_%(name)s && pygobject_check(py_%(name)s, &Py%(type)s_Type))\n' + ' %(name)s = %(cast)s(py_%(name)s->obj);\n' + ' else if (py_%(name)s) {\n' + ' PyErr_SetString(PyExc_TypeError, "%(name)s should be a %(type)s or None");\n' + ' return NULL;\n' + ' }\n') + null = (' if (py_%(name)s && pygobject_check(py_%(name)s, &Py%(type)s_Type))\n' + ' %(name)s = %(cast)s(py_%(name)s->obj);\n' + ' else if ((PyObject *)py_%(name)s != Py_None) {\n' + ' PyErr_SetString(PyExc_TypeError, "%(name)s should be a %(type)s or None");\n' + ' return NULL;\n' + ' }\n') + dflt = ' if (py_%(name)s)\n' \ + ' %(name)s = %(cast)s(py_%(name)s->obj);\n' + def __init__(self, objname, parent, typecode): + self.objname = objname + self.cast = string.replace(typecode, '_TYPE_', '_', 1) + self.parent = parent + def write_param(self, ptype, pname, pdflt, pnull, info): + if pnull: + if pdflt: + info.varlist.add(self.objname, '*' + pname + ' = ' + pdflt) + info.varlist.add('PyGObject', '*py_' + pname + ' = NULL') + info.codebefore.append(self.nulldflt % {'name':pname, + 'cast':self.cast, + 'type':self.objname}) + else: + info.varlist.add(self.objname, '*' + pname + ' = NULL') + info.varlist.add('PyGObject', '*py_' + pname) + info.codebefore.append(self.null % {'name':pname, + 'cast':self.cast, + 'type':self.objname}) + if ptype.endswith('*'): + typename = ptype[:-1] + try: + const, typename = typename.split('const-') + except ValueError: + const = '' + if typename != ptype: + info.arglist.append('(%s *) %s' % (ptype[:-1], pname)) + else: + info.arglist.append(pname) + + info.add_parselist('O', ['&py_' + pname], [pname]) + else: + if pdflt: + info.varlist.add(self.objname, '*' + pname + ' = ' + pdflt) + info.varlist.add('PyGObject', '*py_' + pname + ' = NULL') + info.codebefore.append(self.dflt % {'name':pname, + 'cast':self.cast}) + info.arglist.append(pname) + info.add_parselist('O!', ['&Py%s_Type' % self.objname, + '&py_' + pname], [pname]) + else: + info.varlist.add('PyGObject', '*' + pname) + info.arglist.append('%s(%s->obj)' % (self.cast, pname)) + info.add_parselist('O!', ['&Py%s_Type' % self.objname, + '&' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + if ptype.endswith('*'): + typename = ptype[:-1] + try: + const, typename = typename.split('const-') + except ValueError: + const = '' + info.varlist.add(typename, '*ret') + if ownsreturn: + info.varlist.add('PyObject', '*py_ret') + info.codeafter.append(' py_ret = pygobject_new((GObject *)ret);\n' + ' if (ret != NULL)\n' + ' g_object_unref(ret);\n' + ' return py_ret;') + else: + info.codeafter.append(' /* pygobject_new handles NULL checking */\n' + + ' return pygobject_new((GObject *)ret);') + +class BoxedArg(ArgType): + # haven't done support for default args. Is it needed? + check = (' if (pyg_boxed_check(py_%(name)s, %(typecode)s))\n' + ' %(name)s = pyg_boxed_get(py_%(name)s, %(typename)s);\n' + ' else {\n' + ' PyErr_SetString(PyExc_TypeError, "%(name)s should be a %(typename)s");\n' + ' return NULL;\n' + ' }\n') + null = (' if (pyg_boxed_check(py_%(name)s, %(typecode)s))\n' + ' %(name)s = pyg_boxed_get(py_%(name)s, %(typename)s);\n' + ' else if (py_%(name)s != Py_None) {\n' + ' PyErr_SetString(PyExc_TypeError, "%(name)s should be a %(typename)s or None");\n' + ' return NULL;\n' + ' }\n') + def __init__(self, ptype, typecode): + self.typename = ptype + self.typecode = typecode + def write_param(self, ptype, pname, pdflt, pnull, info): + if pnull: + info.varlist.add(self.typename, '*' + pname + ' = NULL') + info.varlist.add('PyObject', '*py_' + pname + ' = Py_None') + info.codebefore.append(self.null % {'name': pname, + 'typename': self.typename, + 'typecode': self.typecode}) + else: + info.varlist.add(self.typename, '*' + pname + ' = NULL') + info.varlist.add('PyObject', '*py_' + pname) + info.codebefore.append(self.check % {'name': pname, + 'typename': self.typename, + 'typecode': self.typecode}) + if ptype[-1] == '*': + typename = ptype[:-1] + if typename[:6] == 'const-': typename = typename[6:] + if typename != self.typename: + info.arglist.append('(%s *)%s' % (ptype[:-1], pname)) + else: + info.arglist.append(pname) + else: + info.arglist.append(pname) + info.add_parselist('O', ['&py_' + pname], [pname]) + ret_tmpl = ' /* pyg_boxed_new handles NULL checking */\n' \ + ' return pyg_boxed_new(%(typecode)s, %(ret)s, %(copy)s, TRUE);' + def write_return(self, ptype, ownsreturn, info): + if ptype[-1] == '*': + decl_type = self.typename + ret = 'ret' + if ptype[:6] == 'const-': + decl_type = 'const ' + self.typename + ret = '(%s*) ret' % (self.typename,) + info.varlist.add(decl_type, '*ret') + else: + info.varlist.add(self.typename, 'ret') + ret = '&ret' + ownsreturn = 0 # of course it can't own a ref to a local var ... + info.codeafter.append(self.ret_tmpl % + { 'typecode': self.typecode, + 'ret': ret, + 'copy': ownsreturn and 'FALSE' or 'TRUE'}) + +class CustomBoxedArg(ArgType): + # haven't done support for default args. Is it needed? + null = (' if (%(check)s(py_%(name)s))\n' + ' %(name)s = %(get)s(py_%(name)s);\n' + ' else if (py_%(name)s != Py_None) {\n' + ' PyErr_SetString(PyExc_TypeError, "%(name)s should be a %(type)s or None");\n' + ' return NULL;\n' + ' }\n') + def __init__(self, ptype, pytype, getter, new): + self.pytype = pytype + self.getter = getter + self.checker = 'Py' + ptype + '_Check' + self.new = new + def write_param(self, ptype, pname, pdflt, pnull, info): + if pnull: + info.varlist.add(ptype[:-1], '*' + pname + ' = NULL') + info.varlist.add('PyObject', '*py_' + pname + ' = Py_None') + info.codebefore.append(self.null % {'name': pname, + 'get': self.getter, + 'check': self.checker, + 'type': ptype[:-1]}) + info.arglist.append(pname) + info.add_parselist('O', ['&py_' + pname], [pname]) + else: + info.varlist.add('PyObject', '*' + pname) + info.arglist.append(self.getter + '(' + pname + ')') + info.add_parselist('O!', ['&' + self.pytype, '&' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add(ptype[:-1], '*ret') + info.codeafter.append(' if (ret)\n' + + ' return ' + self.new + '(ret);\n' + + ' Py_INCREF(Py_None);\n' + + ' return Py_None;') + +class PointerArg(ArgType): + # haven't done support for default args. Is it needed? + check = (' if (pyg_pointer_check(py_%(name)s, %(typecode)s))\n' + ' %(name)s = pyg_pointer_get(py_%(name)s, %(typename)s);\n' + ' else {\n' + ' PyErr_SetString(PyExc_TypeError, "%(name)s should be a %(typename)s");\n' + ' return NULL;\n' + ' }\n') + null = (' if (pyg_pointer_check(py_%(name)s, %(typecode)s))\n' + ' %(name)s = pyg_pointer_get(py_%(name)s, %(typename)s);\n' + ' else if (py_%(name)s != Py_None) {\n' + ' PyErr_SetString(PyExc_TypeError, "%(name)s should be a %(typename)s or None");\n' + ' return NULL;\n' + ' }\n') + def __init__(self, ptype, typecode): + self.typename = ptype + self.typecode = typecode + def write_param(self, ptype, pname, pdflt, pnull, info): + if pnull: + info.varlist.add(self.typename, '*' + pname + ' = NULL') + info.varlist.add('PyObject', '*py_' + pname + ' = Py_None') + info.codebefore.append(self.null % {'name': pname, + 'typename': self.typename, + 'typecode': self.typecode}) + else: + info.varlist.add(self.typename, '*' + pname + ' = NULL') + info.varlist.add('PyObject', '*py_' + pname) + info.codebefore.append(self.check % {'name': pname, + 'typename': self.typename, + 'typecode': self.typecode}) + info.arglist.append(pname) + info.add_parselist('O', ['&py_' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + if ptype[-1] == '*': + info.varlist.add(self.typename, '*ret') + info.codeafter.append(' /* pyg_pointer_new handles NULL checking */\n' + + ' return pyg_pointer_new(' + self.typecode + ', ret);') + else: + info.varlist.add(self.typename, 'ret') + info.codeafter.append(' /* pyg_pointer_new handles NULL checking */\n' + + ' return pyg_pointer_new(' + self.typecode + ', &ret);') + +class AtomArg(IntArg): + dflt = ' if (py_%(name)s) {\n' \ + ' %(name)s = pygdk_atom_from_pyobject(py_%(name)s);\n' \ + ' if (PyErr_Occurred())\n' \ + ' return NULL;\n' \ + ' }\n' + atom = (' %(name)s = pygdk_atom_from_pyobject(py_%(name)s);\n' + ' if (PyErr_Occurred())\n' + ' return NULL;\n') + def write_param(self, ptype, pname, pdflt, pnull, info): + if pdflt: + info.varlist.add('GdkAtom', pname + ' = ' + pdflt) + info.varlist.add('PyObject', '*py_' + pname + ' = NULL') + info.codebefore.append(self.dflt % {'name': pname}) + else: + info.varlist.add('GdkAtom', pname) + info.varlist.add('PyObject', '*py_' + pname + ' = NULL') + info.codebefore.append(self.atom % {'name': pname}) + info.arglist.append(pname) + info.add_parselist('O', ['&py_' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('GdkAtom', 'ret') + info.varlist.add('PyObject *', 'py_ret') + info.varlist.add('gchar *', 'name') + info.codeafter.append(' name = gdk_atom_name(ret);\n' + ' py_ret = PyString_FromString(name);\n' + ' g_free(name);\n' + ' return py_ret;') + +class GTypeArg(ArgType): + gtype = (' if ((%(name)s = pyg_type_from_object(py_%(name)s)) == 0)\n' + ' return NULL;\n') + def write_param(self, ptype, pname, pdflt, pnull, info): + info.varlist.add('GType', pname) + info.varlist.add('PyObject', '*py_' + pname + ' = NULL') + info.codebefore.append(self.gtype % {'name': pname}) + info.arglist.append(pname) + info.add_parselist('O', ['&py_' + pname], [pname]) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('GType', 'ret') + info.codeafter.append(' return pyg_type_wrapper_new(ret);') + +# simple GError handler. +class GErrorArg(ArgType): + handleerror = (' if (pyg_error_check(&%(name)s))\n' + ' return NULL;\n') + def write_param(self, ptype, pname, pdflt, pnull, info): + info.varlist.add('GError', '*' + pname + ' = NULL') + info.arglist.append('&' + pname) + info.codeafter.append(self.handleerror % { 'name': pname }) + +class GtkTreePathArg(ArgType): + # haven't done support for default args. Is it needed? + normal = (' %(name)s = pygtk_tree_path_from_pyobject(py_%(name)s);\n' + ' if (!%(name)s) {\n' + ' PyErr_SetString(PyExc_TypeError, "could not convert %(name)s to a GtkTreePath");\n' + ' return NULL;\n' + ' }\n') + null = (' if (py_%(name)s != Py_None) {\n' + ' %(name)s = pygtk_tree_path_from_pyobject(py_%(name)s);\n' + ' if (!%(name)s) {\n' + ' PyErr_SetString(PyExc_TypeError, "could not convert %(name)s to a GtkTreePath");\n' + ' return NULL;\n' + ' }\n' + ' }\n') + freepath = (' if (%(name)s)\n' + ' gtk_tree_path_free(%(name)s);\n') + def __init__(self): + pass + def write_param(self, ptype, pname, pdflt, pnull, info): + if pnull: + info.varlist.add('GtkTreePath', '*' + pname + ' = NULL') + info.varlist.add('PyObject', '*py_' + pname + ' = Py_None') + info.codebefore.append(self.null % {'name': pname}) + info.arglist.append(pname) + info.add_parselist('O', ['&py_' + pname], [pname]) + else: + info.varlist.add('GtkTreePath', '*' + pname) + info.varlist.add('PyObject', '*py_' + pname) + info.codebefore.append(self.normal % {'name': pname}) + info.arglist.append(pname) + info.add_parselist('O', ['&py_' + pname], [pname]) + info.codeafter.append(self.freepath % {'name': pname}) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('GtkTreePath', '*ret') + if ownsreturn: + info.codeafter.append(' if (ret) {\n' + ' PyObject *py_ret = pygtk_tree_path_to_pyobject(ret);\n' + ' gtk_tree_path_free(ret);\n' + ' return py_ret;\n' + ' }\n' + ' Py_INCREF(Py_None);\n' + ' return Py_None;') + else: + info.codeafter.append(' if (ret) {\n' + ' PyObject *py_ret = pygtk_tree_path_to_pyobject(ret);\n' + ' return py_ret;\n' + ' }\n' + ' Py_INCREF(Py_None);\n' + ' return Py_None;') + +class GdkRectanglePtrArg(ArgType): + normal = (' if (!pygdk_rectangle_from_pyobject(py_%(name)s, &%(name)s))\n' + ' return NULL;\n') + null = (' if (py_%(name)s == Py_None)\n' + ' %(name)s = NULL;\n' + ' else if (pygdk_rectangle_from_pyobject(py_%(name)s, &%(name)s_rect))\n' + ' %(name)s = &%(name)s_rect;\n' + ' else\n' + ' return NULL;\n') + def write_param(self, ptype, pname, pdflt, pnull, info): + if pnull: + info.varlist.add('GdkRectangle', pname + '_rect = { 0, 0, 0, 0 }') + info.varlist.add('GdkRectangle', '*' + pname) + info.varlist.add('PyObject', '*py_' + pname + ' = Py_None') + info.add_parselist('O', ['&py_' + pname], [pname]) + info.arglist.append(pname) + info.codebefore.append(self.null % {'name': pname}) + else: + info.varlist.add('GdkRectangle', pname + ' = { 0, 0, 0, 0 }') + info.varlist.add('PyObject', '*py_' + pname) + info.add_parselist('O', ['&py_' + pname], [pname]) + info.arglist.append('&' + pname) + info.codebefore.append(self.normal % {'name': pname}) + +class GdkRectangleArg(ArgType): + def write_return(self, ptype, ownsreturn, info): + info.varlist.add('GdkRectangle', 'ret') + info.codeafter.append(' return pyg_boxed_new(GDK_TYPE_RECTANGLE, &ret, TRUE, TRUE);') + +class PyObjectArg(ArgType): + def write_param(self, ptype, pname, pdflt, pnull, info): + info.varlist.add('PyObject', '*' + pname) + info.add_parselist('O', ['&' + pname], [pname]) + info.arglist.append(pname) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add("PyObject", "*ret") + if ownsreturn: + info.codeafter.append(' if (ret) {\n' + ' return ret;\n' + ' }\n' + ' Py_INCREF(Py_None);\n' + ' return Py_None;') + else: + info.codeafter.append(' if (!ret) ret = Py_None;\n' + ' Py_INCREF(ret);\n' + ' return ret;') + +class CairoArg(ArgType): + def write_param(self, ptype, pname, pdflt, pnull, info): + info.varlist.add('PycairoContext', '*' + pname) + info.add_parselist('O!', ['&PycairoContext_Type', '&' + pname], [pname]) + info.arglist.append('%s->ctx' % pname) + def write_return(self, ptype, ownsreturn, info): + info.varlist.add("cairo_t", "*ret") + if ownsreturn: + info.codeafter.append(' return PycairoContext_FromContext(ret, NULL, NULL);') + else: + info.codeafter.append(' cairo_reference(ret);\n' + ' return PycairoContext_FromContext(ret, NULL, NULL);') + + +class ArgMatcher: + def __init__(self): + self.argtypes = {} + self.reverse_argtypes = {} + self.reverse_rettypes = {} + + def register(self, ptype, handler, overwrite=False): + if not overwrite and ptype in self.argtypes: + return + self.argtypes[ptype] = handler + def register_reverse(self, ptype, handler): + self.reverse_argtypes[ptype] = handler + def register_reverse_ret(self, ptype, handler): + self.reverse_rettypes[ptype] = handler + + def register_enum(self, ptype, typecode): + if typecode is None: + self.register(ptype, IntArg()) + else: + self.register(ptype, EnumArg(ptype, typecode)) + def register_flag(self, ptype, typecode): + if typecode is None: + self.register(ptype, IntArg()) + else: + self.register(ptype, FlagsArg(ptype, typecode)) + def register_object(self, ptype, parent, typecode): + oa = ObjectArg(ptype, parent, typecode) + self.register(ptype, oa) # in case I forget the * in the .defs + self.register(ptype+'*', oa) + self.register('const-'+ptype+'*', oa) + if ptype == 'GdkPixmap': + # hack to handle GdkBitmap synonym. + self.register('GdkBitmap', oa) + self.register('GdkBitmap*', oa) + def register_boxed(self, ptype, typecode): + if self.argtypes.has_key(ptype): return + arg = BoxedArg(ptype, typecode) + self.register(ptype, arg) + self.register(ptype+'*', arg) + self.register('const-'+ptype+'*', arg) + def register_custom_boxed(self, ptype, pytype, getter, new): + arg = CustomBoxedArg(ptype, pytype, getter, new) + self.register(ptype+'*', arg) + self.register('const-'+ptype+'*', arg) + def register_pointer(self, ptype, typecode): + arg = PointerArg(ptype, typecode) + self.register(ptype, arg) + self.register(ptype+'*', arg) + self.register('const-'+ptype+'*', arg) + + def get(self, ptype): + try: + return self.argtypes[ptype] + except KeyError: + if ptype[:8] == 'GdkEvent' and ptype[-1] == '*': + return self.argtypes['GdkEvent*'] + raise ArgTypeNotFoundError("No ArgType for %s" % (ptype,)) + def _get_reverse_common(self, ptype, registry): + props = dict(c_type=ptype) + try: + return registry[ptype], props + except KeyError: + try: + handler = self.argtypes[ptype] + except KeyError: + if ptype.startswith('GdkEvent') and ptype.endswith('*'): + handler = self.argtypes['GdkEvent*'] + else: + raise ArgTypeNotFoundError("No ArgType for %s" % (ptype,)) + if isinstance(handler, ObjectArg): + return registry['GObject*'], props + elif isinstance(handler, EnumArg): + props['typecode'] = handler.typecode + props['enumname'] = handler.enumname + return registry['GEnum'], props + elif isinstance(handler, FlagsArg): + props['typecode'] = handler.typecode + props['flagname'] = handler.flagname + return registry['GFlags'], props + elif isinstance(handler, BoxedArg): + props['typecode'] = handler.typecode + props['typename'] = handler.typename + return registry['GBoxed'], props + else: + raise ArgTypeNotFoundError("No ArgType for %s" % (ptype,)) + + def get_reverse(self, ptype): + return self._get_reverse_common(ptype, self.reverse_argtypes) + + def get_reverse_ret(self, ptype): + ret, props = self._get_reverse_common(ptype, self.reverse_rettypes) + if hasattr(ptype, 'optional') and ptype.optional: + if ret.supports_optional: + props['optional'] = True + else: + raise ArgTypeNotFoundError("Unsupported 'optional' for %s" + % (ptype,)) + return ret, props + + def object_is_a(self, otype, parent): + if otype == None: return 0 + if otype == parent: return 1 + if not self.argtypes.has_key(otype): return 0 + return self.object_is_a(self.get(otype).parent, parent) + +matcher = ArgMatcher() + +arg = NoneArg() +matcher.register(None, arg) +matcher.register('none', arg) + +arg = StringArg() +matcher.register('char*', arg) +matcher.register('gchar*', arg) +matcher.register('const-char*', arg) +matcher.register('char-const*', arg) +matcher.register('const-gchar*', arg) +matcher.register('gchar-const*', arg) +matcher.register('string', arg) +matcher.register('static_string', arg) + +arg = UCharArg() +matcher.register('unsigned-char*', arg) +matcher.register('const-guchar*', arg) +matcher.register('const-guint8*', arg) +matcher.register('guchar*', arg) + +arg = CharArg() +matcher.register('char', arg) +matcher.register('gchar', arg) +matcher.register('guchar', arg) + +arg = GUniCharArg() +matcher.register('gunichar', arg) + +arg = IntArg() +matcher.register('int', arg) +matcher.register('gint', arg) +matcher.register('short', arg) +matcher.register('gshort', arg) +matcher.register('gushort', arg) +matcher.register('gsize', SizeArg()) +matcher.register('gssize', SSizeArg()) +matcher.register('guint8', arg) +matcher.register('gint8', arg) +matcher.register('guint16', arg) +matcher.register('gint16', arg) +matcher.register('gint32', arg) +matcher.register('GTime', arg) +matcher.register('GSeekType', arg) # Hack, but we have no python wrapper + +arg = LongArg() +matcher.register('long', arg) +matcher.register('glong', arg) + +arg = UIntArg() +matcher.register('guint', arg) + +arg = BoolArg() +matcher.register('gboolean', arg) + +arg = TimeTArg() +matcher.register('time_t', arg) + +matcher.register('guint32', UInt32Arg()) + +arg = ULongArg() +matcher.register('gulong', arg) + +arg = Int64Arg() +matcher.register('gint64', arg) +matcher.register('long-long', arg) +matcher.register('goffset', arg) + +arg = UInt64Arg() +matcher.register('guint64', arg) +matcher.register('unsigned-long-long', arg) + +arg = DoubleArg() +matcher.register('double', arg) +matcher.register('gdouble', arg) +matcher.register('float', arg) +matcher.register('gfloat', arg) + +arg = FileArg() +matcher.register('FILE*', arg) + +# enums, flags, objects + +matcher.register('GdkAtom', AtomArg()) + +matcher.register('GType', GTypeArg()) +matcher.register('GtkType', GTypeArg()) + +matcher.register('GError**', GErrorArg()) +matcher.register('GtkTreePath*', GtkTreePathArg()) +matcher.register('GdkRectangle*', GdkRectanglePtrArg()) +matcher.register('GtkAllocation*', GdkRectanglePtrArg()) +matcher.register('GdkRectangle', GdkRectangleArg()) +matcher.register('PyObject*', PyObjectArg()) + +matcher.register('GdkNativeWindow', ULongArg()) + +matcher.register_object('GObject', None, 'G_TYPE_OBJECT') + +del arg + +matcher.register('cairo_t*', CairoArg()) +matcher.register_boxed("GClosure", "G_TYPE_CLOSURE") diff --git a/codegen/code-coverage.py b/codegen/code-coverage.py new file mode 100755 index 0000000..1dc54c3 --- /dev/null +++ b/codegen/code-coverage.py @@ -0,0 +1,44 @@ +#! /usr/bin/env python + +from __future__ import generators +import sys, os + +def read_symbols(file, type=None, dynamic=0): + if dynamic: + cmd = 'nm -D %s' % file + else: + cmd = 'nm %s' % file + for line in os.popen(cmd, 'r'): + if line[0] != ' ': # has an address as first bit of line + while line[0] != ' ': + line = line[1:] + while line[0] == ' ': + line = line[1:] + # we should be up to "type symbolname" now + sym_type = line[0] + symbol = line[1:].strip() + + if not type or type == sym_type: + yield symbol + +def main(): + if len(sys.argv) != 3: + sys.stderr.write('usage: coverage-check library.so wrapper.so\n') + sys.exit(1) + library = sys.argv[1] + wrapper = sys.argv[2] + + # first create a dict with all referenced symbols in the wrapper + # should really be a set, but a dict will do ... + wrapper_symbols = {} + for symbol in read_symbols(wrapper, type='U', dynamic=1): + wrapper_symbols[symbol] = 1 + + # now go through the library looking for matches on the defined symbols: + for symbol in read_symbols(library, type='T', dynamic=1): + if symbol[0] == '_': continue + if symbol not in wrapper_symbols: + print symbol + +if __name__ == '__main__': + main() diff --git a/codegen/codegen.py b/codegen/codegen.py new file mode 100755 index 0000000..008f01c --- /dev/null +++ b/codegen/codegen.py @@ -0,0 +1,1722 @@ +#! /usr/bin/env python + +import getopt +import keyword +import os +import string +import sys + +import argtypes +import definitions +import defsparser +import override +import reversewrapper +import warnings + +class Coverage(object): + def __init__(self, name): + self.name = name + self.wrapped = 0 + self.not_wrapped = 0 + + def declare_wrapped(self): + self.wrapped += 1 + + def declare_not_wrapped(self): + self.not_wrapped += 1 + + def printstats(self): + total = self.wrapped + self.not_wrapped + fd = sys.stderr + if total: + fd.write("***INFO*** The coverage of %s is %.2f%% (%i/%i)\n" % + (self.name, + float(self.wrapped*100)/total, + self.wrapped, + total)) + else: + fd.write("***INFO*** There are no declared %s.\n" % self.name) + +functions_coverage = Coverage("global functions") +methods_coverage = Coverage("methods") +vproxies_coverage = Coverage("virtual proxies") +vaccessors_coverage = Coverage("virtual accessors") +iproxies_coverage = Coverage("interface proxies") + +def exc_info(): + warnings.warn("deprecated", DeprecationWarning, stacklevel=2) + #traceback.print_exc() + etype, value, tb = sys.exc_info() + ret = "" + try: + sval = str(value) + if etype == argtypes.ArgTypeError: + ret = "No ArgType for %s" % (sval,) + else: + ret = sval + finally: + del etype, value, tb + return ret + +def fixname(name): + if keyword.iskeyword(name): + return name + '_' + return name + +class FileOutput: + '''Simple wrapper for file object, that makes writing #line + statements easier.''' # " + def __init__(self, fp, filename=None): + self.fp = fp + self.lineno = 1 + if filename: + self.filename = filename + else: + self.filename = self.fp.name + # handle writing to the file, and keep track of the line number ... + def write(self, str): + self.fp.write(str) + self.lineno = self.lineno + string.count(str, '\n') + def writelines(self, sequence): + for line in sequence: + self.write(line) + def close(self): + self.fp.close() + def flush(self): + self.fp.flush() + + def setline(self, linenum, filename): + '''writes out a #line statement, for use by the C + preprocessor.''' # " + self.write('#line %d "%s"\n' % (linenum, filename)) + def resetline(self): + '''resets line numbering to the original file''' + self.setline(self.lineno + 1, self.filename) + +class Wrapper: + type_tmpl = ( + 'PyTypeObject G_GNUC_INTERNAL Py%(typename)s_Type = {\n' + ' PyObject_HEAD_INIT(NULL)\n' + ' 0, /* ob_size */\n' + ' "%(classname)s", /* tp_name */\n' + ' sizeof(%(tp_basicsize)s), /* tp_basicsize */\n' + ' 0, /* tp_itemsize */\n' + ' /* methods */\n' + ' (destructor)%(tp_dealloc)s, /* tp_dealloc */\n' + ' (printfunc)0, /* tp_print */\n' + ' (getattrfunc)%(tp_getattr)s, /* tp_getattr */\n' + ' (setattrfunc)%(tp_setattr)s, /* tp_setattr */\n' + ' (cmpfunc)%(tp_compare)s, /* tp_compare */\n' + ' (reprfunc)%(tp_repr)s, /* tp_repr */\n' + ' (PyNumberMethods*)%(tp_as_number)s, /* tp_as_number */\n' + ' (PySequenceMethods*)%(tp_as_sequence)s, /* tp_as_sequence */\n' + ' (PyMappingMethods*)%(tp_as_mapping)s, /* tp_as_mapping */\n' + ' (hashfunc)%(tp_hash)s, /* tp_hash */\n' + ' (ternaryfunc)%(tp_call)s, /* tp_call */\n' + ' (reprfunc)%(tp_str)s, /* tp_str */\n' + ' (getattrofunc)%(tp_getattro)s, /* tp_getattro */\n' + ' (setattrofunc)%(tp_setattro)s, /* tp_setattro */\n' + ' (PyBufferProcs*)%(tp_as_buffer)s, /* tp_as_buffer */\n' + ' %(tp_flags)s, /* tp_flags */\n' + ' %(tp_doc)s, /* Documentation string */\n' + ' (traverseproc)%(tp_traverse)s, /* tp_traverse */\n' + ' (inquiry)%(tp_clear)s, /* tp_clear */\n' + ' (richcmpfunc)%(tp_richcompare)s, /* tp_richcompare */\n' + ' %(tp_weaklistoffset)s, /* tp_weaklistoffset */\n' + ' (getiterfunc)%(tp_iter)s, /* tp_iter */\n' + ' (iternextfunc)%(tp_iternext)s, /* tp_iternext */\n' + ' (struct PyMethodDef*)%(tp_methods)s, /* tp_methods */\n' + ' (struct PyMemberDef*)0, /* tp_members */\n' + ' (struct PyGetSetDef*)%(tp_getset)s, /* tp_getset */\n' + ' NULL, /* tp_base */\n' + ' NULL, /* tp_dict */\n' + ' (descrgetfunc)%(tp_descr_get)s, /* tp_descr_get */\n' + ' (descrsetfunc)%(tp_descr_set)s, /* tp_descr_set */\n' + ' %(tp_dictoffset)s, /* tp_dictoffset */\n' + ' (initproc)%(tp_init)s, /* tp_init */\n' + ' (allocfunc)%(tp_alloc)s, /* tp_alloc */\n' + ' (newfunc)%(tp_new)s, /* tp_new */\n' + ' (freefunc)%(tp_free)s, /* tp_free */\n' + ' (inquiry)%(tp_is_gc)s /* tp_is_gc */\n' + '};\n\n' + ) + + slots_list = [ + 'tp_getattr', 'tp_setattr', 'tp_getattro', 'tp_setattro', + 'tp_compare', 'tp_repr', + 'tp_as_number', 'tp_as_sequence', 'tp_as_mapping', 'tp_hash', + 'tp_call', 'tp_str', 'tp_as_buffer', 'tp_richcompare', 'tp_iter', + 'tp_iternext', 'tp_descr_get', 'tp_descr_set', 'tp_init', + 'tp_alloc', 'tp_new', 'tp_free', 'tp_is_gc', + 'tp_traverse', 'tp_clear', 'tp_dealloc', 'tp_flags', 'tp_doc' + ] + + getter_tmpl = ( + 'static PyObject *\n' + '%(funcname)s(PyObject *self, void *closure)\n' + '{\n' + '%(varlist)s' + ' ret = %(field)s;\n' + '%(codeafter)s\n' + '}\n\n' + ) + + parse_tmpl = ( + ' if (!PyArg_ParseTupleAndKeywords(args, kwargs,' + '"%(typecodes)s:%(name)s"%(parselist)s))\n' + ' return %(errorreturn)s;\n' + ) + + deprecated_tmpl = ( + ' if (PyErr_Warn(PyExc_DeprecationWarning, ' + '"%(deprecationmsg)s") < 0)\n' + ' return %(errorreturn)s;\n' + ) + + methdef_tmpl = ( + ' { "%(name)s", (PyCFunction)%(cname)s, %(flags)s,\n' + ' %(docstring)s },\n' + ) + + noconstructor = ( + 'static int\n' + 'pygobject_no_constructor(PyObject *self, PyObject *args, ' + 'PyObject *kwargs)\n' + '{\n' + ' gchar buf[512];\n' + '\n' + ' g_snprintf(buf, sizeof(buf), "%s is an abstract widget", ' + 'self->ob_type->tp_name);\n' + ' PyErr_SetString(PyExc_NotImplementedError, buf);\n' + ' return -1;\n' + '}\n\n' + ) + + function_tmpl = ( + 'static PyObject *\n' + '_wrap_%(cname)s(PyObject *self%(extraparams)s)\n' + '{\n' + '%(varlist)s' + '%(parseargs)s' + '%(codebefore)s' + ' %(begin_allow_threads)s\n' + ' %(setreturn)s%(cname)s(%(arglist)s);\n' + ' %(end_allow_threads)s\n' + '%(codeafter)s\n' + '}\n\n' + ) + + virtual_accessor_tmpl = ( + 'static PyObject *\n' + '_wrap_%(cname)s(PyObject *cls%(extraparams)s)\n' + '{\n' + ' gpointer klass;\n' + '%(varlist)s' + '%(parseargs)s' + '%(codebefore)s' + ' klass = g_type_class_ref(pyg_type_from_object(cls));\n' + ' if (%(class_cast_macro)s(klass)->%(virtual)s)\n' + ' %(setreturn)s%(class_cast_macro)s(klass)->' + '%(virtual)s(%(arglist)s);\n' + ' else {\n' + ' PyErr_SetString(PyExc_NotImplementedError, ' + '"virtual method %(name)s not implemented");\n' + ' g_type_class_unref(klass);\n' + ' return NULL;\n' + ' }\n' + ' g_type_class_unref(klass);\n' + '%(codeafter)s\n' + '}\n\n' + ) + + # template for method calls + constructor_tmpl = None + method_tmpl = None + + def __init__(self, parser, objinfo, overrides, fp=FileOutput(sys.stdout)): + self.parser = parser + self.objinfo = objinfo + self.overrides = overrides + self.fp = fp + + def get_lower_name(self): + return string.lower(string.replace(self.objinfo.typecode, + '_TYPE_', '_', 1)) + + def get_field_accessor(self, fieldname): + raise NotImplementedError + + def get_initial_class_substdict(self): return {} + + def get_initial_constructor_substdict(self, constructor): + return { 'name': '%s.__init__' % self.objinfo.py_name, + 'errorreturn': '-1' } + + def get_initial_method_substdict(self, method): + substdict = { 'name': '%s.%s' % (self.objinfo.py_name, method.name) } + if method.unblock_threads: + substdict['begin_allow_threads'] = 'pyg_begin_allow_threads;' + substdict['end_allow_threads'] = 'pyg_end_allow_threads;' + else: + substdict['begin_allow_threads'] = '' + substdict['end_allow_threads'] = '' + return substdict + + def write_class(self): + if self.overrides.is_type_ignored(self.objinfo.c_name): + return + self.fp.write('\n/* ----------- %s ----------- */\n\n' % + self.objinfo.c_name) + substdict = self.get_initial_class_substdict() + if not substdict.has_key('tp_flags'): + substdict['tp_flags'] = 'Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE' + substdict['typename'] = self.objinfo.c_name + if self.overrides.modulename: + substdict['classname'] = '%s.%s' % (self.overrides.modulename, + self.objinfo.name) + else: + substdict['classname'] = self.objinfo.name + substdict['tp_doc'] = self.objinfo.docstring + + # Maybe this could be done in a nicer way, but I'll leave it as it is + # for now: -- Johan + if not self.overrides.slot_is_overriden('%s.tp_init' % + self.objinfo.c_name): + substdict['tp_init'] = self.write_constructor() + substdict['tp_methods'] = self.write_methods() + substdict['tp_getset'] = self.write_getsets() + + # handle slots ... + for slot in self.slots_list: + + slotname = '%s.%s' % (self.objinfo.c_name, slot) + slotfunc = '_wrap_%s_%s' % (self.get_lower_name(), slot) + if slot[:6] == 'tp_as_': + slotfunc = '&' + slotfunc + if self.overrides.slot_is_overriden(slotname): + data = self.overrides.slot_override(slotname) + self.write_function(slotname, data) + substdict[slot] = slotfunc + else: + if not substdict.has_key(slot): + substdict[slot] = '0' + + self.fp.write(self.type_tmpl % substdict) + + self.write_virtuals() + + def write_function_wrapper(self, function_obj, template, + handle_return=0, is_method=0, kwargs_needed=0, + substdict=None): + '''This function is the guts of all functions that generate + wrappers for functions, methods and constructors.''' + if not substdict: substdict = {} + + info = argtypes.WrapperInfo() + + substdict.setdefault('errorreturn', 'NULL') + + # for methods, we want the leading comma + if is_method: + info.arglist.append('') + + if function_obj.varargs: + raise argtypes.ArgTypeNotFoundError("varargs functions not supported") + + for param in function_obj.params: + if param.pdflt != None and '|' not in info.parsestr: + info.add_parselist('|', [], []) + handler = argtypes.matcher.get(param.ptype) + handler.write_param(param.ptype, param.pname, param.pdflt, + param.pnull, info) + + substdict['setreturn'] = '' + if handle_return: + if function_obj.ret not in ('none', None): + substdict['setreturn'] = 'ret = ' + handler = argtypes.matcher.get(function_obj.ret) + handler.write_return(function_obj.ret, + function_obj.caller_owns_return, info) + + if function_obj.deprecated != None: + deprecated = self.deprecated_tmpl % { + 'deprecationmsg': function_obj.deprecated, + 'errorreturn': substdict['errorreturn'] } + else: + deprecated = '' + + # if name isn't set, set it to function_obj.name + substdict.setdefault('name', function_obj.name) + + if function_obj.unblock_threads: + substdict['begin_allow_threads'] = 'pyg_begin_allow_threads;' + substdict['end_allow_threads'] = 'pyg_end_allow_threads;' + else: + substdict['begin_allow_threads'] = '' + substdict['end_allow_threads'] = '' + + if self.objinfo: + substdict['typename'] = self.objinfo.c_name + substdict.setdefault('cname', function_obj.c_name) + substdict['varlist'] = info.get_varlist() + substdict['typecodes'] = info.parsestr + substdict['parselist'] = info.get_parselist() + substdict['arglist'] = info.get_arglist() + substdict['codebefore'] = deprecated + ( + string.replace(info.get_codebefore(), + 'return NULL', 'return ' + substdict['errorreturn']) + ) + substdict['codeafter'] = ( + string.replace(info.get_codeafter(), + 'return NULL', + 'return ' + substdict['errorreturn'])) + + if info.parsestr or kwargs_needed: + substdict['parseargs'] = self.parse_tmpl % substdict + substdict['extraparams'] = ', PyObject *args, PyObject *kwargs' + flags = 'METH_VARARGS|METH_KEYWORDS' + + # prepend the keyword list to the variable list + substdict['varlist'] = info.get_kwlist() + substdict['varlist'] + else: + substdict['parseargs'] = '' + substdict['extraparams'] = '' + flags = 'METH_NOARGS' + + return template % substdict, flags + + def write_constructor(self): + initfunc = '0' + constructor = self.parser.find_constructor(self.objinfo,self.overrides) + if not constructor: + return self.write_default_constructor() + + funcname = constructor.c_name + try: + if self.overrides.is_overriden(funcname): + data = self.overrides.override(funcname) + self.write_function(funcname, data) + self.objinfo.has_new_constructor_api = ( + self.objinfo.typecode in + self.overrides.newstyle_constructors) + else: + # ok, a hack to determine if we should use + # new-style constructores :P + property_based = getattr(self, + 'write_property_based_constructor', + None) + if property_based: + if (len(constructor.params) == 0 or + isinstance(constructor.params[0], + definitions.Property)): + # write_property_based_constructor is only + # implemented in GObjectWrapper + return self.write_property_based_constructor( + constructor) + else: + sys.stderr.write( + "Warning: generating old-style constructor for:" + + constructor.c_name + '\n') + + # write constructor from template ... + code = self.write_function_wrapper(constructor, + self.constructor_tmpl, + handle_return=0, is_method=0, kwargs_needed=1, + substdict=self.get_initial_constructor_substdict( + constructor))[0] + self.fp.write(code) + initfunc = '_wrap_' + funcname + except argtypes.ArgTypeError, ex: + sys.stderr.write('Could not write constructor for %s: %s\n' + % (self.objinfo.c_name, str(ex))) + + initfunc = self.write_noconstructor() + return initfunc + + def write_noconstructor(self): + # this is a hack ... + if not hasattr(self.overrides, 'no_constructor_written'): + self.fp.write(self.noconstructor) + self.overrides.no_constructor_written = 1 + initfunc = 'pygobject_no_constructor' + return initfunc + + def write_default_constructor(self): + return self.write_noconstructor() + + def get_methflags(self, funcname): + if self.overrides.wants_kwargs(funcname): + flags = 'METH_VARARGS|METH_KEYWORDS' + elif self.overrides.wants_noargs(funcname): + flags = 'METH_NOARGS' + elif self.overrides.wants_onearg(funcname): + flags = 'METH_O' + else: + flags = 'METH_VARARGS' + if self.overrides.is_staticmethod(funcname): + flags += '|METH_STATIC' + elif self.overrides.is_classmethod(funcname): + flags += '|METH_CLASS' + return flags + + def write_function(self, funcname, data): + lineno, filename = self.overrides.getstartline(funcname) + self.fp.setline(lineno, filename) + self.fp.write(data) + self.fp.resetline() + self.fp.write('\n\n') + + def _get_class_virtual_substdict(self, meth, cname, parent): + substdict = self.get_initial_method_substdict(meth) + substdict['virtual'] = meth.name + substdict['cname'] = cname + substdict['class_cast_macro'] = parent.typecode.replace( + '_TYPE_', '_', 1) + "_CLASS" + substdict['typecode'] = self.objinfo.typecode + substdict['cast'] = string.replace(parent.typecode, '_TYPE_', '_', 1) + return substdict + + def write_methods(self): + methods = [] + klass = self.objinfo.c_name + # First, get methods from the defs files + for meth in self.parser.find_methods(self.objinfo): + method_name = meth.c_name + if self.overrides.is_ignored(method_name): + continue + try: + if self.overrides.is_overriden(method_name): + if not self.overrides.is_already_included(method_name): + data = self.overrides.override(method_name) + self.write_function(method_name, data) + + methflags = self.get_methflags(method_name) + else: + # write constructor from template ... + code, methflags = self.write_function_wrapper(meth, + self.method_tmpl, handle_return=1, is_method=1, + substdict=self.get_initial_method_substdict(meth)) + self.fp.write(code) + methods.append(self.methdef_tmpl % + { 'name': fixname(meth.name), + 'cname': '_wrap_' + method_name, + 'flags': methflags, + 'docstring': meth.docstring }) + methods_coverage.declare_wrapped() + except argtypes.ArgTypeError, ex: + methods_coverage.declare_not_wrapped() + sys.stderr.write('Could not write method %s.%s: %s\n' + % (klass, meth.name, str(ex))) + + # Now try to see if there are any defined in the override + for method_name in self.overrides.get_defines_for(klass): + c_name = override.class2cname(klass, method_name) + if self.overrides.is_already_included(method_name): + continue + + try: + data = self.overrides.define(klass, method_name) + self.write_function(method_name, data) + methflags = self.get_methflags(method_name) + + methods.append(self.methdef_tmpl % + { 'name': method_name, + 'cname': '_wrap_' + c_name, + 'flags': methflags, + 'docstring': 'NULL' }) + methods_coverage.declare_wrapped() + except argtypes.ArgTypeError, ex: + methods_coverage.declare_not_wrapped() + sys.stderr.write('Could not write method %s.%s: %s\n' + % (klass, method_name, str(ex))) + + # Add GObject virtual method accessors, for chaining to parent + # virtuals from subclasses + methods += self.write_virtual_accessors() + + if methods: + methoddefs = '_Py%s_methods' % self.objinfo.c_name + # write the PyMethodDef structure + methods.append(' { NULL, NULL, 0, NULL }\n') + self.fp.write('static const PyMethodDef %s[] = {\n' % methoddefs) + self.fp.write(string.join(methods, '')) + self.fp.write('};\n\n') + else: + methoddefs = 'NULL' + return methoddefs + + def write_virtual_accessors(self): + klass = self.objinfo.c_name + methods = [] + for meth in self.parser.find_virtuals(self.objinfo): + method_name = self.objinfo.c_name + "__do_" + meth.name + if self.overrides.is_ignored(method_name): + continue + try: + if self.overrides.is_overriden(method_name): + if not self.overrides.is_already_included(method_name): + data = self.overrides.override(method_name) + self.write_function(method_name, data) + methflags = self.get_methflags(method_name) + else: + # temporarily add a 'self' parameter as first argument + meth.params.insert(0, definitions.Parameter( + ptype=(self.objinfo.c_name + '*'), + pname='self', pdflt=None, pnull=None)) + try: + # write method from template ... + code, methflags = self.write_function_wrapper( + meth, self.virtual_accessor_tmpl, + handle_return=True, is_method=False, + substdict=self._get_class_virtual_substdict( + meth, method_name, self.objinfo)) + self.fp.write(code) + finally: + del meth.params[0] + methods.append(self.methdef_tmpl % + { 'name': "do_" + fixname(meth.name), + 'cname': '_wrap_' + method_name, + 'flags': methflags + '|METH_CLASS', + 'docstring': 'NULL'}) + vaccessors_coverage.declare_wrapped() + except argtypes.ArgTypeError, ex: + vaccessors_coverage.declare_not_wrapped() + sys.stderr.write( + 'Could not write virtual accessor method %s.%s: %s\n' + % (klass, meth.name, str(ex))) + return methods + + def write_virtuals(self): + ''' + Write _wrap_FooBar__proxy_do_zbr() reverse wrapers for + GObject virtuals + ''' + klass = self.objinfo.c_name + virtuals = [] + for meth in self.parser.find_virtuals(self.objinfo): + method_name = self.objinfo.c_name + "__proxy_do_" + meth.name + if self.overrides.is_ignored(method_name): + continue + try: + if self.overrides.is_overriden(method_name): + if not self.overrides.is_already_included(method_name): + data = self.overrides.override(method_name) + self.write_function(method_name, data) + else: + # write virtual proxy ... + ret, props = argtypes.matcher.get_reverse_ret(meth.ret) + wrapper = reversewrapper.ReverseWrapper( + '_wrap_' + method_name, is_static=True) + wrapper.set_return_type(ret(wrapper, **props)) + wrapper.add_parameter(reversewrapper.PyGObjectMethodParam( + wrapper, "self", method_name="do_" + meth.name, + c_type=(klass + ' *'))) + for param in meth.params: + handler, props = argtypes.matcher.get_reverse( + param.ptype) + props["direction"] = param.pdir + props["nullok"] = param.pnull + wrapper.add_parameter(handler(wrapper, + param.pname, **props)) + buf = reversewrapper.MemoryCodeSink() + wrapper.generate(buf) + self.fp.write(buf.flush()) + virtuals.append((fixname(meth.name), '_wrap_' + method_name)) + vproxies_coverage.declare_wrapped() + except argtypes.ArgTypeError, ex: + vproxies_coverage.declare_not_wrapped() + virtuals.append((fixname(meth.name), None)) + sys.stderr.write('Could not write virtual proxy %s.%s: %s\n' + % (klass, meth.name, str(ex))) + if virtuals: + # Write a 'pygtk class init' function for this object, + # except when the object type is explicitly ignored (like + # GtkPlug and GtkSocket on win32). + if self.overrides.is_ignored(self.objinfo.typecode): + return + class_cast_macro = self.objinfo.typecode.replace( + '_TYPE_', '_', 1) + "_CLASS" + cast_macro = self.objinfo.typecode.replace('_TYPE_', '_', 1) + funcname = "__%s_class_init" % klass + self.objinfo.class_init_func = funcname + have_implemented_virtuals = not not [True + for name, cname in virtuals + if cname is not None] + self.fp.write( + ('\nstatic int\n' + '%(funcname)s(gpointer gclass, PyTypeObject *pyclass)\n' + '{\n') % vars()) + + if have_implemented_virtuals: + self.fp.write(' PyObject *o;\n') + self.fp.write( + ' %(klass)sClass *klass = ' + '%(class_cast_macro)s(gclass);\n' + ' PyObject *gsignals = ' + 'PyDict_GetItemString(pyclass->tp_dict, "__gsignals__");\n' + % vars()) + + for name, cname in virtuals: + do_name = 'do_' + name + if cname is None: + self.fp.write('\n /* overriding %(do_name)s ' + 'is currently not supported */\n' % vars()) + else: + self.fp.write(''' + o = PyObject_GetAttrString((PyObject *) pyclass, "%(do_name)s"); + if (o == NULL) + PyErr_Clear(); + else { + if (!PyObject_TypeCheck(o, &PyCFunction_Type) + && !(gsignals && PyDict_GetItemString(gsignals, "%(name)s"))) + klass->%(name)s = %(cname)s; + Py_DECREF(o); + } +''' % vars()) + self.fp.write(' return 0;\n}\n') + + def write_getsets(self): + lower_name = self.get_lower_name() + getsets_name = lower_name + '_getsets' + getterprefix = '_wrap_' + lower_name + '__get_' + setterprefix = '_wrap_' + lower_name + '__set_' + + # no overrides for the whole function. If no fields, + # don't write a func + if not self.objinfo.fields: + return '0' + getsets = [] + for ftype, cfname in self.objinfo.fields: + fname = cfname.replace('.', '_') + gettername = '0' + settername = '0' + attrname = self.objinfo.c_name + '.' + fname + if self.overrides.attr_is_overriden(attrname): + code = self.overrides.attr_override(attrname) + self.write_function(attrname, code) + if string.find(code, getterprefix + fname) >= 0: + gettername = getterprefix + fname + if string.find(code, setterprefix + fname) >= 0: + settername = setterprefix + fname + if gettername == '0': + try: + funcname = getterprefix + fname + info = argtypes.WrapperInfo() + handler = argtypes.matcher.get(ftype) + # for attributes, we don't own the "return value" + handler.write_return(ftype, 0, info) + self.fp.write(self.getter_tmpl % + { 'funcname': funcname, + 'varlist': info.varlist, + 'field': self.get_field_accessor(cfname), + 'codeafter': info.get_codeafter() }) + gettername = funcname + except argtypes.ArgTypeError, ex: + sys.stderr.write( + "Could not write getter for %s.%s: %s\n" + % (self.objinfo.c_name, fname, str(ex))) + if gettername != '0' or settername != '0': + getsets.append(' { "%s", (getter)%s, (setter)%s },\n' % + (fixname(fname), gettername, settername)) + + if not getsets: + return '0' + self.fp.write('static const PyGetSetDef %s[] = {\n' % getsets_name) + for getset in getsets: + self.fp.write(getset) + self.fp.write(' { NULL, (getter)0, (setter)0 },\n') + self.fp.write('};\n\n') + + return getsets_name + + def _write_get_symbol_names(self, writer, functions): + self.fp.write("""static PyObject * +_wrap__get_symbol_names(PyObject *self) +{ + PyObject *pylist = PyList_New(0); + +""") + for obj, bases in writer.get_classes(): + self.fp.write(' PyList_Append(pylist, ' + 'PyString_FromString("%s"));\n' % (obj.name)) + + for name, cname, flags, docstring in functions: + self.fp.write(' PyList_Append(pylist, ' + 'PyString_FromString("%s"));\n' % (name)) + + for enum in writer.get_enums(): + self.fp.write(' PyList_Append(pylist, ' + 'PyString_FromString("%s"));\n' % (enum.name)) + for nick, value in enum.values: + name = value[len(self.overrides.modulename)+1:] + self.fp.write(' PyList_Append(pylist, ' + 'PyString_FromString("%s"));\n' % (name)) + + self.fp.write(" return pylist;\n}\n\n"); + + def _write_get_symbol(self, writer, functions): + self.fp.write("""static PyObject * +_wrap__get_symbol(PyObject *self, PyObject *args) +{ + PyObject *d; + char *name; + static PyObject *modulename = NULL; + static PyObject *module = NULL; + static char *strip_prefix = "%s"; + + if (!PyArg_ParseTuple(args, "Os", &d, &name)) + return NULL; + + if (!modulename) + modulename = PyString_FromString("%s"); + + if (!module) + module = PyDict_GetItemString(d, "__module__"); + +""" % (self.overrides.modulename.upper() + '_', + self.overrides.modulename)) + + first = True + # Classes / GObjects + for obj, bases in writer.get_classes(): + if first: + self.fp.write(' if (!strcmp(name, "%s")) {\n' % obj.name) + first = False + else: + self.fp.write(' } else if (!strcmp(name, "%s")) {\n' % obj.name) + self.fp.write( + ' return (PyObject*)pygobject_lookup_class(%s);\n' % + obj.typecode) + self.fp.write(' }\n') + + # Functions + for name, cname, flags, docstring in functions: + self.fp.write(' else if (!strcmp(name, "%s")) {\n' % name) + self.fp.write(' static PyMethodDef ml = { ' + '"%s", (PyCFunction)%s, %s, "%s"};\n' % ( + name, cname, flags, docstring)) + self.fp.write(' return PyCFunction_NewEx(&ml, NULL, modulename);\n') + self.fp.write(' }\n') + + # Enums + def write_enum(enum, returnobj=False): + if returnobj: + ret = 'return ' + else: + ret = '' + if enum.deftype == 'enum': + self.fp.write( + ' %spyg_enum_add(module, "%s", strip_prefix, %s);\n' + % (ret, enum.name, enum.typecode)) + else: + self.fp.write( + ' %spyg_flags_add(module, "%s", strip_prefix, %s);\n' + % (ret, enum.name, enum.typecode)) + + strip_len = len(self.overrides.modulename)+1 # GTK_ + for enum in writer.get_enums(): + # XXX: Implement without typecodes + self.fp.write(' else if (!strcmp(name, "%s")) {\n' % enum.name) + write_enum(enum, returnobj=True) + self.fp.write(' }\n') + + for nick, value in enum.values: + value = value[strip_len:] + self.fp.write(' else if (!strcmp(name, "%s")) {\n' % value) + write_enum(enum) + self.fp.write(' return PyObject_GetAttrString(module, "%s");\n' % + value) + self.fp.write(' }\n') + + self.fp.write(' return Py_None;\n}\n\n'); + + def _write_function_bodies(self): + functions = [] + # First, get methods from the defs files + for func in self.parser.find_functions(): + funcname = func.c_name + if self.overrides.is_ignored(funcname): + continue + try: + if self.overrides.is_overriden(funcname): + data = self.overrides.override(funcname) + self.write_function(funcname, data) + + methflags = self.get_methflags(funcname) + else: + # write constructor from template ... + code, methflags = self.write_function_wrapper(func, + self.function_tmpl, handle_return=1, is_method=0) + self.fp.write(code) + functions.append((func.name, '_wrap_' + funcname, + methflags, func.docstring)) + functions_coverage.declare_wrapped() + except argtypes.ArgTypeError, ex: + functions_coverage.declare_not_wrapped() + sys.stderr.write('Could not write function %s: %s\n' + % (func.name, str(ex))) + + # Now try to see if there are any defined in the override + for funcname in self.overrides.get_functions(): + try: + data = self.overrides.function(funcname) + self.write_function(funcname, data) + methflags = self.get_methflags(funcname) + functions.append((funcname, '_wrap_' + funcname, + methflags, 'NULL')) + functions_coverage.declare_wrapped() + except argtypes.ArgTypeError, ex: + functions_coverage.declare_not_wrapped() + sys.stderr.write('Could not write function %s: %s\n' + % (funcname, str(ex))) + return functions + + def write_functions(self, writer, prefix): + self.fp.write('\n/* ----------- functions ----------- */\n\n') + functions = [] + func_infos = self._write_function_bodies() + + # If we have a dynamic namespace, write symbol and attribute getter + if self.overrides.dynamicnamespace: + self._write_get_symbol_names(writer, func_infos) + self._write_get_symbol(writer, func_infos) + for obj, bases in writer.get_classes(): + self.fp.write("""static PyTypeObject * +%s_register_type(const gchar *name, PyObject *unused) +{ + PyObject *m = PyImport_ImportModule("gtk"); + PyObject *d = PyModule_GetDict(m); +""" % obj.c_name) + writer.write_class(obj, bases, indent=1) + self.fp.write( + ' return (%s)PyDict_GetItemString(d, "%s");\n' % ( + 'PyTypeObject*', obj.name)) + self.fp.write("}\n") + + functions.append(' { "_get_symbol_names", ' + '(PyCFunction)_wrap__get_symbol_names, ' + 'METH_NOARGS, NULL },\n') + functions.append(' { "_get_symbol", ' + '(PyCFunction)_wrap__get_symbol, ' + 'METH_VARARGS, NULL },\n') + else: + for name, cname, flags, docstring in func_infos: + functions.append(self.methdef_tmpl % dict(name=name, + cname=cname, + flags=flags, + docstring=docstring)) + + # write the PyMethodDef structure + functions.append(' { NULL, NULL, 0, NULL }\n') + + self.fp.write('const PyMethodDef ' + prefix + '_functions[] = {\n') + self.fp.write(string.join(functions, '')) + self.fp.write('};\n\n') + +class GObjectWrapper(Wrapper): + constructor_tmpl = ( + 'static int\n' + '_wrap_%(cname)s(PyGObject *self%(extraparams)s)\n' + '{\n' + '%(varlist)s' + '%(parseargs)s' + '%(codebefore)s' + ' self->obj = (GObject *)%(cname)s(%(arglist)s);\n' + '%(codeafter)s\n' + ' if (!self->obj) {\n' + ' PyErr_SetString(PyExc_RuntimeError, ' + '"could not create %(typename)s object");\n' + ' return -1;\n' + ' }\n' + '%(aftercreate)s' + ' pygobject_register_wrapper((PyObject *)self);\n' + ' return 0;\n' + '}\n\n' + ) + + method_tmpl = ( + 'static PyObject *\n' + '_wrap_%(cname)s(PyGObject *self%(extraparams)s)\n' + '{\n' + '%(varlist)s' + '%(parseargs)s' + '%(codebefore)s' + ' %(begin_allow_threads)s\n' + ' %(setreturn)s%(cname)s(%(cast)s(self->obj)%(arglist)s);\n' + ' %(end_allow_threads)s\n' + '%(codeafter)s\n' + '}\n\n' + ) + def __init__(self, parser, objinfo, overrides, fp=FileOutput(sys.stdout)): + Wrapper.__init__(self, parser, objinfo, overrides, fp) + if self.objinfo: + self.castmacro = string.replace(self.objinfo.typecode, + '_TYPE_', '_', 1) + + def get_initial_class_substdict(self): + return { 'tp_basicsize' : 'PyGObject', + 'tp_weaklistoffset' : 'offsetof(PyGObject, weakreflist)', + 'tp_dictoffset' : 'offsetof(PyGObject, inst_dict)' } + + def get_field_accessor(self, fieldname): + castmacro = string.replace(self.objinfo.typecode, '_TYPE_', '_', 1) + return '%s(pygobject_get(self))->%s' % (castmacro, fieldname) + + def get_initial_constructor_substdict(self, constructor): + substdict = Wrapper.get_initial_constructor_substdict(self, + constructor) + if not constructor.caller_owns_return: + substdict['aftercreate'] = " g_object_ref(self->obj);\n" + else: + substdict['aftercreate'] = '' + return substdict + + def get_initial_method_substdict(self, method): + substdict = Wrapper.get_initial_method_substdict(self, method) + substdict['cast'] = string.replace(self.objinfo.typecode, + '_TYPE_', '_', 1) + return substdict + + def write_default_constructor(self): + try: + parent = self.parser.find_object(self.objinfo.parent) + except ValueError: + parent = None + if parent is not None: + ## just like the constructor is inheritted, we should + # inherit the new API compatibility flag + self.objinfo.has_new_constructor_api = ( + parent.has_new_constructor_api) + elif self.objinfo.parent == 'GObject': + self.objinfo.has_new_constructor_api = True + return '0' + + def write_property_based_constructor(self, constructor): + self.objinfo.has_new_constructor_api = True + out = self.fp + print >> out, "static int" + print >> out, '_wrap_%s(PyGObject *self, PyObject *args,' \ + ' PyObject *kwargs)\n{' % constructor.c_name + if constructor.params: + s = " GType obj_type = pyg_type_from_object((PyObject *) self);" + print >> out, s + + def py_str_list_to_c(arg): + if arg: + return "{" + ", ".join( + map(lambda s: '"' + s + '"', arg)) + ", NULL }" + else: + return "{ NULL }" + + classname = '%s.%s' % (self.overrides.modulename, + self.objinfo.name) + + if constructor.params: + mandatory_arguments = [param for param in constructor.params + if not param.optional] + optional_arguments = [param for param in constructor.params + if param.optional] + arg_names = py_str_list_to_c( + [param.argname + for param in mandatory_arguments + optional_arguments]) + + prop_names = py_str_list_to_c( + [param.pname + for param in mandatory_arguments + optional_arguments]) + + print >> out, " GParameter params[%i];" % \ + len(constructor.params) + print >> out, " PyObject *parsed_args[%i] = {NULL, };" % \ + len(constructor.params) + print >> out, " char *arg_names[] = %s;" % arg_names + print >> out, " char *prop_names[] = %s;" % prop_names + print >> out, " guint nparams, i;" + print >> out + if constructor.deprecated is not None: + out.write( + ' if (PyErr_Warn(PyExc_DeprecationWarning, ' + '"%s") < 0)\n' % + constructor.deprecated) + print >> out, ' return -1;' + print >> out + out.write(" if (!PyArg_ParseTupleAndKeywords(args, kwargs, ") + template = '"' + if mandatory_arguments: + template += "O"*len(mandatory_arguments) + if optional_arguments: + template += "|" + "O"*len(optional_arguments) + template += ':%s.__init__"' % classname + print >> out, template, ", arg_names", + for i in range(len(constructor.params)): + print >> out, ", &parsed_args[%i]" % i, + + out.write( + "))\n" + " return -1;\n" + "\n" + " memset(params, 0, sizeof(GParameter)*%i);\n" + " if (!pyg_parse_constructor_args(obj_type, arg_names,\n" + " prop_names, params, \n" + " &nparams, parsed_args))\n" + " return -1;\n" + " pygobject_constructv(self, nparams, params);\n" + " for (i = 0; i < nparams; ++i)\n" + " g_value_unset(¶ms[i].value);\n" + % len(constructor.params)) + else: + out.write( + " static char* kwlist[] = { NULL };\n" + "\n") + + if constructor.deprecated is not None: + out.write( + ' if (PyErr_Warn(PyExc_DeprecationWarning, "%s") < 0)\n' + ' return -1;\n' + '\n' % constructor.deprecated) + + out.write( + ' if (!PyArg_ParseTupleAndKeywords(args, kwargs,\n' + ' ":%s.__init__",\n' + ' kwlist))\n' + ' return -1;\n' + '\n' + ' pygobject_constructv(self, 0, NULL);\n' % classname) + out.write( + ' if (!self->obj) {\n' + ' PyErr_SetString(\n' + ' PyExc_RuntimeError, \n' + ' "could not create %s object");\n' + ' return -1;\n' + ' }\n' % classname) + + if not constructor.caller_owns_return: + print >> out, " g_object_ref(self->obj);\n" + + out.write( + ' return 0;\n' + '}\n\n') + + return "_wrap_%s" % constructor.c_name + + +class GInterfaceWrapper(GObjectWrapper): + virtual_accessor_tmpl = ( + 'static PyObject *\n' + '_wrap_%(cname)s(PyObject *cls%(extraparams)s)\n' + '{\n' + ' %(vtable)s *iface;\n' + '%(varlist)s' + '%(parseargs)s' + '%(codebefore)s' + ' iface = g_type_interface_peek(' + 'g_type_class_peek(pyg_type_from_object(cls)), %(typecode)s);\n' + ' if (iface->%(virtual)s)\n' + ' %(setreturn)siface->%(virtual)s(%(arglist)s);\n' + ' else {\n' + ' PyErr_SetString(PyExc_NotImplementedError, ' + '"interface method %(name)s not implemented");\n' + ' return NULL;\n' + ' }\n' + '%(codeafter)s\n' + '}\n\n' + ) + + def get_initial_class_substdict(self): + return { 'tp_basicsize' : 'PyObject', + 'tp_weaklistoffset' : '0', + 'tp_dictoffset' : '0'} + + def write_constructor(self): + # interfaces have no constructors ... + return '0' + def write_getsets(self): + # interfaces have no fields ... + return '0' + + def _get_class_virtual_substdict(self, meth, cname, parent): + substdict = self.get_initial_method_substdict(meth) + substdict['virtual'] = meth.name + substdict['cname'] = cname + substdict['typecode'] = self.objinfo.typecode + substdict['vtable'] = self.objinfo.vtable + return substdict + + def write_virtuals(self): + ## Now write reverse method wrappers, which let python code + ## implement interface methods. + # First, get methods from the defs files + klass = self.objinfo.c_name + proxies = [] + for meth in self.parser.find_virtuals(self.objinfo): + method_name = self.objinfo.c_name + "__proxy_do_" + meth.name + if self.overrides.is_ignored(method_name): + continue + try: + if self.overrides.is_overriden(method_name): + if not self.overrides.is_already_included(method_name): + data = self.overrides.override(method_name) + self.write_function(method_name, data) + else: + # write proxy ... + ret, props = argtypes.matcher.get_reverse_ret(meth.ret) + wrapper = reversewrapper.ReverseWrapper( + '_wrap_' + method_name, is_static=True) + wrapper.set_return_type(ret(wrapper, **props)) + wrapper.add_parameter(reversewrapper.PyGObjectMethodParam( + wrapper, "self", method_name="do_" + meth.name, + c_type=(klass + ' *'))) + for param in meth.params: + handler, props = argtypes.matcher.get_reverse( + param.ptype) + props["direction"] = param.pdir + props["nullok"] = param.pnull + wrapper.add_parameter( + handler(wrapper, param.pname, **props)) + buf = reversewrapper.MemoryCodeSink() + wrapper.generate(buf) + self.fp.write(buf.flush()) + proxies.append((fixname(meth.name), '_wrap_' + method_name)) + iproxies_coverage.declare_wrapped() + except argtypes.ArgTypeError, ex: + iproxies_coverage.declare_not_wrapped() + proxies.append((fixname(meth.name), None)) + sys.stderr.write('Could not write interface proxy %s.%s: %s\n' + % (klass, meth.name, str(ex))) + + if not proxies or not [cname for name, cname in proxies if cname]: + return + + ## Write an interface init function for this object + funcname = "__%s__interface_init" % klass + vtable = self.objinfo.vtable + self.fp.write( + '\nstatic void\n' + '%(funcname)s(%(vtable)s *iface, PyTypeObject *pytype)\n' + '{\n' + ' %(vtable)s *parent_iface = ' + 'g_type_interface_peek_parent(iface);\n' + ' PyObject *py_method;\n' + '\n' + % vars()) + + for name, cname in proxies: + do_name = 'do_' + name + if cname is None: + continue + + self.fp.write(( + ' py_method = pytype? PyObject_GetAttrString(' + '(PyObject *) pytype, "%(do_name)s") : NULL;\n' + ' if (py_method && !PyObject_TypeCheck(py_method, ' + '&PyCFunction_Type)) {\n' + ' iface->%(name)s = %(cname)s;\n' + ' } else {\n' + ' PyErr_Clear();\n' + ' if (parent_iface) {\n' + ' iface->%(name)s = parent_iface->%(name)s;\n' + ' }\n' + ' Py_XDECREF(py_method);\n' + ' }\n' + ) % vars()) + self.fp.write('}\n\n') + interface_info = "__%s__iinfo" % klass + self.fp.write(''' +static const GInterfaceInfo %s = { + (GInterfaceInitFunc) %s, + NULL, + NULL +}; +''' % (interface_info, funcname)) + self.objinfo.interface_info = interface_info + +class GBoxedWrapper(Wrapper): + constructor_tmpl = ( + 'static int\n' + '_wrap_%(cname)s(PyGBoxed *self%(extraparams)s)\n' + '{\n' + '%(varlist)s' + '%(parseargs)s' + '%(codebefore)s' + ' self->gtype = %(typecode)s;\n' + ' self->free_on_dealloc = FALSE;\n' + ' self->boxed = %(cname)s(%(arglist)s);\n' + '%(codeafter)s\n' + ' if (!self->boxed) {\n' + ' PyErr_SetString(PyExc_RuntimeError, ' + '"could not create %(typename)s object");\n' + ' return -1;\n' + ' }\n' + ' self->free_on_dealloc = TRUE;\n' + ' return 0;\n' + '}\n\n' + ) + + method_tmpl = ( + 'static PyObject *\n' + '_wrap_%(cname)s(PyObject *self%(extraparams)s)\n' + '{\n' + '%(varlist)s' + '%(parseargs)s' + '%(codebefore)s' + ' %(begin_allow_threads)s\n' + ' %(setreturn)s%(cname)s(pyg_boxed_get(self, ' + '%(typename)s)%(arglist)s);\n' + ' %(end_allow_threads)s\n' + '%(codeafter)s\n' + '}\n\n' + ) + + def get_initial_class_substdict(self): + return { 'tp_basicsize' : 'PyGBoxed', + 'tp_weaklistoffset' : '0', + 'tp_dictoffset' : '0' } + + def get_field_accessor(self, fieldname): + return 'pyg_boxed_get(self, %s)->%s' % (self.objinfo.c_name, fieldname) + + def get_initial_constructor_substdict(self, constructor): + substdict = Wrapper.get_initial_constructor_substdict( + self, constructor) + substdict['typecode'] = self.objinfo.typecode + return substdict + +class GPointerWrapper(GBoxedWrapper): + constructor_tmpl = ( + 'static int\n' + '_wrap_%(cname)s(PyGPointer *self%(extraparams)s)\n' + '{\n' + '%(varlist)s' + '%(parseargs)s' + '%(codebefore)s' + ' self->gtype = %(typecode)s;\n' + ' self->pointer = %(cname)s(%(arglist)s);\n' + '%(codeafter)s\n' + ' if (!self->pointer) {\n' + ' PyErr_SetString(PyExc_RuntimeError, ' + '"could not create %(typename)s object");\n' + ' return -1;\n' + ' }\n' + ' return 0;\n' + '}\n\n' + ) + + method_tmpl = ( + 'static PyObject *\n' + '_wrap_%(cname)s(PyObject *self%(extraparams)s)\n' + '{\n' + '%(varlist)s' + '%(parseargs)s' + '%(codebefore)s' + ' %(setreturn)s%(cname)s(pyg_pointer_get(self, ' + '%(typename)s)%(arglist)s);\n' + '%(codeafter)s\n' + '}\n\n' + ) + + def get_initial_class_substdict(self): + return { 'tp_basicsize' : 'PyGPointer', + 'tp_weaklistoffset' : '0', + 'tp_dictoffset' : '0' } + + def get_field_accessor(self, fieldname): + return 'pyg_pointer_get(self, %s)->%s' % (self.objinfo.c_name, + fieldname) + + def get_initial_constructor_substdict(self, constructor): + substdict = Wrapper.get_initial_constructor_substdict( + self, constructor) + substdict['typecode'] = self.objinfo.typecode + return substdict + +class SourceWriter: + def __init__(self, parser, overrides, prefix, fp=FileOutput(sys.stdout)): + self.parser = parser + self.overrides = overrides + self.prefix = prefix + self.fp = fp + + def write(self, py_ssize_t_clean=False): + argtypes.py_ssize_t_clean = py_ssize_t_clean + + self.write_headers(py_ssize_t_clean) + self.write_imports() + self.write_type_declarations() + self.write_body() + self.write_classes() + + wrapper = Wrapper(self.parser, None, self.overrides, self.fp) + wrapper.write_functions(self, self.prefix) + + if not self.overrides.dynamicnamespace: + self.write_enums() + self.write_extension_init() + self.write_registers() + + argtypes.py_ssize_t_clean = False + + def write_headers(self, py_ssize_t_clean): + self.fp.write('/* -- THIS FILE IS GENERATED - DO NOT EDIT */') + self.fp.write('/* -*- Mode: C; c-basic-offset: 4 -*- */\n\n') + if py_ssize_t_clean: + self.fp.write('#define PY_SSIZE_T_CLEAN\n') + self.fp.write('#include <Python.h>\n\n\n') + if py_ssize_t_clean: + self.fp.write(''' + +#if PY_VERSION_HEX < 0x02050000 +typedef int Py_ssize_t; +#define PY_SSIZE_T_MAX INT_MAX +#define PY_SSIZE_T_MIN INT_MIN +typedef inquiry lenfunc; +typedef intargfunc ssizeargfunc; +typedef intobjargproc ssizeobjargproc; +#endif + +''') + self.fp.write(self.overrides.get_headers()) + self.fp.resetline() + self.fp.write('\n\n') + + def write_imports(self): + self.fp.write('/* ---------- types from other modules ---------- */\n') + for module, pyname, cname, importing_for in self.overrides.get_imports(): + if importing_for is None or is_registered_object(importing_for): + self.fp.write('static PyTypeObject *_%s;\n' % cname) + self.fp.write('#define %s (*_%s)\n' % (cname, cname)) + self.fp.write('\n\n') + + def write_type_declarations(self): + #todo use 'static' if used only in one file + self.fp.write('/* ---------- forward type declarations ---------- */\n') + for obj in self.parser.boxes: + if not self.overrides.is_type_ignored(obj.c_name): + self.fp.write('PyTypeObject G_GNUC_INTERNAL Py' + obj.c_name + '_Type;\n') + for obj in self.parser.objects: + if not self.overrides.is_type_ignored(obj.c_name): + self.fp.write('PyTypeObject G_GNUC_INTERNAL Py' + obj.c_name + '_Type;\n') + for interface in self.parser.interfaces: + if not self.overrides.is_type_ignored(interface.c_name): + self.fp.write('PyTypeObject G_GNUC_INTERNAL Py' + interface.c_name + '_Type;\n') + self.fp.write('\n') + + def write_body(self): + self.fp.write(self.overrides.get_body()) + self.fp.resetline() + self.fp.write('\n\n') + + def _sort_parent_children(self, objects): + objects = list(objects) + modified = True + while modified: + modified = False + parent_index = None + child_index = None + for i, obj in enumerate(objects): + if obj.parent == 'GObject': + continue + if obj.parent not in [info.c_name for info in objects[:i]]: + for j, info in enumerate(objects[i+1:]): + if info.c_name == obj.parent: + parent_index = i + 1 + j + child_index = i + break + else: + continue + break + if child_index is not None and parent_index is not None: + if child_index != parent_index: + objects.insert(child_index, objects.pop(parent_index)) + modified = True + return objects + + def write_classes(self): + ## Sort the objects, so that we generate code for the parent types + ## before their children. + objects = self._sort_parent_children(self.parser.objects) + + for klass, items in ((GBoxedWrapper, self.parser.boxes), + (GPointerWrapper, self.parser.pointers), + (GObjectWrapper, objects), + (GInterfaceWrapper, self.parser.interfaces)): + for item in items: + instance = klass(self.parser, item, self.overrides, self.fp) + instance.write_class() + self.fp.write('\n') + + def get_enums(self): + enums = [] + for enum in self.parser.enums: + if self.overrides.is_type_ignored(enum.c_name): + continue + enums.append(enum) + return enums + + def write_enums(self): + if not self.parser.enums: + return + + self.fp.write('\n/* ----------- enums and flags ----------- */\n\n') + self.fp.write( + 'void\n' + self.prefix + + '_add_constants(PyObject *module, const gchar *strip_prefix)\n{\n') + + self.fp.write( + '#ifdef VERSION\n' + ' PyModule_AddStringConstant(module, "__version__", VERSION);\n' + '#endif\n') + + for enum in self.get_enums(): + if enum.typecode is None: + for nick, value in enum.values: + self.fp.write( + ' PyModule_AddIntConstant(module, ' + '(char *) pyg_constant_strip_prefix("%s", strip_prefix), %s);\n' + % (value, value)) + else: + if enum.deftype == 'enum': + self.fp.write(' pyg_enum_add(module, "%s", strip_prefix, %s);\n' + % (enum.name, enum.typecode)) + else: + self.fp.write(' pyg_flags_add(module, "%s", strip_prefix, %s);\n' + % (enum.name, enum.typecode)) + + self.fp.write('\n') + self.fp.write(' if (PyErr_Occurred())\n') + self.fp.write(' PyErr_Print();\n') + self.fp.write('}\n\n') + + def write_object_imports(self, retval=''): + imports = self.overrides.get_imports()[:] + if not imports: + return + + bymod = {} + for module, pyname, cname, importing_for in imports: + if importing_for is None or is_registered_object(importing_for): + bymod.setdefault(module, []).append((pyname, cname)) + self.fp.write(' PyObject *module;\n\n') + for module in bymod: + self.fp.write( + ' if ((module = PyImport_ImportModule("%s")) != NULL) {\n' + % module) + #self.fp.write( + # ' PyObject *moddict = PyModule_GetDict(module);\n\n') + for pyname, cname in bymod[module]: + #self.fp.write( + # ' _%s = (PyTypeObject *)PyDict_GetItemString(' + # 'moddict, "%s");\n' % (cname, pyname)) + self.fp.write( + ' _%s = (PyTypeObject *)PyObject_GetAttrString(' + 'module, "%s");\n' % (cname, pyname)) + self.fp.write(' if (_%s == NULL) {\n' % cname) + self.fp.write(' PyErr_SetString(PyExc_ImportError,\n') + self.fp.write(' "cannot import name %s from %s");\n' + % (pyname, module)) + self.fp.write(' return %s;\n' % retval) + self.fp.write(' }\n') + self.fp.write(' } else {\n') + self.fp.write(' PyErr_SetString(PyExc_ImportError,\n') + self.fp.write(' "could not import %s");\n' % module) + self.fp.write(' return %s;\n' % retval) + self.fp.write(' }\n') + self.fp.write('\n') + + def write_extension_init(self): + self.fp.write('/* initialise stuff extension classes */\n') + self.fp.write('void\n' + self.prefix + '_register_classes(PyObject *d)\n{\n') + self.write_object_imports() + self.fp.write(self.overrides.get_init() + '\n') + self.fp.resetline() + + def get_classes(self): + objects = self.parser.objects[:] + pos = 0 + while pos < len(objects): + parent = objects[pos].parent + for i in range(pos+1, len(objects)): + if objects[i].c_name == parent: + objects.insert(i+1, objects[pos]) + del objects[pos] + break + else: + pos = pos + 1 + + retval = [] + for obj in objects: + if self.overrides.is_type_ignored(obj.c_name): + continue + bases = [] + if obj.parent != None: + bases.append(obj.parent) + bases = bases + obj.implements + retval.append((obj, bases)) + + return retval + + def write_registers(self): + for boxed in self.parser.boxes: + if not self.overrides.is_type_ignored(boxed.c_name): + self.fp.write(' pyg_register_boxed(d, "' + boxed.name + + '", ' + boxed.typecode + + ', &Py' + boxed.c_name + + '_Type);\n') + for pointer in self.parser.pointers: + if not self.overrides.is_type_ignored(pointer.c_name): + self.fp.write(' pyg_register_pointer(d, "' + pointer.name + + '", ' + pointer.typecode + + ', &Py' + pointer.c_name + '_Type);\n') + for interface in self.parser.interfaces: + if not self.overrides.is_type_ignored(interface.c_name): + self.fp.write(' pyg_register_interface(d, "' + + interface.name + '", '+ interface.typecode + + ', &Py' + interface.c_name + '_Type);\n') + if interface.interface_info is not None: + self.fp.write(' pyg_register_interface_info(%s, &%s);\n' % + (interface.typecode, interface.interface_info)) + + if not self.overrides.dynamicnamespace: + for obj, bases in self.get_classes(): + self.write_class(obj, bases) + else: + for obj, bases in self.get_classes(): + self.fp.write( + ' pyg_type_register_custom_callback("%s", ' + '(PyGTypeRegistrationFunction)%s_register_type, d);\n' % + (obj.c_name, obj.c_name)) + + self.fp.write('}\n') + + def _can_direct_ref(self, base): + if not self.overrides.dynamicnamespace: + return True + if base == 'GObject': + return True + obj = get_object_by_name(base) + if obj.module.lower() != self.overrides.modulename: + return True + return False + + def write_class(self, obj, bases, indent=1): + indent_str = ' ' * (indent * 4) + if bases: + bases_str = 'Py_BuildValue("(%s)"' % (len(bases) * 'O') + + for base in bases: + if self._can_direct_ref(base): + bases_str += ', &Py%s_Type' % base + else: + baseobj = get_object_by_name(base) + bases_str += ', PyObject_GetAttrString(m, "%s")' % baseobj.name + bases_str += ')' + else: + bases_str = 'NULL' + + self.fp.write( + '%(indent)spygobject_register_class(d, "%(c_name)s", %(typecode)s, &Py%(c_name)s_Type, %(bases)s);\n' + % dict(indent=indent_str, c_name=obj.c_name, typecode=obj.typecode, bases=bases_str)) + + if obj.has_new_constructor_api: + self.fp.write( + indent_str + 'pyg_set_object_has_new_constructor(%s);\n' % + obj.typecode) + else: + print >> sys.stderr, ( + "Warning: Constructor for %s needs to be updated to new API\n" + " See http://live.gnome.org/PyGTK_2fWhatsNew28" + "#update-constructors") % obj.c_name + + if obj.class_init_func is not None: + self.fp.write( + indent_str + 'pyg_register_class_init(%s, %s);\n' % + (obj.typecode, obj.class_init_func)) + +_objects = {} + +def is_registered_object(c_name): + return c_name in _objects + +def get_object_by_name(c_name): + global _objects + return _objects[c_name] + +def register_types(parser): + global _objects + for boxed in parser.boxes: + argtypes.matcher.register_boxed(boxed.c_name, boxed.typecode) + _objects[boxed.c_name] = boxed + for pointer in parser.pointers: + argtypes.matcher.register_pointer(pointer.c_name, pointer.typecode) + for obj in parser.objects: + argtypes.matcher.register_object(obj.c_name, obj.parent, obj.typecode) + _objects[obj.c_name] = obj + for iface in parser.interfaces: + argtypes.matcher.register_object(iface.c_name, None, iface.typecode) + _objects[iface.c_name] = iface + for enum in parser.enums: + if enum.deftype == 'flags': + argtypes.matcher.register_flag(enum.c_name, enum.typecode) + else: + argtypes.matcher.register_enum(enum.c_name, enum.typecode) + +usage = 'usage: codegen.py [-o overridesfile] [-p prefix] defsfile' +def main(argv): + o = override.Overrides() + prefix = 'pygtk' + outfilename = None + errorfilename = None + opts, args = getopt.getopt(argv[1:], "o:p:r:t:D:I:", + ["override=", "prefix=", "register=", "outfilename=", + "load-types=", "errorfilename=", "py_ssize_t-clean"]) + defines = {} # -Dkey[=val] options + py_ssize_t_clean = False + for opt, arg in opts: + if opt in ('-o', '--override'): + o = override.Overrides(arg) + elif opt in ('-p', '--prefix'): + prefix = arg + elif opt in ('-r', '--register'): + # Warning: user has to make sure all -D options appear before -r + p = defsparser.DefsParser(arg, defines) + p.startParsing() + register_types(p) + del p + elif opt == '--outfilename': + outfilename = arg + elif opt == '--errorfilename': + errorfilename = arg + elif opt in ('-t', '--load-types'): + globals = {} + execfile(arg, globals) + elif opt == '-D': + nameval = arg.split('=') + try: + defines[nameval[0]] = nameval[1] + except IndexError: + defines[nameval[0]] = None + elif opt == '-I': + defsparser.include_path.insert(0, arg) + elif opt == '--py_ssize_t-clean': + py_ssize_t_clean = True + if len(args) < 1: + print >> sys.stderr, usage + return 1 + if errorfilename: + sys.stderr = open(errorfilename, "w") + p = defsparser.DefsParser(args[0], defines) + if not outfilename: + outfilename = os.path.splitext(args[0])[0] + '.c' + + p.startParsing() + + register_types(p) + sw = SourceWriter(p, o, prefix, FileOutput(sys.stdout, outfilename)) + sw.write(py_ssize_t_clean) + + functions_coverage.printstats() + methods_coverage.printstats() + vproxies_coverage.printstats() + vaccessors_coverage.printstats() + iproxies_coverage.printstats() + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/codegen/createdefs.py b/codegen/createdefs.py new file mode 100755 index 0000000..84d50b1 --- /dev/null +++ b/codegen/createdefs.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +import sys + +def main(args): + output = args[1] + input = args[2:] + outfd = open(output, 'w') + outfd.write(';; -*- scheme -*-\n') + outfd.write(';; THIS FILE IS GENERATED - DO NOT EDIT\n') + for filename in input: + outfd.write('(include "%s")\n' % filename) + outfd.close() + + return 0 + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/codegen/definitions.py b/codegen/definitions.py new file mode 100644 index 0000000..aca5adb --- /dev/null +++ b/codegen/definitions.py @@ -0,0 +1,575 @@ +# -*- Mode: Python; py-indent-offset: 4 -*- +import copy +import sys + +def get_valid_scheme_definitions(defs): + return [x for x in defs if isinstance(x, tuple) and len(x) >= 2] + +def unescape(s): + s = s.replace('\r\n', '\\r\\n').replace('\t', '\\t') + return s.replace('\r', '\\r').replace('\n', '\\n') + +def make_docstring(lines): + return "(char *) " + '\n'.join(['"%s"' % unescape(s) for s in lines]) + +# New Parameter class, wich emulates a tuple for compatibility reasons +class Parameter(object): + def __init__(self, ptype, pname, pdflt, pnull, pdir=None): + self.ptype = ptype + self.pname = pname + self.pdflt = pdflt + self.pnull = pnull + self.pdir = pdir + + def __len__(self): return 4 + def __getitem__(self, i): + return (self.ptype, self.pname, self.pdflt, self.pnull)[i] + + def merge(self, old): + if old.pdflt is not None: + self.pdflt = old.pdflt + if old.pnull is not None: + self.pnull = old.pnull + +# We currently subclass 'str' to make impact on the rest of codegen as +# little as possible. Later we can subclass 'object' instead, but +# then we must find and adapt all places which expect return types to +# be strings. +class ReturnType(str): + def __new__(cls, *args, **kwds): + return str.__new__(cls, *args[:1]) + def __init__(self, type_name, optional=False): + str.__init__(self) + self.optional = optional + +# Parameter for property based constructors +class Property(object): + def __init__(self, pname, optional, argname): + self.pname = pname + self.optional = optional + self.argname = argname + + def __len__(self): return 4 + def __getitem__(self, i): + return ('', self.pname, self.optional, self.argname)[i] + + def merge(self, old): + if old.optional is not None: + self.optional = old.optional + if old.argname is not None: + self.argname = old.argname + + +class Definition(object): + docstring = "NULL" + + def py_name(self): + return '%s.%s' % (self.module, self.name) + + py_name = property(py_name) + + def __init__(self, *args): + """Create a new defs object of this type. The arguments are the + components of the definition""" + raise RuntimeError, "this is an abstract class" + + def merge(self, old): + """Merge in customisations from older version of definition""" + raise RuntimeError, "this is an abstract class" + + def write_defs(self, fp=sys.stdout): + """write out this definition in defs file format""" + raise RuntimeError, "this is an abstract class" + + def guess_return_value_ownership(self): + "return 1 if caller owns return value" + if getattr(self, 'is_constructor_of', False): + self.caller_owns_return = True + elif self.ret in ('char*', 'gchar*', 'string'): + self.caller_owns_return = True + else: + self.caller_owns_return = False + + +class ObjectDef(Definition): + def __init__(self, name, *args): + self.name = name + self.module = None + self.parent = None + self.c_name = None + self.typecode = None + self.fields = [] + self.implements = [] + self.class_init_func = None + self.has_new_constructor_api = False + for arg in get_valid_scheme_definitions(args): + if arg[0] == 'in-module': + self.module = arg[1] + elif arg[0] == 'docstring': + self.docstring = make_docstring(arg[1:]) + elif arg[0] == 'parent': + self.parent = arg[1] + elif arg[0] == 'c-name': + self.c_name = arg[1] + elif arg[0] == 'gtype-id': + self.typecode = arg[1] + elif arg[0] == 'fields': + for parg in arg[1:]: + self.fields.append((parg[0], parg[1])) + elif arg[0] == 'implements': + self.implements.append(arg[1]) + def merge(self, old): + # currently the .h parser doesn't try to work out what fields of + # an object structure should be public, so we just copy the list + # from the old version ... + self.fields = old.fields + self.implements = old.implements + def write_defs(self, fp=sys.stdout): + fp.write('(define-object ' + self.name + '\n') + if self.module: + fp.write(' (in-module "' + self.module + '")\n') + if self.parent != (None, None): + fp.write(' (parent "' + self.parent + '")\n') + for interface in self.implements: + fp.write(' (implements "' + interface + '")\n') + if self.c_name: + fp.write(' (c-name "' + self.c_name + '")\n') + if self.typecode: + fp.write(' (gtype-id "' + self.typecode + '")\n') + if self.fields: + fp.write(' (fields\n') + for (ftype, fname) in self.fields: + fp.write(' \'("' + ftype + '" "' + fname + '")\n') + fp.write(' )\n') + fp.write(')\n\n') + +class InterfaceDef(Definition): + def __init__(self, name, *args): + self.name = name + self.module = None + self.c_name = None + self.typecode = None + self.vtable = None + self.fields = [] + self.interface_info = None + for arg in get_valid_scheme_definitions(args): + if arg[0] == 'in-module': + self.module = arg[1] + elif arg[0] == 'docstring': + self.docstring = make_docstring(arg[1:]) + elif arg[0] == 'c-name': + self.c_name = arg[1] + elif arg[0] == 'gtype-id': + self.typecode = arg[1] + elif arg[0] == 'vtable': + self.vtable = arg[1] + if self.vtable is None: + self.vtable = self.c_name + "Iface" + def write_defs(self, fp=sys.stdout): + fp.write('(define-interface ' + self.name + '\n') + if self.module: + fp.write(' (in-module "' + self.module + '")\n') + if self.c_name: + fp.write(' (c-name "' + self.c_name + '")\n') + if self.typecode: + fp.write(' (gtype-id "' + self.typecode + '")\n') + fp.write(')\n\n') + +class EnumDef(Definition): + def __init__(self, name, *args): + self.deftype = 'enum' + self.name = name + self.in_module = None + self.c_name = None + self.typecode = None + self.values = [] + for arg in get_valid_scheme_definitions(args): + if arg[0] == 'in-module': + self.in_module = arg[1] + elif arg[0] == 'c-name': + self.c_name = arg[1] + elif arg[0] == 'gtype-id': + self.typecode = arg[1] + elif arg[0] == 'values': + for varg in arg[1:]: + self.values.append((varg[0], varg[1])) + def merge(self, old): + pass + def write_defs(self, fp=sys.stdout): + fp.write('(define-' + self.deftype + ' ' + self.name + '\n') + if self.in_module: + fp.write(' (in-module "' + self.in_module + '")\n') + fp.write(' (c-name "' + self.c_name + '")\n') + fp.write(' (gtype-id "' + self.typecode + '")\n') + if self.values: + fp.write(' (values\n') + for name, val in self.values: + fp.write(' \'("' + name + '" "' + val + '")\n') + fp.write(' )\n') + fp.write(')\n\n') + +class FlagsDef(EnumDef): + def __init__(self, *args): + apply(EnumDef.__init__, (self,) + args) + self.deftype = 'flags' + +class BoxedDef(Definition): + def __init__(self, name, *args): + self.name = name + self.module = None + self.c_name = None + self.typecode = None + self.copy = None + self.release = None + self.fields = [] + for arg in get_valid_scheme_definitions(args): + if arg[0] == 'in-module': + self.module = arg[1] + elif arg[0] == 'c-name': + self.c_name = arg[1] + elif arg[0] == 'gtype-id': + self.typecode = arg[1] + elif arg[0] == 'copy-func': + self.copy = arg[1] + elif arg[0] == 'release-func': + self.release = arg[1] + elif arg[0] == 'fields': + for parg in arg[1:]: + self.fields.append((parg[0], parg[1])) + def merge(self, old): + # currently the .h parser doesn't try to work out what fields of + # an object structure should be public, so we just copy the list + # from the old version ... + self.fields = old.fields + def write_defs(self, fp=sys.stdout): + fp.write('(define-boxed ' + self.name + '\n') + if self.module: + fp.write(' (in-module "' + self.module + '")\n') + if self.c_name: + fp.write(' (c-name "' + self.c_name + '")\n') + if self.typecode: + fp.write(' (gtype-id "' + self.typecode + '")\n') + if self.copy: + fp.write(' (copy-func "' + self.copy + '")\n') + if self.release: + fp.write(' (release-func "' + self.release + '")\n') + if self.fields: + fp.write(' (fields\n') + for (ftype, fname) in self.fields: + fp.write(' \'("' + ftype + '" "' + fname + '")\n') + fp.write(' )\n') + fp.write(')\n\n') + +class PointerDef(Definition): + def __init__(self, name, *args): + self.name = name + self.module = None + self.c_name = None + self.typecode = None + self.fields = [] + for arg in get_valid_scheme_definitions(args): + if arg[0] == 'in-module': + self.module = arg[1] + elif arg[0] == 'c-name': + self.c_name = arg[1] + elif arg[0] == 'gtype-id': + self.typecode = arg[1] + elif arg[0] == 'fields': + for parg in arg[1:]: + self.fields.append((parg[0], parg[1])) + def merge(self, old): + # currently the .h parser doesn't try to work out what fields of + # an object structure should be public, so we just copy the list + # from the old version ... + self.fields = old.fields + def write_defs(self, fp=sys.stdout): + fp.write('(define-pointer ' + self.name + '\n') + if self.module: + fp.write(' (in-module "' + self.module + '")\n') + if self.c_name: + fp.write(' (c-name "' + self.c_name + '")\n') + if self.typecode: + fp.write(' (gtype-id "' + self.typecode + '")\n') + if self.fields: + fp.write(' (fields\n') + for (ftype, fname) in self.fields: + fp.write(' \'("' + ftype + '" "' + fname + '")\n') + fp.write(' )\n') + fp.write(')\n\n') + +class MethodDefBase(Definition): + def __init__(self, name, *args): + dump = 0 + self.name = name + self.ret = None + self.caller_owns_return = None + self.unblock_threads = None + self.c_name = None + self.typecode = None + self.of_object = None + self.params = [] # of form (type, name, default, nullok) + self.varargs = 0 + self.deprecated = None + for arg in get_valid_scheme_definitions(args): + if arg[0] == 'of-object': + self.of_object = arg[1] + elif arg[0] == 'docstring': + self.docstring = make_docstring(arg[1:]) + elif arg[0] == 'c-name': + self.c_name = arg[1] + elif arg[0] == 'gtype-id': + self.typecode = arg[1] + elif arg[0] == 'return-type': + type_name = arg[1] + optional = False + for prop in arg[2:]: + if prop[0] == 'optional': + optional = True + self.ret = ReturnType(type_name, optional) + elif arg[0] == 'caller-owns-return': + self.caller_owns_return = arg[1] in ('t', '#t') + elif arg[0] == 'unblock-threads': + self.unblock_threads = arg[1] in ('t', '#t') + elif arg[0] == 'parameters': + for parg in arg[1:]: + ptype = parg[0] + pname = parg[1] + pdflt = None + pnull = 0 + pdir = None + for farg in parg[2:]: + assert isinstance(farg, tuple) + if farg[0] == 'default': + pdflt = farg[1] + elif farg[0] == 'null-ok': + pnull = 1 + elif farg[0] == 'direction': + pdir = farg[1] + self.params.append(Parameter(ptype, pname, pdflt, pnull, pdir)) + elif arg[0] == 'varargs': + self.varargs = arg[1] in ('t', '#t') + elif arg[0] == 'deprecated': + self.deprecated = arg[1] + else: + sys.stderr.write("Warning: %s argument unsupported.\n" + % (arg[0])) + dump = 1 + if dump: + self.write_defs(sys.stderr) + + if self.caller_owns_return is None and self.ret is not None: + self.guess_return_value_ownership() + + def merge(self, old, parmerge): + self.caller_owns_return = old.caller_owns_return + self.varargs = old.varargs + # here we merge extra parameter flags accross to the new object. + if not parmerge: + self.params = copy.deepcopy(old.params) + return + for i in range(len(self.params)): + ptype, pname, pdflt, pnull = self.params[i] + for p2 in old.params: + if p2[1] == pname: + self.params[i] = (ptype, pname, p2[2], p2[3]) + break + def _write_defs(self, fp=sys.stdout): + if self.of_object != (None, None): + fp.write(' (of-object "' + self.of_object + '")\n') + if self.c_name: + fp.write(' (c-name "' + self.c_name + '")\n') + if self.typecode: + fp.write(' (gtype-id "' + self.typecode + '")\n') + if self.caller_owns_return: + fp.write(' (caller-owns-return #t)\n') + if self.unblock_threads: + fp.write(' (unblock_threads #t)\n') + if self.ret: + fp.write(' (return-type "' + self.ret + '")\n') + if self.deprecated: + fp.write(' (deprecated "' + self.deprecated + '")\n') + if self.params: + fp.write(' (parameters\n') + for ptype, pname, pdflt, pnull in self.params: + fp.write(' \'("' + ptype + '" "' + pname +'"') + if pdflt: fp.write(' (default "' + pdflt + '")') + if pnull: fp.write(' (null-ok)') + fp.write(')\n') + fp.write(' )\n') + if self.varargs: + fp.write(' (varargs #t)\n') + fp.write(')\n\n') + + +class MethodDef(MethodDefBase): + def __init__(self, name, *args): + MethodDefBase.__init__(self, name, *args) + for item in ('c_name', 'of_object'): + if self.__dict__[item] == None: + self.write_defs(sys.stderr) + raise RuntimeError, "definition missing required %s" % (item,) + + def write_defs(self, fp=sys.stdout): + fp.write('(define-method ' + self.name + '\n') + self._write_defs(fp) + +class VirtualDef(MethodDefBase): + def write_defs(self, fp=sys.stdout): + fp.write('(define-virtual ' + self.name + '\n') + self._write_defs(fp) + +class FunctionDef(Definition): + def __init__(self, name, *args): + dump = 0 + self.name = name + self.in_module = None + self.is_constructor_of = None + self.ret = None + self.caller_owns_return = None + self.unblock_threads = None + self.c_name = None + self.typecode = None + self.params = [] # of form (type, name, default, nullok) + self.varargs = 0 + self.deprecated = None + for arg in get_valid_scheme_definitions(args): + if arg[0] == 'in-module': + self.in_module = arg[1] + elif arg[0] == 'docstring': + self.docstring = make_docstring(arg[1:]) + elif arg[0] == 'is-constructor-of': + self.is_constructor_of = arg[1] + elif arg[0] == 'c-name': + self.c_name = arg[1] + elif arg[0] == 'gtype-id': + self.typecode = arg[1] + elif arg[0] == 'return-type': + self.ret = arg[1] + elif arg[0] == 'caller-owns-return': + self.caller_owns_return = arg[1] in ('t', '#t') + elif arg[0] == 'unblock-threads': + self.unblock_threads = arg[1] in ('t', '#t') + elif arg[0] == 'parameters': + for parg in arg[1:]: + ptype = parg[0] + pname = parg[1] + pdflt = None + pnull = 0 + for farg in parg[2:]: + if farg[0] == 'default': + pdflt = farg[1] + elif farg[0] == 'null-ok': + pnull = 1 + self.params.append(Parameter(ptype, pname, pdflt, pnull)) + elif arg[0] == 'properties': + if self.is_constructor_of is None: + print >> sys.stderr, "Warning: (properties ...) "\ + "is only valid for constructors" + for prop in arg[1:]: + pname = prop[0] + optional = False + argname = pname + for farg in prop[1:]: + if farg[0] == 'optional': + optional = True + elif farg[0] == 'argname': + argname = farg[1] + self.params.append(Property(pname, optional, argname)) + elif arg[0] == 'varargs': + self.varargs = arg[1] in ('t', '#t') + elif arg[0] == 'deprecated': + self.deprecated = arg[1] + else: + sys.stderr.write("Warning: %s argument unsupported\n" + % (arg[0],)) + dump = 1 + if dump: + self.write_defs(sys.stderr) + + if self.caller_owns_return is None and self.ret is not None: + self.guess_return_value_ownership() + for item in ('c_name',): + if self.__dict__[item] == None: + self.write_defs(sys.stderr) + raise RuntimeError, "definition missing required %s" % (item,) + + _method_write_defs = MethodDef.__dict__['write_defs'] + + def merge(self, old, parmerge): + self.caller_owns_return = old.caller_owns_return + self.varargs = old.varargs + if not parmerge: + self.params = copy.deepcopy(old.params) + return + # here we merge extra parameter flags accross to the new object. + def merge_param(param): + for old_param in old.params: + if old_param.pname == param.pname: + if isinstance(old_param, Property): + # h2def never scans Property's, therefore if + # we have one it was manually written, so we + # keep it. + return copy.deepcopy(old_param) + else: + param.merge(old_param) + return param + raise RuntimeError, "could not find %s in old_parameters %r" % ( + param.pname, [p.pname for p in old.params]) + try: + self.params = map(merge_param, self.params) + except RuntimeError: + # parameter names changed and we can't find a match; it's + # safer to keep the old parameter list untouched. + self.params = copy.deepcopy(old.params) + + if not self.is_constructor_of: + try: + self.is_constructor_of = old.is_constructor_of + except AttributeError: + pass + if isinstance(old, MethodDef): + self.name = old.name + # transmogrify from function into method ... + self.write_defs = self._method_write_defs + self.of_object = old.of_object + del self.params[0] + def write_defs(self, fp=sys.stdout): + fp.write('(define-function ' + self.name + '\n') + if self.in_module: + fp.write(' (in-module "' + self.in_module + '")\n') + if self.is_constructor_of: + fp.write(' (is-constructor-of "' + self.is_constructor_of +'")\n') + if self.c_name: + fp.write(' (c-name "' + self.c_name + '")\n') + if self.typecode: + fp.write(' (gtype-id "' + self.typecode + '")\n') + if self.caller_owns_return: + fp.write(' (caller-owns-return #t)\n') + if self.unblock_threads: + fp.write(' (unblock-threads #t)\n') + if self.ret: + fp.write(' (return-type "' + self.ret + '")\n') + if self.deprecated: + fp.write(' (deprecated "' + self.deprecated + '")\n') + if self.params: + if isinstance(self.params[0], Parameter): + fp.write(' (parameters\n') + for ptype, pname, pdflt, pnull in self.params: + fp.write(' \'("' + ptype + '" "' + pname +'"') + if pdflt: fp.write(' (default "' + pdflt + '")') + if pnull: fp.write(' (null-ok)') + fp.write(')\n') + fp.write(' )\n') + elif isinstance(self.params[0], Property): + fp.write(' (properties\n') + for prop in self.params: + fp.write(' \'("' + prop.pname +'"') + if prop.optional: fp.write(' (optional)') + fp.write(')\n') + fp.write(' )\n') + else: + assert False, "strange parameter list %r" % self.params[0] + if self.varargs: + fp.write(' (varargs #t)\n') + + fp.write(')\n\n') diff --git a/codegen/defsconvert.py b/codegen/defsconvert.py new file mode 100755 index 0000000..c3c2324 --- /dev/null +++ b/codegen/defsconvert.py @@ -0,0 +1,132 @@ +#! /usr/bin/env python + +import sys +import string, re + +# ------------------ Create typecodes from typenames --------- + +_upperstr_pat1 = re.compile(r'([^A-Z])([A-Z])') +_upperstr_pat2 = re.compile(r'([A-Z][A-Z])([A-Z][0-9a-z])') +_upperstr_pat3 = re.compile(r'^([A-Z])([A-Z])') + +def to_upper_str(name): + """Converts a typename to the equivalent upercase and underscores + name. This is used to form the type conversion macros and enum/flag + name variables""" + name = _upperstr_pat1.sub(r'\1_\2', name) + name = _upperstr_pat2.sub(r'\1_\2', name) + name = _upperstr_pat3.sub(r'\1_\2', name, count=1) + return string.upper(name) + +def typecode(typename): + """create a typecode (eg. GTK_TYPE_WIDGET) from a typename""" + return string.replace(to_upper_str(typename), '_', '_TYPE_', 1) + + +STATE_START = 0 +STATE_OBJECT = 1 +STATE_INTERFACE = 2 +STATE_BOXED = 3 +STATE_ENUM = 4 +STATE_FLAGS = 5 +STATE_METHOD = 6 +STATE_FUNCTION = 7 + +def convert(infp=sys.stdin, outfp=sys.stdout): + state = STATE_START + seen_params = 0 + + line = infp.readline() + while line: + if line[:8] == '(object ': + state = STATE_OBJECT + seen_params = 0 + outfp.write('(define-object ' + line[8:]) + elif line[:11] == '(interface ': + state = STATE_INTERFACE + seen_params = 0 + outfp.write('(define-interface ' + line[11:]) + elif line[:7] == '(boxed ': + state = STATE_BOXED + seen_params = 0 + outfp.write('(define-boxed ' + line[7:]) + elif line[:6] == '(enum ': + state = STATE_ENUM + seen_params = 0 + outfp.write('(define-enum ' + line[6:]) + elif line[:7] == '(flags ': + state = STATE_FLAGS + seen_params = 0 + outfp.write('(define-flags ' + line[7:]) + elif line[:8] == '(method ': + state = STATE_METHOD + seen_params = 0 + outfp.write('(define-method ' + line[8:]) + elif line[:10] == '(function ': + state = STATE_FUNCTION + seen_params = 0 + outfp.write('(define-function ' + line[10:]) + elif line[:13] == ' (in-module ': + outfp.write(re.sub(r'^(\s+\(in-module\s+)(\w+)(.*)$', + r'\1"\2"\3', line)) + elif line[:10] == ' (parent ': + outfp.write(re.sub(r'^(\s+\(parent\s+)(\w+)(\s+\((\w+)\))?(.*)$', + r'\1"\4\2"\5', line)) + elif line[:14] == ' (implements ': + outfp.write(re.sub(r'^(\s+\(implements\s+)([^\s]+)(\s*\))$', + r'\1"\2"\3', line)) + elif line[:13] == ' (of-object ': + outfp.write(re.sub(r'^(\s+\(of-object\s+)(\w+)(\s+\((\w+)\))?(.*)$', + r'\1"\4\2"\5', line)) + elif line[:10] == ' (c-name ': + outfp.write(re.sub(r'^(\s+\(c-name\s+)([^\s]+)(\s*\))$', + r'\1"\2"\3', line)) + if state in (STATE_OBJECT, STATE_INTERFACE, STATE_BOXED, + STATE_ENUM, STATE_FLAGS): + c_name = re.match(r'^\s+\(c-name\s+([^\s]+)\s*\)$', + line).group(1) + outfp.write(' (gtype-id "%s")\n' % typecode(c_name)) + elif line[:15] == ' (return-type ': + outfp.write(re.sub(r'^(\s+\(return-type\s+)([^\s]+)(\s*\))$', + r'\1"\2"\3', line)) + elif line[:13] == ' (copy-func ': + outfp.write(re.sub(r'^(\s+\(copy-func\s+)(\w+)(.*)$', + r'\1"\2"\3', line)) + elif line[:16] == ' (release-func ': + outfp.write(re.sub(r'^(\s+\(release-func\s+)(\w+)(.*)$', + r'\1"\2"\3', line)) + elif line[:9] == ' (field ': + if not seen_params: + outfp.write(' (fields\n') + seen_params = 1 + outfp.write(re.sub(r'^\s+\(field\s+\(type-and-name\s+([^\s]+)\s+([^\s]+)\s*\)\s*\)$', + ' \'("\\1" "\\2")', line)) + elif line[:9] == ' (value ': + if not seen_params: + outfp.write(' (values\n') + seen_params = 1 + outfp.write(re.sub(r'^\s+\(value\s+\(name\s+([^\s]+)\)\s+\(c-name\s+([^\s]+)\s*\)\s*\)$', + ' \'("\\1" "\\2")', line)) + elif line[:13] == ' (parameter ': + if not seen_params: + outfp.write(' (parameters\n') + seen_params = 1 + outfp.write(re.sub(r'^\s+\(parameter\s+\(type-and-name\s+([^\s]+)\s+([^\s]+)\s*\)(\s*.*)\)$', + ' \'("\\1" "\\2"\\3)', line)) + elif line[:11] == ' (varargs ': + if seen_params: + outfp.write(' )\n') + seen_params = 0 + outfp.write(' (varargs #t)\n') + elif line[0] == ')': + if seen_params: + outfp.write(' )\n') + seen_params = 0 + state = STATE_START + outfp.write(line) + else: + outfp.write(line) + line = infp.readline() + +if __name__ == '__main__': + convert() diff --git a/codegen/defsgen.py b/codegen/defsgen.py new file mode 100755 index 0000000..6c2e63d --- /dev/null +++ b/codegen/defsgen.py @@ -0,0 +1,737 @@ +#!/usr/bin/env python +# -*- Mode: Python; py-indent-offset: 4 -*- +# +# Copyright (C) 2006-2009 John Finlay. +# +# Scan the given public .h files of a GTK module (or module using +# GTK object conventions) and generates a set of scheme defs. +# +# defsgen uses the ctypes module to extract information from the installed +# module library (or libraries) to generate the object, interface, function, +# method, virtual, enum and flags defs. defsgen uses the gobject library +# g_type_* functions. defsgen will try to open the "libgobject-2.0.so" library +# if one is not specified on the command line. +# +# Basically the operation of defsgen is: +# +# - open and initialize the gobject and module libraries +# - read each .h file into a buffer which is scrubbed of extraneous data +# - find all *_get_type() functions prototypes +# - look in the module libraries for the get_type symbols +# - if found run the get_type() function to retrieve the GType +# - find the parent type name and save the object info +# - find each function prototypes in the file and check if it has a symbol in +# the module libraries - save the info if found +# - extract the virtual prototypes from the Class or Iface structs and save +# - write out the various defs. +# +# The command line options are: +# +# -l --modulelib Adds the given module library name to the list to be used +# for finding symbols. Mor ethan one modulefile may be +# specified. (required) +# -L --libgobject Specifies the name of the gobject library (optional but +# must be specified if "libgobject-2.0.so" is not availble) +# -s --separate Create separate files for objects and function/method defs +# using the given name as the base name (optional). If this +# is not specified the combined object and function defs +# will be output to sys.stdout. +# -f --defsfile Extract defs from the given file to filter the output defs +# that is don't output defs that are defined in the +# defsfile. More than one deffile may be specified. +# -D --defines Include portions of the defsfile defs that are conditional +# on the given define, for example GDK_TARGET_X11. Only +# useful with the --defsfile option +# -m --modulename The prefix to be stripped from the front of function names +# for the given module +# -p --useprefix Use the modulename prefix as a hint to split names into +# module and name for object and enum defs. Also used for +# generating type codes. +# --onlyenums Only produce defs for enums and flags +# --onlyobjdefs Only produce defs for objects +# --onlyvirtuals Only produce defs for virtuals +# --genpropgetsets Experimental option to generate prop-getset annotations. +# Not supported by codegen.py and friends. +# +# Examples: +# +# python defsgen.py -m pango -l libpango-1.0.so \ +# /usr/local/include/pango-1.0/pango/*.h >/tmp/pango.defs +# +# - Outputs all defs for the pango module.using the library module +# libpango-1.0.so. +# +# python defsgen.py -m gdk -DGDK_TARGET_X11 -l libgdk-x11-2.0.so \ +# -l libgdk_pixbuf-2.0.so -s /tmp/gdk-2.10 \ +# -f /usr/tmp/pygtk/gtk/gdk-base.defs \ +# /usr/local/include/gtk-2.0/gdk/*.h \ +# /usr/local/include/gtk-2.0/gdk-pixbuf/*.h +# +# - Outputs the gdk module defs that are not contained in the defs file +# /usr/tmp/pygtk/gtk/gdk-base.defs. Two output files are created: +# /tmp/gdk-2.10-types.defs and /tmp/gdk-2.10.defs. +# +# Based on the original h2def.py program by +# Toby D. Reeves <toby@max.rl.plh.af.mil> and +# modified by James Henstridge <james@daa.com.au> to output stuff in +# Havoc's new defs format. Info on this format can be seen at: +# http://www.gnome.org/mailing-lists/archives/gtk-devel-list/2000-January/0085.shtml +# Updated to be PEP-8 compatible and refactored to use OOP +# Extensively modified by John Finlay to use ctypes module to extract GType +# info from the given library and to create virtual defines. +# + +import getopt +import os +import re, string +import sys +import ctypes +import defsparser + +#------------------ utility defs ----------------- + +_upperstr_pat1 = re.compile(r'([^A-Z])([A-Z])') +_upperstr_pat2 = re.compile(r'([A-Z][A-Z])([A-Z][0-9a-z])') +_upperstr_pat3 = re.compile(r'^([A-Z])([A-Z])') + +def to_upper_str(name): + """Converts a typename to the equivalent uppercase and underscores + name. This is used to form the type conversion macros and enum/flag + name variables""" + name = _upperstr_pat1.sub(r'\1_\2', name) + name = _upperstr_pat2.sub(r'\1_\2', name) + name = _upperstr_pat3.sub(r'\1_\2', name, count=1) + return name.upper() + +def typecode(typename, prefix, use_prefix): + """create a typecode (eg. GTK_TYPE_WIDGET) from a typename""" + tcode = to_upper_str(typename) + if (use_prefix and prefix and tcode.lower() != prefix + and tcode.lower().startswith(prefix)): + l = len(prefix) + tcode = tcode[:l] + '_TYPE' + tcode[l:] + else: + tcode = tcode.replace('_', '_TYPE_', 1) + return tcode + +_class_iface_pat = re.compile(r'\w+(Class|Iface)') + +def class_iface_sub(mobj): + '''Returns matched string if it matches a Class or Iface struct + otherwise returns the empty string''' + if not _class_iface_pat.match(mobj.group(1)): + return '' + return mobj.group(0) + +clean_patterns = [ + # strip comments + (re.compile(r'/\*.*?\*/', re.DOTALL), ''), + # compact continued lines + (re.compile(r"\\\n", re.MULTILINE), ''), + # remove preprocess directives + (re.compile(r"""^[#].*?$""", re.MULTILINE), ''), + # strip DECLS macros and Windows DLL API macros + (re.compile(r"""G_BEGIN_DECLS|BEGIN_LIBGTOP_DECLS|G_END_DECLS|[A-Z]+_API """, + re.MULTILINE), ''), + # remove extern "C" + (re.compile(r'^\s*(extern)\s+"C"\s+{', re.MULTILINE), ''), + # remove singleline typedefs of stucts + (re.compile(r'^typedef\s+struct\s*[^{;\n]*;\s*$', re.MULTILINE), ''), + # remove all struct definitons but those for object classes and interfaces + (re.compile(r'^struct\s+(\w+)\s+{[^}]+}\s*;\s*$', re.MULTILINE), + class_iface_sub), + # compress multiple whitespace + (re.compile(r'\s+', re.MULTILINE), ' '), + # clean up line ends + (re.compile(r';\s*', re.MULTILINE), '\n'), + (re.compile(r'^\s*', re.MULTILINE), ''), + # associate *, &, and [] with type instead of variable + (re.compile(r' \s* ([*|&]+) \s* ([(\w]+)', re.VERBOSE), r'\1 \2'), + (re.compile(r'\s+ (\w+) \[ \s* \]', re.VERBOSE), r'[] \1'), + # make return types that are const work. + (re.compile(r'\s*\*\s*G_CONST_RETURN\s*\*\s*'), '** '), + (re.compile(r'G_CONST_RETURN |const '), 'const-'), + # remove typedefs of callback types + (re.compile(r'^typedef\s+\w+\s*\*?\s*\(\s*\*\s*\w+\)\s*\([^(]*\)\n', + re.MULTILINE), ''), + #strip GSEAL macros from the middle of function declarations: + (re.compile(r"""GSEAL""", re.VERBOSE), '') + ] + +def clean_buffer(buf): + """Cleans out extraneous data leaving function prototypes, Class and Iface + structs.""" + for pat, subst in clean_patterns: + buf = pat.sub(subst, buf) + return buf + +# ------------------ utility classes ------------------------- + +class ObjDef(object): + def __init__(self, name, type_id, parent_name, parent_type, base_name): + self.name = name + self.type = type_id + self.parent_name = parent_name + self.parent_type = parent_type + self.base_name = base_name + self.props = [] + return + def __cmp__(self, other): + try: + res = cmp(self.name, other.name) + except AttributeError: + res = cmp(id(self), id(other)) + return res + def set_properties(self, gobj): + if self.base_name == 'GObject': + self.props = self._get_gobject_properties(gobj) + elif self.base_name == 'GInterface': + self.props = self._get_ginterface_properties(gobj) + + def _get_gobject_properties(self, gobj): + klass = gobj.g_type_class_ref(self.type) + num = ctypes.c_uint() + plist = gobj.g_object_class_list_properties(klass, ctypes.byref(num)) + props = [plist[i][0].name for i in range(num.value) + if self.name == gobj.g_type_name(plist[i][0].owner_type)] + return props + def _get_ginterface_properties(self, gobj): + iface = gobj.g_type_default_interface_ref(self.type) + num = ctypes.c_uint() + plist = gobj.g_object_interface_list_properties(iface, + ctypes.byref(num)) + props = [plist[i][0].name for i in range(num.value)] + return props + +# ------------------ Find object definitions ----------------- + +split_prefix_pat = re.compile('([A-Z]+[a-z]*)([A-Za-z0-9]+)') + +get_type_pat = re.compile(r'''^\s*(GType|GtkType)\s+ +([a-z]\w+_get_type)\s*(\(void\)|\(\)).*$''', re.VERBOSE | re.MULTILINE) + +defkeys = 'GBoxed GInterface GObject gpointer GEnum GFlags' + +def find_defs(buf, gobj, modlib, defs): + """Find possible gobject, gboxed, interface, gpointer, enum and flags + definitions in header files.and find parent type.""" + # find all *_get_type() functions that may represent a GObject + for m in get_type_pat.findall(buf): + func_name = m[1] + for lib in modlib: + if hasattr(lib, func_name): + objtype = apply(getattr(lib, func_name)) + obj_name = gobj.g_type_name(objtype) + parent = gobj.g_type_parent(objtype) + parent_name = gobj.g_type_name(parent) + base_name = gobj.g_type_name(gobj.g_type_fundamental(parent)) + #if gobj.g_type_is_a(parent, gobj.GObject): + # base_name = 'GObject' + if base_name in defkeys: + obj = ObjDef(obj_name, objtype, parent_name, parent, + base_name) + obj.set_properties(gobj) + defs[obj.base_name].append(obj) + break + return + +# ------------------ Find function definitions ----------------- + +arg_split_pat = re.compile("\s*,\s*") + +proto_pat=re.compile(r"""^ +\s*((?:-|\w|\&|\*)+) # return type +\s+ # skip whitespace +([a-z]\w+)\s*[(] # match the function name until the opening ( +\s*(.*?)\s*[)].* # group the function arguments +$""", re.IGNORECASE|re.VERBOSE|re.MULTILINE) + +def find_func_defs(buf, modlib, deffile, defs, verbose): + '''Find function prototypes in buf that have symbols in modlib + and save in defs.''' + funcs = defs['funcs'][deffile] = [] + for m in proto_pat.findall(buf): + ret, func, args = m + if not True in [hasattr(lib, func) for lib in modlib]: + if verbose: + sys.stderr.write('no symbol for function: ' + func + + ' from file' + deffile + '\n') + else: + args = arg_split_pat.split(args) + args = [a.replace(' ','-', a.count(' ')-1) for a in args] + funcs.append((func, ret, args)) + return + +virtual_pat = re.compile(r'''^ +\s*((?:-|\w|\&|\*)+) # return type +\s* # skip whitespace +\(\s*\*\s* # opening ( +([a-z]\w+)\) # match the function name until the closing ) +\s*\(\s*([^)]*)\).* # group the function arguments +$''', re.VERBOSE|re.MULTILINE) + +class_iface_struct_pat = re.compile( + r'^struct\s+_(\w+)(?:Class|Iface)\s+{([^}]+)}\s*$', re.MULTILINE) + +def find_virt_defs(buf, deffile, defs): + '''Find virtual function prototypes in buf and save in defs.''' + virts = defs['virts'][deffile] = [] + # get the Class or Iface structs + for m in class_iface_struct_pat.findall(buf): + objname, virtuals = m + for v in virtual_pat.findall(virtuals): + ret, func, args = v + if 'reserved' in func or args == 'void': + continue + args = arg_split_pat.split(args) + args = [a.replace(' ','-', a.count(' ')-1) for a in args] + virts.append((func, ret, args, objname)) + return + +enum_pat = re.compile(r'^\s*typedef enum\s+{\s*([^}]*)}\s*([^\s]*)$', + re.MULTILINE) +values_splitter = re.compile(r'\s*,\s', re.MULTILINE) + +def find_enums(buf, defs): + for vals, name in enum_pat.findall(buf): + if name != 'GdkCursorType': + isflags = '<<' in vals + entries = [val.split()[0] for val in values_splitter.split(vals) + if val.strip()] + if entries: + defs['untypedenums'][name] = (isflags, entries) + return + +# ------------------ write definitions ----------------- + +type_pat = re.compile(r'(?:const-)?([A-Za-z0-9]+)\*?\s+') +pointer_pat = re.compile('(.*)\*$') +func_new_pat = re.compile('(\w+)_new$') +getset_pat = re.compile(r'^(?:get|set)_(.*)$') + +def split_prefix(cname, prefix, use_prefix): + # use the module prefix to split the cname + pre = prefix.replace('_', '') + if use_prefix and cname.lower().startswith(pre): + l = len(pre) + module = cname[:l] + name = cname[l:] + else: + m = split_prefix_pat.match(cname) + if m: + module = m.group(1) + name = m.group(2) + return module, name + +class DefsWriter: + def __init__(self, defs, fp=None, prefix=None, verbose=False, + defsfiles=None, defines={}, genpropgetsets=False, + useprefix=False): + self.defs = defs + self.use_prefix = useprefix + self.objnames = reduce(list.__add__, + [[o.name for o in defs[base]] + for base in defkeys.split()[:3]]) + self.othernames = reduce(list.__add__, + [[o.name for o in defs[base]] + for base in defkeys.split()[3:]]) + self.objifacedefs = dict(reduce(list.__add__, + [[(o.name, o) for o in defs[base]] + for base in defkeys.split()[1:3]])) + self.fp = (fp, sys.stdout)[not fp] + self.prefix = prefix + self.verbose = verbose + self.genpropgetsets = genpropgetsets + self._c_names={} + for defsfile in defsfiles: + filter = defsparser.DefsParser(defsfile, defines) + filter.startParsing() + self._c_names.update(filter.c_name) + for vdef in filter.virtuals: + self._c_names[vdef.of_object + '.' + vdef.name] = None + return + + def write_func_defs(self, deffiles, onlyvirts=False): + filter = self._c_names + for deffile in deffiles: + self.fp.write('\n;; From %s\n\n' % os.path.basename(deffile)) + if not onlyvirts: + for func, ret, args in self.defs['funcs'][deffile]: + if not func in filter: + self._write_func(func, ret, args) + for virt, ret, args, objname in self.defs['virts'][deffile]: + if not objname + '.' + virt in filter: + self._write_virt(objname, virt, ret, args) + self.fp.write('\n') + return + + def write_enum_defs(self, fp=None): + fp = (fp, self.fp)[not fp] + klassptrs = {'GEnum':ctypes.POINTER(EnumClass), + 'GFlags':ctypes.POINTER(FlagsClass)} + filter = self._c_names + objs = self.defs['GEnum'] + self.defs ['GFlags'] + #objs.sort() + fp.write(';; Enumerations and Flags ...\n\n') + for obj in objs: + cname = name = obj.name + tcode = typecode(cname, self.prefix, self.use_prefix) + if cname in filter: + continue + if cname in self.defs['untypedenums']: + if tcode not in self.defs['typedefines']: + # no type define so skip and print as untyped enum + continue + self.defs['untypedenums'].pop(cname, None) + parent_name = obj.parent_name + klassptr = klassptrs[parent_name] + typename = parent_name.lower()[1:] + module = None + module, name = split_prefix(cname, self.prefix, self.use_prefix) + fp.write('(define-' + typename + ' ' + name + '\n') + if module: + fp.write(' (in-module "' + module + '")\n') + fp.write(' (c-name "' + cname + '")\n') + fp.write(' (gtype-id "' + tcode + '")\n') + fp.write(' (values\n') + classref = self.gobj.g_type_class_ref(obj.type) + itemclass = ctypes.cast(classref, klassptr).contents + for i in range(itemclass.n_values): + val = itemclass.values[i] + fp.write(' \'("%s" "%s")\n' % (val.value_nick, + val.value_name)) + fp.write(' )\n') + fp.write(')\n\n') + if self.defs['untypedenums']: + self.write_untyped_enum_defs(fp) + return + + def write_untyped_enum_defs(self, fp): + fp.write(';; Untyped enumerations and flags ...\n\n') + filter = self._c_names + for cname, (isflags, entries) in self.defs['untypedenums'].items(): + if filter and cname in filter: continue + module, name = split_prefix(cname, self.prefix, self.use_prefix) + if isflags: + fp.write('(define-flags ' + name + '\n') + else: + fp.write('(define-enum ' + name + '\n') + if module: + fp.write(' (in-module "' + module + '")\n') + fp.write(' (c-name "' + cname + '")\n') + preindex = entries[0].rfind('_') + for ent in entries[1:]: + while (preindex > 0 + and ent[:preindex] != entries[0][:preindex]): + preindex = ent[:preindex].rfind('_') + fp.write(' (values\n') + for ent in entries: + fp.write(' \'("%s" "%s")\n' % + (ent[preindex+1:].lower().replace('_', '-'), ent)) + fp.write(' )\n') + fp.write(')\n\n') + + + def _write_obj_helper(self, obj, fp): + base_name = obj.base_name.lower()[1:] + cmodule = None + cname = name = obj.name + type_id = obj.type + parent_name = obj.parent_name + cmodule, name = split_prefix(cname, self.prefix, self.use_prefix) + fp.write('(define-' + base_name + ' ' + name + '\n') + if cmodule: + fp.write(' (in-module "' + cmodule + '")\n') + if base_name == 'object': + fp.write(' (parent "' + parent_name + '")\n') + fp.write(' (c-name "' + cname + '")\n') + fp.write(' (gtype-id "' + + typecode(cname, self.prefix, self.use_prefix) + '")\n') + n = ctypes.c_uint() + ifaces = self.gobj.g_type_interfaces(type_id, ctypes.byref(n)) + for i in range(n.value): + iface_name = self.gobj.g_type_name(ifaces[i]) + if iface_name in self.interfaces: + fp.write(' (implements "%s")\n' % iface_name) + if base_name == 'interface': + prereq = self.gobj.g_type_interface_prerequisites(type_id, + ctypes.byref(n)) + for i in range(n.value): + fp.write(' (prerequisite "%s")\n' + % self.gobj.g_type_name(prereq[i])) + # should do something about accessible fields + fp.write(')\n\n') + return + + def write_obj_defs(self, fp=None): + fp = (fp, self.fp)[not fp] + fp.write(';; -*- scheme -*-\n') + filter = self._c_names + for base in defkeys.split()[:4]: + base_name = base.lower()[1:] + fp.write('; %s definitions ...\n\n' % base_name) + for obj in self.defs[base]: + if not obj.name in filter: + self._write_obj_helper(obj, fp) + return + + def _write_func(self, name, ret, args): + if len(args) >= 1: + # methods must have at least one argument + munged_name = name.replace('_', '') + m = type_pat.match(args[0]) + if m: + obj = m.group(1) + if munged_name.startswith(obj.lower()): + if obj not in self.othernames: + self._write_method(obj, name, ret, args) + return + fname = name + if self.prefix: + fname = re.sub('^'+self.prefix+'_', '', fname) + + # it is either a constructor or normal function + self.fp.write('(define-function ' + fname + '\n') + self.fp.write(' (c-name "' + name + '")\n') + + # Asume that a constructor function name + # ends with '_new' and it returns an object pointer. + m = func_new_pat.match(name) + r = pointer_pat.match(ret) + if m and r: + cname = '' + # get the type name by using the _get_type function + func = m.group(1) + '_get_type' + lib = [l for l in self.modlib if hasattr(l, func)] + if lib: + cname = self.gobj.g_type_name(apply(getattr(lib[0], func))) + if cname and self.gobj.g_type_from_name(r.group(1)): + self.fp.write(' (is-constructor-of "' + cname + '")\n') + self._write_return(ret) + self._write_arguments(args) + return + + def _write_method(self, obj, name, ret, args): + regex = ''.join([c+'_?' for c in obj.lower()]) + mname, count = re.subn(regex, '', name, 1) + if not count and self.prefix: + mname = re.sub('^'+self.prefix+'_', '', mname) + self.fp.write('(define-method ' + mname + '\n') + self.fp.write(' (of-object "' + obj + '")\n') + self.fp.write(' (c-name "' + name + '")\n') + m = getset_pat.match(mname) + if self.genpropgetsets and m and len(args[1:]) <= 1: + prop = m.group(1) + if obj in self.objifacedefs: + oidef = self.objifacedefs[obj] + if prop.replace('_', '-') in oidef.props: + self.fp.write(' (prop-getset "' + prop + '")\n') + self._write_return(ret) + self._write_arguments(args[1:]) + return + + def _write_virt(self, obj, name, ret, args): + self.fp.write('(define-virtual ' + name + '\n') + self.fp.write(' (of-object "' + obj + '")\n') + self._write_return(ret) + self._write_arguments(args[1:]) + return + + def _write_return(self, ret): + if ret == 'void': + ret = 'none' + self.fp.write(' (return-type "' + ret + '")\n') + return + + def _write_arguments(self, args): + if args and not 'void' in args: + varargs = '' + self.fp.write(' (parameters\n') + for arg in args: + if arg == '...': + varargs = ' (varargs #t)\n' + else: + tupleArg = tuple(arg.split()) + if len(tupleArg) == 2: + self.fp.write(' \'("%s" "%s")\n' % tupleArg) + self.fp.write(' )\n' + varargs) + self.fp.write(')\n\n') + +# ---------- ctypes support classes for gobject library functions ---------- + +GType = ctypes.c_uint + +class GTypeClass(ctypes.Structure): + _fields_ = [('g_type', GType)] + +class GTypeInstance(ctypes.Structure): + _fields_ = [('g_class', ctypes.POINTER(GTypeClass))] + +class EnumValue(ctypes.Structure): + _fields_ = [('value', ctypes.c_int), + ('value_name', ctypes.c_char_p), + ('value_nick', ctypes.c_char_p)] + +class FlagsValue(ctypes.Structure): + _fields_ = [('value', ctypes.c_uint), + ('value_name', ctypes.c_char_p), + ('value_nick', ctypes.c_char_p)] + +class EnumClass(ctypes.Structure): + _fields_ = [('g_type_class', GTypeClass), + ('minimum', ctypes.c_int), + ('maximum', ctypes.c_int), + ('n_values', ctypes.c_uint), + ('values', ctypes.POINTER(EnumValue))] + +class FlagsClass(ctypes.Structure): + _fields_ = [('g_type_class', GTypeClass), + ('mask', ctypes.c_uint), + ('n_values', ctypes.c_uint), + ('values', ctypes.POINTER(FlagsValue))] + +class GTypeInterface(ctypes.Structure): + _fields_ = [('g_type', GType), + ('g_instance_type', GType)] + +class GParamSpec(ctypes.Structure): + _fields_ = [('g_type_instance', GTypeInstance), + ('name', ctypes.c_char_p), + ('flags', ctypes.c_uint), + ('value_type', GType), + ('owner_type', GType)] + +# ------------------ Main function ----------------- + +def main(args): + verbose = False + all = True + onlyenums = False + onlyobjdefs = False + onlyvirtuals = False + separate = False + modulename = None + defsfiles = [] + libgobject = None + modulelibs = [] + defines = {} + genpropgetsets = False + use_prefix = False + opts, args = getopt.getopt(args[1:], 'vs:m:f:D:L:l:p', + ['onlyenums', 'onlyobjdefs', 'onlyvirtuals', + 'modulename=', 'separate=', + 'defsfile=', 'defines=', 'genpropgetsets', + 'libgobject-', 'modulelib=', 'useprefix']) + for o, v in opts: + if o == '-v': + verbose = True + if o == '--onlyenums': + onlyenums = True + all = False + if o == '--onlyvirtuals': + onlyvirtuals = True + all = False + if o == '--onlyobjdefs': + onlyobjdefs = True + all = False + if o in ('-p', '--useprefix'): + use_prefix = True + if o == '--genpropgetsets': + genpropgetsets = True + if o in ('-s', '--separate'): + separate = v + if o in ('-m', '--modulename'): + modulename = v + if o in ('-L', '--libgobject'): + libgobject = v + if o in ('-l', '--modulelib'): + modulelibs.append(v) + if o in ('-f', '--defsfile'): + defsfiles.append(v) + if o in ('-D', '--defines'): + nameval = v.split('=') + try: + defines[nameval[0]] = nameval[1] + except IndexError: + defines[nameval[0]] = None + + if not args[0:1]: + print 'Must specify at least one input file name' + return -1 + if not modulelibs: + print 'Must specify one or more modulelib names' + return -1 + + # load the gobject and module libraries and init the gtype system + if not libgobject: + if verbose: + sys.stderr.write('Using "libgobject-2.0.so" as the libobject' \ + 'library name by default\n') + gobj = ctypes.cdll.LoadLibrary('libgobject-2.0.so') + else: + gobj = ctypes.cdll.LoadLibrary(libgobject) + + modlib = [ctypes.cdll.LoadLibrary(lib) for lib in modulelibs] + + gobj.g_type_init() + gobj.g_type_name.restype = ctypes.c_char_p + gobj.g_type_from_name.argtypes = [ctypes.c_char_p] + gobj.g_type_interfaces.restype = ctypes.POINTER(ctypes.c_int) + gobj.g_type_interface_prerequisites.restype = ctypes.POINTER(ctypes.c_int) + gobj.g_object_class_list_properties.restype = ctypes.POINTER(ctypes.POINTER(GParamSpec)) + gobj.g_object_interface_list_properties.restype = ctypes.POINTER(ctypes.POINTER(GParamSpec)) + gobj.GObject = gobj.g_type_from_name('GObject') + gobj.g_object_new(gobj.GObject, None) + + defs = {} + for key in defkeys.split(): + defs[key] = [] + defs['funcs'] = {} + defs['virts'] = {} + defs['untypedenums'] = {} + defs['typedefines'] = [] + + # read in all the object and function definitions + args.sort() + type_define_pat = re.compile( + r'^#define\s+([A-Z]\S+_TYPE_\S+)\s+.*[a-z]\w+_get_type.*$', + re.MULTILINE) + for filename in args: + buf = open(filename).read() + defs['typedefines'] += type_define_pat.findall(buf) + buf = clean_buffer(buf) + find_enums(buf, defs) + find_defs(buf, gobj, modlib, defs) + find_func_defs(buf, modlib, filename, defs, verbose) + find_virt_defs(buf, filename, defs) + + for key in defkeys.split(): + defs[key].sort() + + methods = types = None + if separate: + methods = file(separate + '.defs', 'w') + types = file(separate + '-types.defs', 'w') + + dw = DefsWriter(defs, methods, prefix=modulename, verbose=verbose, + defsfiles=defsfiles, defines=defines, + genpropgetsets=genpropgetsets, useprefix=use_prefix) + dw.interfaces = [i.name for i in defs['GInterface']] + dw.gobj = gobj + dw.modlib = modlib + + if onlyobjdefs or all: + dw.write_obj_defs(types) + if separate: + print "Wrote object defs to %s-types.defs" % separate + if onlyenums or all: + dw.write_enum_defs(types) + if separate: + print "Wrote enum and flags defs to %s-types.defs" % separate + if onlyvirtuals or all: + dw.write_func_defs(args, onlyvirtuals) + if separate: + print "Wrote function and virtual defs to %s.defs" % separate + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/codegen/defsparser.py b/codegen/defsparser.py new file mode 100644 index 0000000..37ba0a2 --- /dev/null +++ b/codegen/defsparser.py @@ -0,0 +1,153 @@ +# -*- Mode: Python; py-indent-offset: 4 -*- +import os, sys +import scmexpr +from definitions import BoxedDef, EnumDef, FlagsDef, FunctionDef, \ + InterfaceDef, MethodDef, ObjectDef, PointerDef, VirtualDef + +include_path = ['.'] + +class IncludeParser(scmexpr.Parser): + """A simple parser that follows include statements automatically""" + def include(self, input_filename): + global include_path + if os.path.isabs(input_filename): + filename = input_filename + # set self.filename to the include name, to handle recursive includes + oldfile = self.filename + self.filename = filename + self.startParsing() + self.filename = oldfile + else: + inc_path = [os.path.dirname(self.filename)] + include_path + for filename in [os.path.join(path_entry, input_filename) + for path_entry in inc_path]: + if not os.path.exists(filename): + continue + # set self.filename to the include name, to handle recursive includes + oldfile = self.filename + self.filename = filename + self.startParsing() + self.filename = oldfile + break + else: + raise IOError("%s not found in include path %s" % (input_filename, inc_path)) + +class DefsParser(IncludeParser): + def __init__(self, arg, defines={}): + IncludeParser.__init__(self, arg) + self.objects = [] + self.interfaces = [] + self.enums = [] # enums and flags + self.boxes = [] # boxed types + self.pointers = [] # pointer types + self.functions = [] # functions and methods + self.virtuals = [] # virtual methods + self.c_name = {} # hash of c names of functions + self.methods = {} # hash of methods of particular objects + self.defines = defines # -Dfoo=bar options, as dictionary + + def define_object(self, *args): + odef = apply(ObjectDef, args) + self.objects.append(odef) + self.c_name[odef.c_name] = odef + def define_interface(self, *args): + idef = apply(InterfaceDef, args) + self.interfaces.append(idef) + self.c_name[idef.c_name] = idef + def define_enum(self, *args): + edef = apply(EnumDef, args) + self.enums.append(edef) + self.c_name[edef.c_name] = edef + def define_flags(self, *args): + fdef = apply(FlagsDef, args) + self.enums.append(fdef) + self.c_name[fdef.c_name] = fdef + def define_boxed(self, *args): + bdef = apply(BoxedDef, args) + self.boxes.append(bdef) + self.c_name[bdef.c_name] = bdef + def define_pointer(self, *args): + pdef = apply(PointerDef, args) + self.pointers.append(pdef) + self.c_name[pdef.c_name] = pdef + def define_function(self, *args): + fdef = apply(FunctionDef, args) + self.functions.append(fdef) + self.c_name[fdef.c_name] = fdef + def define_method(self, *args): + mdef = apply(MethodDef, args) + self.functions.append(mdef) + self.c_name[mdef.c_name] = mdef + def define_virtual(self, *args): + vdef = apply(VirtualDef, args) + self.virtuals.append(vdef) + def merge(self, old, parmerge): + for obj in self.objects: + if old.c_name.has_key(obj.c_name): + obj.merge(old.c_name[obj.c_name]) + for f in self.functions: + if old.c_name.has_key(f.c_name): + f.merge(old.c_name[f.c_name], parmerge) + + def printMissing(self, old): + for obj in self.objects: + if not old.c_name.has_key(obj.c_name): + obj.write_defs() + for f in self.functions: + if not old.c_name.has_key(f.c_name): + f.write_defs() + + def write_defs(self, fp=sys.stdout): + for obj in self.objects: + obj.write_defs(fp) + for enum in self.enums: + enum.write_defs(fp) + for boxed in self.boxes: + boxed.write_defs(fp) + for pointer in self.pointers: + pointer.write_defs(fp) + for func in self.functions: + func.write_defs(fp) + + def find_object(self, c_name): + for obj in self.objects: + if obj.c_name == c_name: + return obj + else: + raise ValueError('object %r not found' % c_name) + + def find_constructor(self, obj, overrides): + for func in self.functions: + if isinstance(func, FunctionDef) and \ + func.is_constructor_of == obj.c_name and \ + not overrides.is_ignored(func.c_name): + return func + + def find_methods(self, obj): + objname = obj.c_name + return filter(lambda func, on=objname: isinstance(func, MethodDef) and + func.of_object == on, self.functions) + + def find_virtuals(self, obj): + objname = obj.c_name + retval = filter(lambda func, on=objname: isinstance(func, VirtualDef) and + func.of_object == on, self.virtuals) + return retval + + def find_functions(self): + return filter(lambda func: isinstance(func, FunctionDef) and + not func.is_constructor_of, self.functions) + + def ifdef(self, *args): + if args[0] in self.defines: + for arg in args[1:]: + #print >> sys.stderr, "-----> Handling conditional definition (%s): %s" % (args[0], arg) + self.handle(arg) + else: + pass + #print >> sys.stderr, "-----> Conditional %s is not true" % (args[0],) + + def ifndef(self, *args): + if args[0] not in self.defines: + for arg in args[1:]: + self.handle(arg) diff --git a/codegen/docextract.py b/codegen/docextract.py new file mode 100644 index 0000000..eff8c5e --- /dev/null +++ b/codegen/docextract.py @@ -0,0 +1,448 @@ +# -*- Mode: Python; py-indent-offset: 4 -*- +'''Simple module for extracting GNOME style doc comments from C +sources, so I can use them for other purposes.''' + +import sys, os, string, re + +__all__ = ['extract'] + +class GtkDoc: + def __init__(self): + self.name = None + self.block_type = '' # The block type ('function', 'signal', 'property') + self.params = [] + self.annotations = [] + self.description = '' + self.ret = ('', []) # (return, annotations) + def set_name(self, name): + self.name = name + def set_type(self, block_type): + self.block_type = block_type + def get_type(self): + return self.block_type + def add_param(self, name, description, annotations=[]): + if name == '...': + name = 'Varargs' + self.params.append((name, description, annotations)) + def append_to_last_param(self, extra): + self.params[-1] = (self.params[-1][0], self.params[-1][1] + extra, + self.params[-1][2]) + def append_to_named_param(self, name, extra): + for i in range(len(self.params)): + if self.params[i][0] == name: + self.params[i] = (name, self.params[i][1] + extra, + self.params[i][2]) + return + # fall through to adding extra parameter ... + self.add_param(name, extra) + def add_annotation(self, annotation): + self.annotations.append(annotation) + def get_annotations(self): + return self.annotations + def append_to_description(self, extra): + self.description = self.description + extra + def get_description(self): + return self.description + def add_return(self, first_line, annotations=[]): + self.ret = (first_line, annotations) + def append_to_return(self, extra): + self.ret = (self.ret[0] + extra, self.ret[1]) + +comment_start_pattern = re.compile(r'^\s*/\*\*\s') +comment_end_pattern = re.compile(r'^\s*\*+/') +comment_line_lead_pattern = re.compile(r'^\s*\*\s*') +comment_empty_line_pattern = re.compile(r'^\s*\**\s*$') +function_name_pattern = re.compile(r'^([a-z]\w*)\s*:?(\s*\(.*\)\s*){0,2}\s*$') +signal_name_pattern = re.compile(r'^([A-Z]\w+::[a-z0-9-]+)\s*:?(\s*\(.*\)\s*){0,2}\s*$') +property_name_pattern = re.compile(r'^([A-Z]\w+:[a-z0-9-]+)\s*:?(\s*\(.*\)\s*){0,2}\s*$') +return_pattern = re.compile(r'^@?(returns:|return\s+value:)(.*\n?)$', re.IGNORECASE) +deprecated_pattern = re.compile(r'^(deprecated\s*:\s*.*\n?)$', re.IGNORECASE) +rename_to_pattern = re.compile(r'^(rename\s+to)\s*:\s*(.*\n?)$', re.IGNORECASE) +param_pattern = re.compile(r'^@(\S+)\s*:(.*\n?)$') +# Used to extract the annotations in the parameter and return descriptions +# extracted using above [param|return]_pattern patterns. +annotations_pattern = re.compile(r'^(?:(\s*\(.*\)\s*)*:)') +# Used to construct the annotation lists. +annotation_lead_pattern = re.compile(r'^\s*\(\s*(.*?)\s*\)\s*') + +# These patterns determine the identifier of the current comment block. They +# are grouped in a list for easy determination of block identifiers (in +# skip_to_identifier). The function_name_pattern should be tested for last +# because it always matches signal and property identifiers. +identifier_patterns = [ signal_name_pattern, property_name_pattern, function_name_pattern ] + +# This pattern is to match return sections that forget to have a colon (':') +# after the initial 'Return' phrase. It is not included by default in the list +# of final sections below because a lot of function descriptions begin with +# 'Returns ...' and the process_description() function would stop right at that +# first line, thinking it is a return section. +no_colon_return_pattern = re.compile(r'^@?(returns|return\s+value)\s*(.*\n?)$', re.IGNORECASE) +since_pattern = re.compile(r'^(since\s*:\s*.*\n?)$', re.IGNORECASE) + +# These patterns normally will be encountered after the description. Knowing +# the order of their appearance is difficult so this list is used to test when +# one begins and the other ends when processing the rest of the sections after +# the description. +final_section_patterns = [ return_pattern, since_pattern, deprecated_pattern, rename_to_pattern ] + +def parse_file(fp, doc_dict): + line = fp.readline() + while line: + cur_doc = GtkDoc() + line = skip_to_comment_block(fp, line) + line = skip_to_identifier(fp, line, cur_doc) + # See if the identifier is found (stored in the current GtkDoc by + # skip_to_identifier). If so, continue reading the rest of the comment + # block. + if cur_doc.name: + line = process_params(fp, line, cur_doc) + line = process_description(fp, line, cur_doc) + line = process_final_sections(fp, line, cur_doc) + # Add the current doc block to the dictionary of doc blocks. + doc_dict[cur_doc.name] = cur_doc + +# Given a list of annotations as string of the form +# '(annotation1) (annotation2) ...' return a list of annotations of the form +# [ (name1, value1), (name2, value2) ... ]. Not all annotations have values so +# the values in the list of tuples could be empty (''). +def get_annotation_list(annotations): + annotation_list = [] + while annotations: + match = annotation_lead_pattern.match(annotations) + if match: + annotation_contents = match.group(1) + name, split, value = annotation_contents.strip().partition(' ') + annotation_list.append((name, value)) + # Remove first occurrence to continue processing. + annotations = annotation_lead_pattern.sub('', annotations) + else: + break + return annotation_list + +# Given a currently read line, test that line and continue reading until the +# beginning of a comment block is found or eof is reached. Return the last +# read line. +def skip_to_comment_block(fp, line): + while line: + if comment_start_pattern.match(line): + break + line = fp.readline() + return line + +# Given the current line in a comment block, continue skipping lines until a +# non-blank line in the comment block is found or until the end of the block +# (or eof) is reached. Returns the line where reading stopped. +def skip_to_nonblank(fp, line): + while line: + if not comment_empty_line_pattern.match(line): + break + line = fp.readline() + # Stop processing if eof or end of comment block is reached. + if not line or comment_end_pattern.match(line): + break + return line + +# Given the first line of a comment block (the '/**'), see if the next +# non-blank line is the identifier of the comment block. Stop processing if +# the end of the block or eof is reached. Store the identifier (if there is +# one) and its type ('function', 'signal' or 'property') in the given GtkDoc. +# Return the line where the identifier is found or the line that stops the +# processing (if eof or the end of the comment block is found first). +def skip_to_identifier(fp, line, cur_doc): + # Skip the initial comment block line ('/**') if not eof. + if line: line = fp.readline() + + # Now skip empty lines. + line = skip_to_nonblank(fp, line) + + # See if the first non-blank line is the identifier. + if line and not comment_end_pattern.match(line): + # Remove the initial ' * ' in comment block line and see if there is an + # identifier. + line = comment_line_lead_pattern.sub('', line) + for pattern in identifier_patterns: + match = pattern.match(line) + if match: + # Set the GtkDoc name. + cur_doc.set_name(match.group(1)) + # Get annotations and add them to the GtkDoc. + annotations = get_annotation_list(match.group(2)) + for annotation in annotations: + cur_doc.add_annotation(annotation) + # Set the GtkDoc type. + if pattern == signal_name_pattern: + cur_doc.set_type('signal') + elif pattern == property_name_pattern: + cur_doc.set_type('property') + elif pattern == function_name_pattern: + cur_doc.set_type('function') + return line + return line + +# Given a currently read line (presumably the identifier line), read the next +# lines, testing to see if the lines are part of parameter descriptions. If +# so, store the parameter descriptions in the given doc block. Stop on eof and +# return the last line that stops the processing. +def process_params(fp, line, cur_doc): + # Skip the identifier line if not eof. Also skip any blank lines in the + # comment block. Return if eof or the end of the comment block are + # encountered. + if line: line = fp.readline() + line = skip_to_nonblank(fp, line) + if not line or comment_end_pattern.match(line): + return line + + # Remove initial ' * ' in first non-empty comment block line. + line = comment_line_lead_pattern.sub('', line) + + # Now process possible parameters as long as no eof or the end of the + # param section is not reached (which could be triggered by anything that + # doesn't match a '@param:..." line, even the end of the comment block). + match = param_pattern.match(line) + while line and match: + description = match.group(2) + + # First extract the annotations from the description and save them. + annotations = [] + annotation_match = annotations_pattern.match(description) + if annotation_match: + annotations = get_annotation_list(annotation_match.group(1)) + # Remove the annotations from the description + description = annotations_pattern.sub('', description) + + # Default to appending lines to current parameter. + append_func = cur_doc.append_to_last_param + + # See if the return has been included as part of the parameter + # section and make sure that lines are added to the GtkDoc return if + # so. + if match.group(1).lower() == "returns": + cur_doc.add_return(description, annotations) + append_func = cur_doc.append_to_return + # If not, just add it as a regular parameter. + else: + cur_doc.add_param(match.group(1), description, annotations) + + # Now read lines and append them until next parameter, beginning of + # description (an empty line), the end of the comment block or eof. + line = fp.readline() + while line: + # Stop processing if end of comment block or a blank comment line + # is encountered. + if comment_empty_line_pattern.match(line) or \ + comment_end_pattern.match(line): + break + + # Remove initial ' * ' in comment block line. + line = comment_line_lead_pattern.sub('', line) + + # Break from current param processing if a new one is + # encountered. + if param_pattern.match(line): break; + + # Otherwise, just append the current line and get the next line. + append_func(line) + line = fp.readline() + + # Re-evaluate match for while condition + match = param_pattern.match(line) + + # End by returning the current line. + return line + +# Having processed parameters, read the following lines into the description of +# the current doc block until the end of the comment block, the end of file or +# a return section is encountered. +def process_description(fp, line, cur_doc): + # First skip empty lines returning on eof or end of comment block. + line = skip_to_nonblank(fp, line) + if not line or comment_end_pattern.match(line): + return line + + # Remove initial ' * ' in non-empty comment block line. + line = comment_line_lead_pattern.sub('', line) + + # Also remove possible 'Description:' prefix. + if line[:12] == 'Description:': line = line[12:] + + # Used to tell if the previous line was blank and a return section + # uncommonly marked with 'Returns ...' instead of 'Returns: ...' has + # started (assume it is non-empty to begin with). + prev_line = 'non-empty' + + # Now read lines until a new section (like a return or a since section) is + # encountered. + while line: + # See if the description section has ended (if the line begins with + # 'Returns ...' and the previous line was empty -- this loop replaces + # empty lines with a newline). + if no_colon_return_pattern.match(line) and prev_line == '\n': + return line + # Or if one of the patterns of the final sections match + for pattern in final_section_patterns: + if pattern.match(line): + return line + + # If not, append lines to description in the doc comment block. + cur_doc.append_to_description(line) + + prev_line = line + line = fp.readline() + + # Stop processing on eof or at the end of comment block. + if not line or comment_end_pattern.match(line): + return line + + # Remove initial ' * ' in line so that the text can be appended to the + # description of the comment block and make sure that if the line is + # empty it be interpreted as a newline. + line = comment_line_lead_pattern.sub('', line) + if not line: line = '\n' + +# Given the line that ended the description (the first line of one of the final +# sections) process the final sections ('Returns:', 'Since:', etc.) until the +# end of the comment block or eof. Return the line that ends the processing. +def process_final_sections(fp, line, cur_doc): + while line and not comment_end_pattern.match(line): + # Remove leading ' * ' from current non-empty comment line. + line = comment_line_lead_pattern.sub('', line) + # Temporarily append the no colon return pattern to the final section + # patterns now that the description has been processed. It will be + # removed after the for loop below executes so that future descriptions + # that begin with 'Returns ...' are not interpreted as a return + # section. + final_section_patterns.append(no_colon_return_pattern) + for pattern in final_section_patterns: + match = pattern.match(line) + if match: + if pattern == return_pattern or \ + pattern == no_colon_return_pattern: + # Dealing with a 'Returns:' so first extract the + # annotations from the description and save them. + description = match.group(2) + annotations = [] + annotation_match = \ + annotations_pattern.match(description) + if annotation_match: + annotations = \ + get_annotation_list(annotation_match.group(1)) + # Remove the annotations from the description + description = annotations_pattern.sub('', description) + + # Now add the return. + cur_doc.add_return(description, annotations) + # In case more lines need to be appended. + append_func = cur_doc.append_to_return + elif pattern == rename_to_pattern: + # Dealing with a 'Rename to:' section (GObjectIntrospection + # annotation) so no further lines will be appended but this + # single one (and only to the annotations). + append_func = None + cur_doc.add_annotation((match.group(1), + match.group(2))) + else: + # For all others ('Since:' and 'Deprecated:') just append + # the line to the description for now. + cur_doc.append_to_description(line) + # In case more lines need to be appended. + append_func = cur_doc.append_to_description + + # Stop final section pattern matching for loop since a match + # has already been found. + break + + # Remove the no colon return pattern (which was temporarily added in + # the just executed loop) from the list of final section patterns. + final_section_patterns.pop() + + line = fp.readline() + + # Now continue appending lines to current section until a new one is + # found or an eof or the end of the comment block is encountered. + finished = False + while not finished and line and \ + not comment_end_pattern.match(line): + # Remove leading ' * ' from line and make sure that if it is empty, + # it be interpreted as a newline. + line = comment_line_lead_pattern.sub('', line) + if not line: line = '\n' + + for pattern in final_section_patterns: + if pattern.match(line): + finished = True + break + + # Break out of loop if a new section is found (determined in above + # inner loop). + if finished: break + + # Now it's safe to append line. + if append_func: append_func(line) + + # Get the next line to continue processing. + line = fp.readline() + + return line + +def parse_dir(dir, doc_dict): + for file in os.listdir(dir): + if file in ('.', '..'): continue + path = os.path.join(dir, file) + if os.path.isdir(path): + parse_dir(path, doc_dict) + if len(file) > 2 and file[-2:] == '.c': + sys.stderr.write("Processing " + path + '\n') + parse_file(open(path, 'r'), doc_dict) + +def extract(dirs, doc_dict=None): + if not doc_dict: doc_dict = {} + for dir in dirs: + parse_dir(dir, doc_dict) + return doc_dict + +tmpl_section_pattern = re.compile(r'^<!-- ##### (\w+) (\w+) ##### -->$') +def parse_tmpl(fp, doc_dict): + cur_doc = None + + line = fp.readline() + while line: + match = tmpl_section_pattern.match(line) + if match: + cur_doc = None # new input shouldn't affect the old doc dict + sect_type = match.group(1) + sect_name = match.group(2) + + if sect_type == 'FUNCTION': + cur_doc = doc_dict.get(sect_name) + if not cur_doc: + cur_doc = GtkDoc() + cur_doc.set_name(sect_name) + doc_dict[sect_name] = cur_doc + elif line == '<!-- # Unused Parameters # -->\n': + cur_doc = None # don't worry about unused params. + elif cur_doc: + if line[:10] == '@Returns: ': + if string.strip(line[10:]): + cur_doc.append_to_return(line[10:]) + elif line[0] == '@': + pos = string.find(line, ':') + if pos >= 0: + cur_doc.append_to_named_param(line[1:pos], line[pos+1:]) + else: + cur_doc.append_to_description(line) + else: + cur_doc.append_to_description(line) + + line = fp.readline() + +def extract_tmpl(dirs, doc_dict=None): + if not doc_dict: doc_dict = {} + for dir in dirs: + for file in os.listdir(dir): + if file in ('.', '..'): continue + path = os.path.join(dir, file) + if os.path.isdir(path): + continue + if len(file) > 2 and file[-2:] == '.sgml': + parse_tmpl(open(path, 'r'), doc_dict) + return doc_dict diff --git a/codegen/docextract_to_xml.py b/codegen/docextract_to_xml.py new file mode 100755 index 0000000..7ac1053 --- /dev/null +++ b/codegen/docextract_to_xml.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# -*- Mode: Python; py-indent-offset: 4 -*- +# +# This litte script outputs the C doc comments to an XML format. +# So far it's only used by gtkmm (The C++ bindings). Murray Cumming. +# Usage example: +# # ./docextract_to_xml.py -s /gnome/head/cvs/gtk+/gtk/ -s /gnome/head/cvs/gtk+/docs/reference/gtk/tmpl/ > gtk_docs.xml + +import getopt +import re +import string +import sys + +import docextract + +def usage(): + sys.stderr.write('usage: docextract_to_xml.py ' + + '[-s /src/dir | --source-dir=/src/dir] ' + + '[-a | --with-annotations] [-p | --with-properties] ' + + '[-i | --with-signals ]\n') + sys.exit(1) + +# Translates special texts to &... HTML acceptable format. Also replace +# occurrences of '/*' and '*/' with '/ *' and '* /' respectively to avoid +# comment errors (note the spaces). Some function descriptions include C++ +# multi-line comments which cause errors when the description is included in a +# C++ Doxygen comment block. +def escape_text(unescaped_text): + # Escape every "&" not part of an entity reference + escaped_text = re.sub(r'&(?![A-Za-z]+;)', '&', unescaped_text) + + # These weird entities turn up in the output... + escaped_text = string.replace(escaped_text, '—', '—') + escaped_text = string.replace(escaped_text, '*', '*') + escaped_text = string.replace(escaped_text, '%', '%') + escaped_text = string.replace(escaped_text, '@', '@') + escaped_text = string.replace(escaped_text, '#', '#') + escaped_text = string.replace(escaped_text, ' ', ' ') + # This represents a '/' before or after an '*' so replace with slash but + # with spaces. + escaped_text = string.replace(escaped_text, '/', ' / ') + + # Escape for both tag contents and attribute values + escaped_text = string.replace(escaped_text, '<', '<') + escaped_text = string.replace(escaped_text, '>', '>') + escaped_text = string.replace(escaped_text, '"', '"') + + # Replace C++ comment begin and ends to ones that don't affect Doxygen. + escaped_text = string.replace(escaped_text, '/*', '/ *') + escaped_text = string.replace(escaped_text, '*/', '* /') + + return escaped_text + +def print_annotations(annotations): + for annotation in annotations: + print "<annotation name=" + annotation[0] + ">" + \ + escape_text(annotation[1]) + "</annotation>" + +if __name__ == '__main__': + try: + opts, args = getopt.getopt(sys.argv[1:], "d:s:o:api", + ["source-dir=", "with-annotations", + "with-properties", "with-signals"]) + except getopt.error, e: + sys.stderr.write('docextract_to_xml.py: %s\n' % e) + usage() + source_dirs = [] + with_annotations = False + with_signals = False + with_properties = False + for opt, arg in opts: + if opt in ('-s', '--source-dir'): + source_dirs.append(arg) + if opt in ('-a', '--with-annotations'): + with_annotations = True + if opt in ('-p', '--with-properties'): + with_properties = True + if opt in ('-i', '--with-signals'): + with_signals = True + if len(args) != 0: + usage() + + docs = docextract.extract(source_dirs); + docextract.extract_tmpl(source_dirs, docs); #Try the tmpl sgml files too. + + # print d.docs + + if docs: + + print "<root>" + + for name, value in docs.items(): + # Get the type of comment block ('function', 'signal' or + # 'property') (the value is a GtkDoc). + block_type = value.get_type() + + # Skip signals if the option was not specified. + if block_type == 'signal' and not with_signals: + continue + # Likewise for properties. + elif block_type == 'property' and not with_properties: + continue + + print "<" + block_type + " name=\"" + escape_text(name) + "\">" + + print "<description>" + print escape_text(value.get_description()) + print "</description>" + + # Loop through the parameters if not dealing with a property: + if block_type != 'property': + print "<parameters>" + for name, description, annotations in value.params: + print "<parameter name=\"" + escape_text(name) + "\">" + print "<parameter_description>" + escape_text(description) + "</parameter_description>" + + if with_annotations: + print_annotations(annotations) + + print "</parameter>" + + print "</parameters>" + + # Show the return-type (also if not dealing with a property): + if with_annotations: + print "<return>" + print "<return_description>" + escape_text(value.ret[0]) + \ + "</return_description>" + print_annotations(value.ret[1]) + print "</return>" + else: + print "<return>" + escape_text(value.ret[0]) + "</return>" + + if with_annotations: + print_annotations(value.get_annotations()) + + print "</" + block_type + ">\n" + + print "</root>" diff --git a/codegen/docgen.py b/codegen/docgen.py new file mode 100755 index 0000000..b9e2f67 --- /dev/null +++ b/codegen/docgen.py @@ -0,0 +1,766 @@ +#!/usr/bin/env python +import getopt +import os +import re +import sys + +import definitions +import defsparser +import docextract +import override + + +class Node: + + def __init__(self, name, interfaces=[]): + self.name = name + self.interfaces = interfaces + self.subclasses = [] + + def add_child(self, node): + self.subclasses.append(node) + + +def build_object_tree(parser): + # reorder objects so that parent classes come first ... + objects = parser.objects[:] + pos = 0 + while pos < len(objects): + parent = objects[pos].parent + for i in range(pos+1, len(objects)): + if objects[i].c_name == parent: + objects.insert(i+1, objects[pos]) + del objects[pos] + break + else: + pos = pos + 1 + + root = Node(None) + nodes = {None: root} + for obj_def in objects: + parent_name = obj_def.parent + if parent_name == 'GObject': + parent_name = None + parent_node = nodes[parent_name] + node = Node(obj_def.c_name, obj_def.implements) + parent_node.add_child(node) + nodes[node.name] = node + + if parser.interfaces: + interfaces = Node('gobject.GInterface') + root.add_child(interfaces) + nodes[interfaces.name] = interfaces + for obj_def in parser.interfaces: + node = Node(obj_def.c_name) + interfaces.add_child(node) + nodes[node.name] = node + + if parser.boxes: + boxed = Node('gobject.GBoxed') + root.add_child(boxed) + nodes[boxed.name] = boxed + for obj_def in parser.boxes: + node = Node(obj_def.c_name) + boxed.add_child(node) + nodes[node.name] = node + + if parser.pointers: + pointers = Node('gobject.GPointer') + root.add_child(pointers) + nodes[pointers.name] = pointers + for obj_def in parser.pointers: + node = Node(obj_def.c_name) + pointers.add_child(node) + nodes[node.name] = node + + return root + + +class DocWriter: + + def __init__(self): + self._fp = None + # parse the defs file + self.parser = defsparser.DefsParser(()) + self.overrides = override.Overrides() + self.classmap = {} + self.docs = {} + + def add_sourcedirs(self, source_dirs): + self.docs = docextract.extract(source_dirs, self.docs) + + def add_tmpldirs(self, tmpl_dirs): + self.docs = docextract.extract_tmpl(tmpl_dirs, self.docs) + + def add_docs(self, defs_file, overrides_file, module_name): + '''parse information about a given defs file''' + self.parser.filename = defs_file + self.parser.startParsing(defs_file) + if overrides_file: + self.overrides.handle_file(overrides_file) + + for obj in (self.parser.objects + self.parser.interfaces + + self.parser.boxes + self.parser.pointers): + if not obj.c_name in self.classmap: + self.classmap[obj.c_name] = '%s.%s' % ( + module_name, obj.name) + + def pyname(self, name): + return self.classmap.get(name, name) + + def _compare(self, obja, objb): + return cmp(self.pyname(obja.c_name), self.pyname(objb.c_name)) + + def output_docs(self, output_prefix): + files = {} + + # class hierarchy + hierarchy = build_object_tree(self.parser) + filename = self.create_filename('hierarchy', output_prefix) + self._fp = open(filename, 'w') + self.write_full_hierarchy(hierarchy) + self._fp.close() + + obj_defs = (self.parser.objects + self.parser.interfaces + + self.parser.boxes + self.parser.pointers) + obj_defs.sort(self._compare) + + for obj_def in obj_defs: + filename = self.create_filename(obj_def.c_name, output_prefix) + self._fp = open(filename, 'w') + if isinstance(obj_def, definitions.ObjectDef): + self.output_object_docs(obj_def) + elif isinstance(obj_def, definitions.InterfaceDef): + self.output_interface_docs(obj_def) + elif isinstance(obj_def, definitions.BoxedDef): + self.output_boxed_docs(obj_def) + elif isinstance(obj_def, definitions.PointerDef): + self.output_boxed_docs(obj_def) + self._fp.close() + files[os.path.basename(filename)] = obj_def + + if not files: + return + + output_filename = self.create_toc_filename(output_prefix) + self._fp = open(output_filename, 'w') + self.output_toc(files) + self._fp.close() + + def output_object_docs(self, obj_def): + self.write_class_header(obj_def.c_name) + + self.write_heading('Synopsis') + self.write_synopsis(obj_def) + self.close_section() + + # construct the inheritence hierarchy ... + ancestry = [(obj_def.c_name, obj_def.implements)] + try: + parent = obj_def.parent + while parent != None: + if parent == 'GObject': + ancestry.append(('GObject', [])) + parent = None + else: + parent_def = self.parser.find_object(parent) + ancestry.append((parent_def.c_name, parent_def.implements)) + parent = parent_def.parent + except ValueError: + pass + ancestry.reverse() + self.write_heading('Ancestry') + self.write_hierarchy(obj_def.c_name, ancestry) + self.close_section() + + constructor = self.parser.find_constructor(obj_def, self.overrides) + if constructor: + self.write_heading('Constructor') + self.write_constructor(constructor, + self.docs.get(constructor.c_name, None)) + self.close_section() + + methods = self.parser.find_methods(obj_def) + methods = filter(lambda meth, self=self: + not self.overrides.is_ignored(meth.c_name), methods) + if methods: + self.write_heading('Methods') + for method in methods: + self.write_method(method, self.docs.get(method.c_name, None)) + self.close_section() + + self.write_class_footer(obj_def.c_name) + + def get_methods_for_object(self, obj_def): + methods = [] + for method in self.parser.find_methods(obj_def): + if not self.overrides.is_ignored(method.c_name): + methods.append(method) + return methods + + def output_interface_docs(self, int_def): + self.write_class_header(int_def.c_name) + + self.write_heading('Synopsis') + self.write_synopsis(int_def) + self.close_section() + + methods = self.get_methods_for_object(int_def) + if methods: + self.write_heading('Methods') + for method in methods: + self.write_method(method, self.docs.get(method.c_name, None)) + self.close_section() + self.write_class_footer(int_def.c_name) + + def output_boxed_docs(self, box_def): + self.write_class_header(box_def.c_name) + + self.write_heading('Synopsis') + self.write_synopsis(box_def) + self.close_section() + + constructor = self.parser.find_constructor(box_def, self.overrides) + if constructor: + self.write_heading('Constructor') + self.write_constructor(constructor, + self.docs.get(constructor.c_name, None)) + self.close_section() + + methods = self.get_methods_for_object(box_def) + if methods: + self.write_heading('Methods') + for method in methods: + self.write_method(method, self.docs.get(method.c_name, None)) + self.close_section() + + self.write_class_footer(box_def.c_name) + + def output_toc(self, files): + self._fp.write('TOC\n\n') + for filename in sorted(files): + obj_def = files[filename] + self._fp.write(obj_def.c_name + ' - ' + filename + '\n') + + # override the following to create a more complex output format + + def create_filename(self, obj_name, output_prefix): + '''Create output filename for this particular object''' + return output_prefix + '-' + obj_name.lower() + '.txt' + + def create_toc_filename(self, output_prefix): + return self.create_filename(self, 'docs', output_prefix) + + def write_full_hierarchy(self, hierarchy): + + def handle_node(node, indent=''): + for child in node.subclasses: + self._fp.write(indent + node.name) + if node.interfaces: + self._fp.write(' (implements ') + self._fp.write(', '.join(node.interfaces)) + self._fp.write(')\n') + else: + self._fp.write('\n') + handle_node(child, indent + ' ') + handle_node(hierarchy) + + def serialize_params(self, func_def): + params = [] + for param in func_def.params: + params.append(param[1]) + return ', '.join(params) + + # these need to handle default args ... + + def create_constructor_prototype(self, func_def): + return '%s(%s)' % (func_def.is_constructor_of, + self.serialize_params(func_def)) + + def create_function_prototype(self, func_def): + return '%s(%s)' % (func_def.name, + self.serialize_params(func_def)) + + def create_method_prototype(self, meth_def): + return '%s.%s(%s)' % (meth_def.of_object, + meth_def.name, + self.serialize_params(meth_def)) + + def write_class_header(self, obj_name): + self._fp.write('Class %s\n' % obj_name) + self._fp.write('======%s\n\n' % ('=' * len(obj_name))) + + def write_class_footer(self, obj_name): + pass + + def write_heading(self, text): + self._fp.write('\n' + text + '\n' + ('-' * len(text)) + '\n') + + def close_section(self): + pass + + def write_synopsis(self, obj_def): + self._fp.write('class %s' % obj_def.c_name) + if isinstance(obj_def, definitions.ObjectDef): + bases = [] + if obj_def.parent: + bases.append(obj_def.parent) + bases = bases = obj_def.implements + if bases: + self._fp.write('(%s)' % ', '.join(bases, )) + self._fp.write(':\n') + + constructor = self.parser.find_constructor(obj_def, self.overrides) + if constructor: + prototype = self.create_constructor_prototype(constructor) + self._fp.write(' def %s\n' % prototype) + + for method in self.get_methods_for_object(obj_def): + prototype = self.create_method_prototype(method) + self._fp.write(' def %s\n' % prototype) + + def write_hierarchy(self, obj_name, ancestry): + indent = '' + for name, interfaces in ancestry: + self._fp.write(indent + '+-- ' + name) + if interfaces: + self._fp.write(' (implements ') + self._fp.write(', '.join(interfaces)) + self._fp.write(')\n') + else: + self._fp.write('\n') + indent = indent + ' ' + self._fp.write('\n') + + def write_constructor(self, func_def, func_doc): + prototype = self.create_constructor_prototype(func_def) + self._fp.write(prototype + '\n\n') + for type, name, dflt, null in func_def.params: + self.write_parameter(name, func_doc) + self.write_return_value(func_def, func_doc) + if func_doc and func_doc.description: + self._fp.write(func_doc.description) + self._fp.write('\n\n\n') + + def write_method(self, meth_def, func_doc): + prototype = self.create_method_prototype(meth_def) + self._fp.write(prototype + '\n\n') + for type, name, dflt, null in meth_def.params: + self.write_parameter(name, func_doc) + self.write_return_value(meth_def, func_doc) + if func_doc and func_doc.description: + self._fp.write('\n') + self._fp.write(func_doc.description) + self._fp.write('\n\n') + + def write_parameter(self, param_name, func_doc): + if func_doc: + descr = func_doc.get_param_description(param_name) + else: + descr = 'a ' + type + self._fp.write(' ' + param_name + ': ' + descr + '\n') + + def write_return_value(self, meth_def, func_doc): + if meth_def.ret and meth_def.ret != 'none': + if func_doc and func_doc.ret: + descr = func_doc.ret + else: + descr = 'a ' + meth_def.ret + self._fp.write(' Returns: ' + descr + '\n') + +CLASS_HEADER_TEMPLATE = """<refentry id="%(entryid)s"> + <refmeta> + <refentrytitle>%(name)s</refentrytitle> + <manvolnum>3</manvolnum> + <refmiscinfo>%(miscinfo)s</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>%(name)s</refname><refpurpose></refpurpose> + </refnamediv> + +""" +VARIABLE_TEMPLATE = """<varlistentry> + <term><parameter>%(parameter)s</parameter> :</term> + <listitem><simpara>%(description)s</simpara></listitem> + </varlistentry> +""" + +DOCBOOK_HEADER = """<?xml version="1.0" standalone="no"?> +<!DOCTYPE synopsis PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd"> +""" + + +class DocbookDocWriter(DocWriter): + + def __init__(self): + DocWriter.__init__(self) + + self._function_pat = re.compile(r'(\w+)\s*\(\)') + self._parameter_pat = re.compile(r'\@(\w+)') + self._constant_pat = re.compile(r'\%(-?\w+)') + self._symbol_pat = re.compile(r'#([\w-]+)') + + self._transtable = ['-'] * 256 + # make string -> reference translation func + for digit in '0123456789': + self._transtable[ord(digit)] = digit + + for letter in 'abcdefghijklmnopqrstuvwxyz': + self._transtable[ord(letter)] = letter + self._transtable[ord(letter.upper())] = letter + self._transtable = ''.join(self._transtable) + + def create_filename(self, obj_name, output_prefix): + '''Create output filename for this particular object''' + stem = output_prefix + '-' + obj_name.lower() + return stem + '.xml' + + def create_toc_filename(self, output_prefix): + return self.create_filename('classes', output_prefix) + + def make_class_ref(self, obj_name): + return 'class-' + obj_name.translate(self._transtable) + + def make_method_ref(self, meth_def): + return 'method-%s--%s' % ( + meth_def.of_object.translate(self._transtable), + meth_def.name.translate(self._transtable)) + + def _format_function(self, match): + info = self.parser.c_name.get(match.group(1), None) + if info: + if isinstance(info, defsparser.FunctionDef): + return self._format_funcdef(info) + if isinstance(info, defsparser.MethodDef): + return self._format_method(info) + + # fall through through + return '<function>%s()</function>' % (match.group(1), ) + + def _format_funcdef(self, info): + if info.is_constructor_of is not None: + # should have a link here + return '<methodname>%s()</methodname>' % ( + self.pyname(info.is_constructor_of), ) + else: + return '<function>%s()</function>' % (info.name, ) + + def _format_param(self, match): + return '<parameter>%s</parameterdliteral>' % (match.group(1), ) + + def _format_const(self, match): + return '<literal>%s</literal>' % (match.group(1), ) + + def _format_method(self, info): + return ('<link linkend="%s">' + '<methodname>%s.%s</methodname>' + '</link>') % (self.make_method_ref(info), + self.pyname(info.of_object), + info.name) + + def _format_object(self, info): + return ('<link linkend="%s">' + '<classname>%s</classname>' + '</link>') % (self.make_class_ref(info.c_name), + self.pyname(info.c_name)) + + def _format_symbol(self, match): + info = self.parser.c_name.get(match.group(1), None) + if info: + if isinstance(info, defsparser.FunctionDef): + return self._format_funcdef(info) + elif isinstance(info, defsparser.MethodDef): + return self._format_method(info) + elif isinstance(info, (defsparser.ObjectDef, + defsparser.InterfaceDef, + defsparser.BoxedDef, + defsparser.PointerDef)): + return self._format_object(info) + + # fall through through + return '<literal>%s</literal>' % (match.group(1), ) + + def reformat_text(self, text, singleline=0): + # replace special strings ... + text = self._function_pat.sub(self._format_function, text) + text = self._parameter_pat.sub(self._format_param, text) + text = self._constant_pat.sub(self._format_const, text) + text = self._symbol_pat.sub(self._format_symbol, text) + + # don't bother with <para> expansion for single line text. + if singleline: + return text + + lines = text.strip().split('\n') + for index in range(len(lines)): + if lines[index].strip() == '': + lines[index] = '</para>\n<para>' + continue + return '<para>%s</para>' % ('\n'.join(lines), ) + + # write out hierarchy + + def write_full_hierarchy(self, hierarchy): + + def handle_node(node, indent=''): + if node.name: + self._fp.write('%s<link linkend="%s">%s</link>' % + (indent, self.make_class_ref(node.name), + self.pyname(node.name))) + if node.interfaces: + self._fp.write(' (implements ') + for i in range(len(node.interfaces)): + self._fp.write('<link linkend="%s">%s</link>' % + (self.make_class_ref(node.interfaces[i]), + self.pyname(node.interfaces[i]))) + if i != len(node.interfaces) - 1: + self._fp.write(', ') + self._fp.write(')\n') + else: + self._fp.write('\n') + + indent = indent + ' ' + node.subclasses.sort(lambda a, b: + cmp(self.pyname(a.name), self.pyname(b.name))) + for child in node.subclasses: + handle_node(child, indent) + + self._fp.write(DOCBOOK_HEADER) + self._fp.write('<synopsis>') + handle_node(hierarchy) + self._fp.write('</synopsis>\n') + + # these need to handle default args ... + + def create_constructor_prototype(self, func_def): + xml = ['<constructorsynopsis language="python">\n'] + xml.append(' <methodname>__init__</methodname>\n') + for type, name, dflt, null in func_def.params: + xml.append(' <methodparam><parameter>') + xml.append(name) + xml.append('</parameter>') + if dflt: + xml.append('<initializer>') + xml.append(dflt) + xml.append('</initializer>') + xml.append('</methodparam>\n') + if not func_def.params: + xml.append(' <methodparam></methodparam>') + xml.append(' </constructorsynopsis>') + return ''.join(xml) + + def create_function_prototype(self, func_def): + xml = ['<funcsynopsis language="python">\n <funcprototype>\n'] + xml.append(' <funcdef><function>') + xml.append(func_def.name) + xml.append('</function></funcdef>\n') + for type, name, dflt, null in func_def.params: + xml.append(' <paramdef><parameter>') + xml.append(name) + xml.append('</parameter>') + if dflt: + xml.append('<initializer>') + xml.append(dflt) + xml.append('</initializer>') + xml.append('</paramdef>\n') + if not func_def.params: + xml.append(' <paramdef></paramdef') + xml.append(' </funcprototype>\n </funcsynopsis>') + return ''.join(xml) + + def create_method_prototype(self, meth_def, addlink=0): + xml = ['<methodsynopsis language="python">\n'] + xml.append(' <methodname>') + if addlink: + xml.append('<link linkend="%s">' % self.make_method_ref(meth_def)) + xml.append(self.pyname(meth_def.name)) + if addlink: + xml.append('</link>') + xml.append('</methodname>\n') + for type, name, dflt, null in meth_def.params: + xml.append(' <methodparam><parameter>') + xml.append(name) + xml.append('</parameter>') + if dflt: + xml.append('<initializer>') + xml.append(dflt) + xml.append('</initializer>') + xml.append('</methodparam>\n') + if not meth_def.params: + xml.append(' <methodparam></methodparam>') + xml.append(' </methodsynopsis>') + return ''.join(xml) + + def write_class_header(self, obj_name): + self._fp.write(DOCBOOK_HEADER) + self._fp.write(CLASS_HEADER_TEMPLATE % dict( + entryid=self.make_class_ref(obj_name), + name=self.pyname(obj_name), + miscinfo="PyGTK Docs")) + + def write_class_footer(self, obj_name): + self._fp.write('</refentry>\n') + + def write_heading(self, text): + self._fp.write(' <refsect1>\n') + self._fp.write(' <title>' + text + '</title>\n\n') + + def close_section(self): + self._fp.write(' </refsect1>\n') + + def write_synopsis(self, obj_def): + self._fp.write('<classsynopsis language="python">\n') + self._fp.write(' <ooclass><classname>%s</classname></ooclass>\n' + % self.pyname(obj_def.c_name)) + if isinstance(obj_def, definitions.ObjectDef): + if obj_def.parent: + self._fp.write(' <ooclass><classname><link linkend="%s">%s' + '</link></classname></ooclass>\n' + % (self.make_class_ref(obj_def.parent), + self.pyname(obj_def.parent))) + for base in obj_def.implements: + self._fp.write(' <ooclass><classname><link linkend="%s">%s' + '</link></classname></ooclass>\n' + % (self.make_class_ref(base), self.pyname(base))) + elif isinstance(obj_def, definitions.InterfaceDef): + self._fp.write(' <ooclass><classname>gobject.GInterface' + '</classname></ooclass>\n') + elif isinstance(obj_def, definitions.BoxedDef): + self._fp.write(' <ooclass><classname>gobject.GBoxed' + '</classname></ooclass>\n') + elif isinstance(obj_def, definitions.PointerDef): + self._fp.write(' <ooclass><classname>gobject.GPointer' + '</classname></ooclass>\n') + + constructor = self.parser.find_constructor(obj_def, self.overrides) + if constructor: + self._fp.write( + '%s\n' % self.create_constructor_prototype(constructor)) + for method in self.get_methods_for_object(obj_def): + self._fp.write( + '%s\n' % self.create_method_prototype(method, addlink=1)) + self._fp.write('</classsynopsis>\n\n') + + def write_hierarchy(self, obj_name, ancestry): + self._fp.write('<synopsis>') + indent = '' + for name, interfaces in ancestry: + self._fp.write( + '%s+-- <link linkend="%s">%s</link>' % + (indent, self.make_class_ref(name), self.pyname(name))) + if interfaces: + self._fp.write(' (implements ') + for i in range(len(interfaces)): + self._fp.write('<link linkend="%s">%s</link>' % + (self.make_class_ref(interfaces[i]), + self.pyname(interfaces[i]))) + if i != len(interfaces) - 1: + self._fp.write(', ') + self._fp.write(')\n') + else: + self._fp.write('\n') + indent = indent + ' ' + self._fp.write('</synopsis>\n\n') + + def write_params(self, params, ret, func_doc): + if not params and (not ret or ret == 'none'): + return + self._fp.write(' <variablelist>\n') + for type, name, dflt, null in params: + if func_doc: + descr = func_doc.get_param_description(name).strip() + else: + descr = 'a ' + type + self._fp.write(VARIABLE_TEMPLATE % dict( + parameter=name, + description=self.reformat_text(descr, singleline=1))) + if ret and ret != 'none': + if func_doc and func_doc.ret: + descr = func_doc.ret.strip() + else: + descr = 'a ' + ret + self._fp.write(VARIABLE_TEMPLATE % dict( + parameter='Returns', + description=self.reformat_text(descr, singleline=1))) + self._fp.write(' </variablelist>\n') + + def write_constructor(self, func_def, func_doc): + prototype = self.create_constructor_prototype(func_def) + self._fp.write('<programlisting>%s</programlisting>\n' % prototype) + self.write_params(func_def.params, func_def.ret, func_doc) + + if func_doc and func_doc.description: + self._fp.write(self.reformat_text(func_doc.description)) + self._fp.write('\n\n\n') + + def write_method(self, meth_def, func_doc): + self._fp.write(' <refsect2 id="%s">\n' % ( + self.make_method_ref(meth_def), )) + self._fp.write(' <title>%s.%s</title>\n\n' % ( + self.pyname(meth_def.of_object), + meth_def.name)) + prototype = self.create_method_prototype(meth_def) + self._fp.write('<programlisting>%s</programlisting>\n' % prototype) + self.write_params(meth_def.params, meth_def.ret, func_doc) + if func_doc and func_doc.description: + self._fp.write(self.reformat_text(func_doc.description)) + self._fp.write(' </refsect2>\n\n\n') + + def output_toc(self, files, fp=sys.stdout): + self._fp.write(DOCBOOK_HEADER) + + #self._fp.write('<reference id="class-reference">\n') + #self._fp.write(' <title>Class Documentation</title>\n') + #for filename, obj_def in files: + # self._fp.write('&' + + # obj_def.c_name.translate(self._transtable) + ';\n') + #self._fp.write('</reference>\n') + + self._fp.write('<reference id="class-reference" ' + 'xmlns:xi="http://www.w3.org/2001/XInclude">\n') + self._fp.write(' <title>Class Reference</title>\n') + for filename in sorted(files): + self._fp.write(' <xi:include href="%s"/>\n' % filename) + self._fp.write('</reference>\n') + + +def main(args): + try: + opts, args = getopt.getopt(args[1:], "d:s:o:", + ["defs-file=", "override=", "source-dir=", + "output-prefix="]) + except getopt.error, e: + sys.stderr.write('docgen.py: %s\n' % e) + sys.stderr.write( + 'usage: docgen.py -d file.defs [-s /src/dir] [-o output-prefix]\n') + return 1 + + defs_file = None + overrides_file = None + source_dirs = [] + output_prefix = 'docs' + for opt, arg in opts: + if opt in ('-d', '--defs-file'): + defs_file = arg + if opt in ('--override', ): + overrides_file = arg + elif opt in ('-s', '--source-dir'): + source_dirs.append(arg) + elif opt in ('-o', '--output-prefix'): + output_prefix = arg + if len(args) != 0 or not defs_file: + sys.stderr.write( + 'usage: docgen.py -d file.defs [-s /src/dir] [-o output-prefix]\n') + return 1 + + d = DocbookDocWriter() + d.add_sourcedirs(source_dirs) + d.add_docs(defs_file, overrides_file, 'gio') + d.output_docs(output_prefix) + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/codegen/h2def.py b/codegen/h2def.py new file mode 100755 index 0000000..17617fa --- /dev/null +++ b/codegen/h2def.py @@ -0,0 +1,631 @@ +#!/usr/bin/env python +# -*- Mode: Python; py-indent-offset: 4 -*- +# GPL'ed +# Toby D. Reeves <toby@max.rl.plh.af.mil> +# +# Modified by James Henstridge <james@daa.com.au> to output stuff in +# Havoc's new defs format. Info on this format can be seen at: +# http://mail.gnome.org/archives/gtk-devel-list/2000-January/msg00070.html +# Updated to be PEP-8 compatible and refactored to use OOP +# +# Scan the given public .h files of a GTK module (or module using +# GTK object conventions) and generates a set of scheme defs. +# +# h2def searches through a header file looking for function prototypes and +# generates a scheme style defenition for each prototype. +# Basically the operation of h2def is: +# +# - read each .h file into a buffer which is scrubbed of extraneous data +# - find all object defenitions: +# - find all structures that may represent a GtkObject +# - find all structures that might represent a class +# - find all structures that may represent a GtkObject subclass +# - find all structures that might represent a class/Iface inherited from +# GTypeInterface +# - find all enum defenitions +# - write out the defs +# +# The command line options are: +# +# -s --separate Create separate files for objects and function/method defs +# using the given name as the base name (optional). If this +# is not specified the combined object and function defs +# will be output to sys.stdout. +# -f --defsfilter Extract defs from the given file to filter the output defs +# that is don't output defs that are defined in the +# defsfile. More than one deffile may be specified. +# -m --modulename The prefix to be stripped from the front of function names +# for the given module +# -n --namespace The module or namespace name to be used, for example +# WebKit where h2def is unable to detect the module name +# automatically. it also sets the gtype-id prefix. +# --onlyenums Only produce defs for enums and flags +# --onlyobjdefs Only produce defs for objects +# -v Verbose output +# +# Examples: +# +# python h2def.py /usr/local/include/pango-1.0/pango/*.h >/tmp/pango.defs +# +# - Outputs all defs for the pango module. +# +# python h2def.py -m gdk -s /tmp/gdk-2.10 \ +# -f /usr/tmp/pygtk/gtk/gdk-base.defs \ +# /usr/local/include/gtk-2.0/gdk/*.h \ +# /usr/local/include/gtk-2.0/gdk-pixbuf/*.h +# +# - Outputs the gdk module defs that are not contained in the defs file +# /usr/tmp/pygtk/gtk/gdk-base.defs. Two output files are created: +# /tmp/gdk-2.10-types.defs and /tmp/gdk-2.10.defs. +# +# python h2def.py -n WebKit /usr/incude/webkit-1.0/webkit/*.h \ +# >/tmp/webkit.defs +# +# - Outputs all the defs for webkit module, setting the module name to WebKit +# and the gtype-id prefix to WEBKIT_ which can't be detected automatically. +# + +import getopt +import os +import re +import string +import sys + +import defsparser + +# ------------------ Create typecodes from typenames --------- + +_upperstr_pat1 = re.compile(r'([^A-Z])([A-Z])') +_upperstr_pat2 = re.compile(r'([A-Z][A-Z])([A-Z][0-9a-z])') +_upperstr_pat3 = re.compile(r'^([A-Z])([A-Z])') + +def to_upper_str(name): + """Converts a typename to the equivalent upercase and underscores + name. This is used to form the type conversion macros and enum/flag + name variables""" + name = _upperstr_pat1.sub(r'\1_\2', name) + name = _upperstr_pat2.sub(r'\1_\2', name) + name = _upperstr_pat3.sub(r'\1_\2', name, count=1) + return string.upper(name) + +def typecode(typename, namespace=None): + """create a typecode (eg. GTK_TYPE_WIDGET) from a typename""" + if namespace: + return string.replace(string.upper(namespace) + "_" + to_upper_str(typename[len(namespace):]), '_', '_TYPE_', 1) + + return string.replace(to_upper_str(typename), '_', '_TYPE_', 1) + + +# ------------------ Find object definitions ----------------- +# Strips the comments from buffer +def strip_comments(buf): + parts = [] + lastpos = 0 + while 1: + pos = string.find(buf, '/*', lastpos) + if pos >= 0: + parts.append(buf[lastpos:pos]) + pos = string.find(buf, '*/', pos) + if pos >= 0: + lastpos = pos + 2 + else: + break + else: + parts.append(buf[lastpos:]) + break + return string.join(parts, '') + +# Strips the dll API from buffer, for example WEBKIT_API +def strip_dll_api(buf): + pat = re.compile("[A-Z]*_API ") + buf = pat.sub("", buf) + return buf + +obj_name_pat = "[A-Z][a-z]*[A-Z][A-Za-z0-9]*" + +split_prefix_pat = re.compile('([A-Z]+[a-z]*)([A-Za-z0-9]+)') + +def find_obj_defs(buf, objdefs=[]): + """ + Try to find object definitions in header files. + """ + + # filter out comments from buffer. + buf = strip_comments(buf) + + # filter out dll api + buf = strip_dll_api(buf) + + maybeobjdefs = [] # contains all possible objects from file + + # first find all structures that look like they may represent a GtkObject + pat = re.compile("struct\s+_(" + obj_name_pat + ")\s*{\s*" + + "(" + obj_name_pat + ")\s+", re.MULTILINE) + pos = 0 + while pos < len(buf): + m = pat.search(buf, pos) + if not m: break + maybeobjdefs.append((m.group(1), m.group(2))) + pos = m.end() + + # handle typedef struct { ... } style struct defs. + pat = re.compile("typedef struct\s+[_\w]*\s*{\s*" + + "(" + obj_name_pat + ")\s+[^}]*}\s*" + + "(" + obj_name_pat + ")\s*;", re.MULTILINE) + pos = 0 + while pos < len(buf): + m = pat.search(buf, pos) + if not m: break + maybeobjdefs.append((m.group(2), m.group(1))) + pos = m.end() + + # now find all structures that look like they might represent a class: + pat = re.compile("struct\s+_(" + obj_name_pat + ")Class\s*{\s*" + + "(" + obj_name_pat + ")Class\s+", re.MULTILINE) + pos = 0 + while pos < len(buf): + m = pat.search(buf, pos) + if not m: break + t = (m.group(1), m.group(2)) + # if we find an object structure together with a corresponding + # class structure, then we have probably found a GtkObject subclass. + if t in maybeobjdefs: + objdefs.append(t) + pos = m.end() + + pat = re.compile("typedef struct\s+[_\w]*\s*{\s*" + + "(" + obj_name_pat + ")Class\s+[^}]*}\s*" + + "(" + obj_name_pat + ")Class\s*;", re.MULTILINE) + pos = 0 + while pos < len(buf): + m = pat.search(buf, pos) + if not m: break + t = (m.group(2), m.group(1)) + # if we find an object structure together with a corresponding + # class structure, then we have probably found a GtkObject subclass. + if t in maybeobjdefs: + objdefs.append(t) + pos = m.end() + + # now find all structures that look like they might represent + # a class inherited from GTypeInterface: + pat = re.compile("struct\s+_(" + obj_name_pat + ")Class\s*{\s*" + + "GTypeInterface\s+", re.MULTILINE) + pos = 0 + while pos < len(buf): + m = pat.search(buf, pos) + if not m: break + t = (m.group(1), '') + t2 = (m.group(1)+'Class', 'GTypeInterface') + # if we find an object structure together with a corresponding + # class structure, then we have probably found a GtkObject subclass. + if t2 in maybeobjdefs: + objdefs.append(t) + pos = m.end() + + # now find all structures that look like they might represent + # an Iface inherited from GTypeInterface: + pat = re.compile("struct\s+_(" + obj_name_pat + ")Iface\s*{\s*" + + "GTypeInterface\s+", re.MULTILINE) + pos = 0 + while pos < len(buf): + m = pat.search(buf, pos) + if not m: break + t = (m.group(1), '') + t2 = (m.group(1)+'Iface', 'GTypeInterface') + # if we find an object structure together with a corresponding + # class structure, then we have probably found a GtkObject subclass. + if t2 in maybeobjdefs: + objdefs.append(t) + pos = m.end() + +def sort_obj_defs(objdefs): + objdefs.sort() # not strictly needed, but looks nice + pos = 0 + while pos < len(objdefs): + klass,parent = objdefs[pos] + for i in range(pos+1, len(objdefs)): + # parent below subclass ... reorder + if objdefs[i][0] == parent: + objdefs.insert(i+1, objdefs[pos]) + del objdefs[pos] + break + else: + pos = pos + 1 + return objdefs + +# ------------------ Find enum definitions ----------------- + +def find_enum_defs(buf, enums=[]): + # strip comments + # bulk comments + buf = strip_comments(buf) + + # strip dll api macros + buf = strip_dll_api(buf) + + # strip # directives + pat = re.compile(r"""^[#].*?$""", re.MULTILINE) + buf = pat.sub('', buf) + + buf = re.sub('\n', ' ', buf) + + enum_pat = re.compile(r'enum\s*{([^}]*)}\s*([A-Z][A-Za-z]*)(\s|;)') + splitter = re.compile(r'\s*,\s', re.MULTILINE) + pos = 0 + while pos < len(buf): + m = enum_pat.search(buf, pos) + if not m: break + + name = m.group(2) + vals = m.group(1) + isflags = string.find(vals, '<<') >= 0 + entries = [] + for val in splitter.split(vals): + if not string.strip(val): continue + entries.append(string.split(val)[0]) + if name != 'GdkCursorType': + enums.append((name, isflags, entries)) + + pos = m.end() + +# ------------------ Find function definitions ----------------- + +def clean_func(buf): + """ + Ideally would make buf have a single prototype on each line. + Actually just cuts out a good deal of junk, but leaves lines + where a regex can figure prototypes out. + """ + # bulk comments + buf = strip_comments(buf) + + # dll api + buf = strip_dll_api(buf) + + # compact continued lines + pat = re.compile(r"""\\\n""", re.MULTILINE) + buf = pat.sub('', buf) + + # Preprocess directives + pat = re.compile(r"""^[#].*?$""", re.MULTILINE) + buf = pat.sub('', buf) + + #typedefs, stucts, and enums + pat = re.compile(r"""^(typedef|struct|enum)(\s|.|\n)*?;\s*""", + re.MULTILINE) + buf = pat.sub('', buf) + + #strip DECLS macros + pat = re.compile(r"""G_BEGIN_DECLS|BEGIN_LIBGTOP_DECLS""", re.MULTILINE) + buf = pat.sub('', buf) + + #extern "C" + pat = re.compile(r"""^\s*(extern)\s+\"C\"\s+{""", re.MULTILINE) + buf = pat.sub('', buf) + + #multiple whitespace + pat = re.compile(r"""\s+""", re.MULTILINE) + buf = pat.sub(' ', buf) + + #clean up line ends + pat = re.compile(r""";\s*""", re.MULTILINE) + buf = pat.sub('\n', buf) + buf = buf.lstrip() + + #associate *, &, and [] with type instead of variable + #pat = re.compile(r'\s+([*|&]+)\s*(\w+)') + pat = re.compile(r' \s* ([*|&]+) \s* (\w+)', re.VERBOSE) + buf = pat.sub(r'\1 \2', buf) + pat = re.compile(r'\s+ (\w+) \[ \s* \]', re.VERBOSE) + buf = pat.sub(r'[] \1', buf) + + # make return types that are const work. + buf = re.sub(r'\s*\*\s*G_CONST_RETURN\s*\*\s*', '** ', buf) + buf = string.replace(buf, 'G_CONST_RETURN ', 'const-') + buf = string.replace(buf, 'const ', 'const-') + + #strip GSEAL macros from the middle of function declarations: + pat = re.compile(r"""GSEAL""", re.VERBOSE) + buf = pat.sub('', buf) + + return buf + +proto_pat=re.compile(r""" +(?P<ret>(-|\w|\&|\*)+\s*) # return type +\s+ # skip whitespace +(?P<func>\w+)\s*[(] # match the function name until the opening ( +\s*(?P<args>.*?)\s*[)] # group the function arguments +""", re.IGNORECASE|re.VERBOSE) +#""" +arg_split_pat = re.compile("\s*,\s*") + +get_type_pat = re.compile(r'(const-)?([A-Za-z0-9]+)\*?\s+') +pointer_pat = re.compile('.*\*$') +func_new_pat = re.compile('(\w+)_new$') + +class DefsWriter: + def __init__(self, fp=None, prefix=None, ns=None, verbose=False, + defsfilter=None): + if not fp: + fp = sys.stdout + + self.fp = fp + self.prefix = prefix + self.namespace = ns + self.verbose = verbose + + self._enums = {} + self._objects = {} + self._functions = {} + if defsfilter: + filter = defsparser.DefsParser(defsfilter) + filter.startParsing() + for func in filter.functions + filter.methods.values(): + self._functions[func.c_name] = func + for obj in filter.objects + filter.boxes + filter.interfaces: + self._objects[obj.c_name] = obj + for obj in filter.enums: + self._enums[obj.c_name] = obj + + def write_def(self, deffile): + buf = open(deffile).read() + + self.fp.write('\n;; From %s\n\n' % os.path.basename(deffile)) + self._define_func(buf) + self.fp.write('\n') + + def write_enum_defs(self, enums, fp=None): + if not fp: + fp = self.fp + + fp.write(';; Enumerations and flags ...\n\n') + trans = string.maketrans(string.uppercase + '_', + string.lowercase + '-') + filter = self._enums + for cname, isflags, entries in enums: + if filter: + if cname in filter: + continue + name = cname + module = None + if self.namespace: + module = self.namespace + name = cname[len(self.namespace):] + else: + m = split_prefix_pat.match(cname) + if m: + module = m.group(1) + name = m.group(2) + if isflags: + fp.write('(define-flags ' + name + '\n') + else: + fp.write('(define-enum ' + name + '\n') + if module: + fp.write(' (in-module "' + module + '")\n') + fp.write(' (c-name "' + cname + '")\n') + fp.write(' (gtype-id "' + typecode(cname, self.namespace) + '")\n') + prefix = entries[0] + for ent in entries: + # shorten prefix til we get a match ... + # and handle GDK_FONT_FONT, GDK_FONT_FONTSET case + while ((len(prefix) and prefix[-1] != '_') or ent[:len(prefix)] != prefix + or len(prefix) >= len(ent)): + prefix = prefix[:-1] + prefix_len = len(prefix) + fp.write(' (values\n') + for ent in entries: + fp.write(' \'("%s" "%s")\n' % + (string.translate(ent[prefix_len:], trans), ent)) + fp.write(' )\n') + fp.write(')\n\n') + + def write_obj_defs(self, objdefs, fp=None): + if not fp: + fp = self.fp + + fp.write(';; -*- scheme -*-\n') + fp.write('; object definitions ...\n') + + filter = self._objects + for klass, parent in objdefs: + if filter: + if klass in filter: + continue + if self.namespace: + cname = klass[len(self.namespace):] + cmodule = self.namespace + else: + m = split_prefix_pat.match(klass) + cname = klass + cmodule = None + if m: + cmodule = m.group(1) + cname = m.group(2) + fp.write('(define-object ' + cname + '\n') + if cmodule: + fp.write(' (in-module "' + cmodule + '")\n') + if parent: + fp.write(' (parent "' + parent + '")\n') + fp.write(' (c-name "' + klass + '")\n') + fp.write(' (gtype-id "' + typecode(klass, self.namespace) + '")\n') + # should do something about accessible fields + fp.write(')\n\n') + + def _define_func(self, buf): + buf = clean_func(buf) + buf = string.split(buf,'\n') + filter = self._functions + for p in buf: + if not p: + continue + m = proto_pat.match(p) + if m == None: + if self.verbose: + sys.stderr.write('No match:|%s|\n' % p) + continue + func = m.group('func') + if func[0] == '_': + continue + if filter: + if func in filter: + continue + ret = m.group('ret') + args = m.group('args') + args = arg_split_pat.split(args) + for i in range(len(args)): + spaces = string.count(args[i], ' ') + if spaces > 1: + args[i] = string.replace(args[i], ' ', '-', spaces - 1) + + self._write_func(func, ret, args) + + def _write_func(self, name, ret, args): + if len(args) >= 1: + # methods must have at least one argument + munged_name = name.replace('_', '') + m = get_type_pat.match(args[0]) + if m: + obj = m.group(2) + if munged_name[:len(obj)] == obj.lower(): + self._write_method(obj, name, ret, args) + return + + if self.prefix: + l = len(self.prefix) + if name[:l] == self.prefix and name[l] == '_': + fname = name[l+1:] + else: + fname = name + else: + fname = name + + # it is either a constructor or normal function + self.fp.write('(define-function ' + fname + '\n') + self.fp.write(' (c-name "' + name + '")\n') + + # Hmmm... Let's asume that a constructor function name + # ends with '_new' and it returns a pointer. + m = func_new_pat.match(name) + if pointer_pat.match(ret) and m: + cname = '' + for s in m.group(1).split ('_'): + cname += s.title() + if cname != '': + self.fp.write(' (is-constructor-of "' + cname + '")\n') + + self._write_return(ret) + self._write_arguments(args) + + def _write_method(self, obj, name, ret, args): + regex = string.join(map(lambda x: x+'_?', string.lower(obj)),'') + mname = re.sub(regex, '', name, 1) + if self.prefix: + l = len(self.prefix) + 1 + if mname[:l] == self.prefix and mname[l+1] == '_': + mname = mname[l+1:] + self.fp.write('(define-method ' + mname + '\n') + self.fp.write(' (of-object "' + obj + '")\n') + self.fp.write(' (c-name "' + name + '")\n') + self._write_return(ret) + self._write_arguments(args[1:]) + + def _write_return(self, ret): + if ret != 'void': + self.fp.write(' (return-type "' + ret + '")\n') + else: + self.fp.write(' (return-type "none")\n') + + def _write_arguments(self, args): + is_varargs = 0 + has_args = len(args) > 0 + for arg in args: + if arg == '...': + is_varargs = 1 + elif arg in ('void', 'void '): + has_args = 0 + if has_args: + self.fp.write(' (parameters\n') + for arg in args: + if arg != '...': + tupleArg = tuple(string.split(arg)) + if len(tupleArg) == 2: + self.fp.write(' \'("%s" "%s")\n' % tupleArg) + self.fp.write(' )\n') + if is_varargs: + self.fp.write(' (varargs #t)\n') + self.fp.write(')\n\n') + +# ------------------ Main function ----------------- + +def main(args): + verbose = False + onlyenums = False + onlyobjdefs = False + separate = False + modulename = None + namespace = None + defsfilter = None + opts, args = getopt.getopt(args[1:], 'vs:m:n:f:', + ['onlyenums', 'onlyobjdefs', + 'modulename=', 'namespace=', + 'separate=', 'defsfilter=']) + for o, v in opts: + if o == '-v': + verbose = True + if o == '--onlyenums': + onlyenums = True + if o == '--onlyobjdefs': + onlyobjdefs = True + if o in ('-s', '--separate'): + separate = v + if o in ('-m', '--modulename'): + modulename = v + if o in ('-n', '--namespace'): + namespace = v + if o in ('-f', '--defsfilter'): + defsfilter = v + + if not args[0:1]: + print 'Must specify at least one input file name' + return -1 + + # read all the object definitions in + objdefs = [] + enums = [] + for filename in args: + buf = open(filename).read() + find_obj_defs(buf, objdefs) + find_enum_defs(buf, enums) + objdefs = sort_obj_defs(objdefs) + + if separate: + methods = file(separate + '.defs', 'w') + types = file(separate + '-types.defs', 'w') + + dw = DefsWriter(methods, prefix=modulename, ns=namespace, + verbose=verbose, defsfilter=defsfilter) + dw.write_obj_defs(objdefs, types) + dw.write_enum_defs(enums, types) + print "Wrote %s-types.defs" % separate + + for filename in args: + dw.write_def(filename) + print "Wrote %s.defs" % separate + else: + dw = DefsWriter(prefix=modulename, ns=namespace, + verbose=verbose, defsfilter=defsfilter) + + if onlyenums: + dw.write_enum_defs(enums) + elif onlyobjdefs: + dw.write_obj_defs(objdefs) + else: + dw.write_obj_defs(objdefs) + dw.write_enum_defs(enums) + + for filename in args: + dw.write_def(filename) + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/codegen/mergedefs.py b/codegen/mergedefs.py new file mode 100755 index 0000000..773e499 --- /dev/null +++ b/codegen/mergedefs.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# -*- Mode: Python; py-indent-offset: 4 -*- + +import optparse + +import defsparser + +parser = optparse.OptionParser( + usage="usage: %prog [options] generated-defs old-defs") +parser.add_option("-p", "--merge-parameters", + help="Merge changes in function/methods parameter lists", + action="store_true", dest="parmerge", default=False) +(options, args) = parser.parse_args() + +if len(args) != 2: + parser.error("wrong number of arguments") + +newp = defsparser.DefsParser(args[0]) +oldp = defsparser.DefsParser(args[1]) + +newp.startParsing() +oldp.startParsing() + +newp.merge(oldp, options.parmerge) + +newp.write_defs() diff --git a/codegen/missingdefs.py b/codegen/missingdefs.py new file mode 100755 index 0000000..f0017e7 --- /dev/null +++ b/codegen/missingdefs.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +# -*- Mode: Python; py-indent-offset: 4 -*- + +import sys +import defsparser + +if len(sys.argv) < 3: + sys.stderr.write("Usage: missingdefs.py generated-defs old-defs\n") + sys.exit(1) + +newp = defsparser.DefsParser(sys.argv[1]) +oldp = defsparser.DefsParser(sys.argv[2]) + +newp.startParsing() +oldp.startParsing() + +newp.printMissing(oldp) diff --git a/codegen/mkskel.py b/codegen/mkskel.py new file mode 100755 index 0000000..61f520b --- /dev/null +++ b/codegen/mkskel.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# -*- Mode: Python; py-indent-offset: 4 -*- + +import sys, os, getopt + +module_init_template = \ +'/* -*- Mode: C; c-basic-offset: 4 -*- */\n' + \ +'#ifdef HAVE_CONFIG_H\n' + \ +'# include "config.h"\n' + \ +'#endif\n' + \ +'#include <Python.h>\n' + \ +'#include <pygtk.h>\n' + \ +'\n' + \ +'/* include any extra headers needed here */\n' + \ +'\n' + \ +'void %(prefix)s_register_classes(PyObject *d);\n' + \ +'extern PyMethodDef %(prefix)s_functions[];\n' + \ +'\n' + \ +'DL_EXPORT(void)\n' + \ +'init%(module)s(void)\n' + \ +'{\n' + \ +' PyObject *m, *d;\n' + \ +'\n' + \ +' /* perform any initialisation required by the library here */\n' + \ +'\n' + \ +' m = Py_InitModule("%(module)s", %(prefix)s_functions);\n' + \ +' d = PyModule_GetDict(m);\n' + \ +'\n' + \ +' init_pygtk();\n' + \ +'\n' + \ +' %(prefix)s_register_classes(d);\n' + \ +'\n' + \ +' /* add anything else to the module dictionary (such as constants) */\n' +\ +'\n' + \ +' if (PyErr_Occurred())\n' + \ +' Py_FatalError("could not initialise module %(module)s");\n' + \ +'}\n' + +override_template = \ +'/* -*- Mode: C; c-basic-offset: 4 -*- */\n' + \ +'%%%%\n' + \ +'headers\n' + \ +'/* include any required headers here */\n' + \ +'%%%%\n' + \ +'init\n' + \ +' /* include any code here that needs to be executed before the\n' + \ +' * extension classes get initialised */\n' + \ +'%%%%\n' + \ +'\n' + \ +'/* you should add appropriate ignore, ignore-glob and\n' + \ +' * override sections here */\n' + +def open_with_backup(file): + if os.path.exists(file): + try: + os.rename(file, file+'~') + except OSError: + # fail silently if we can't make a backup + pass + return open(file, 'w') + +def write_skels(fileprefix, prefix, module): + fp = open_with_backup(fileprefix+'module.c') + fp.write(module_init_template % { 'prefix': prefix, 'module': module }) + fp.close() + fp = open_with_backup(fileprefix+'.override') + fp.write(override_template % { 'prefix': prefix, 'module': module }) + fp.close() + +if __name__ == '__main__': + opts, args = getopt.getopt(sys.argv[1:], 'f:p:m:h', + ['file-prefix=', 'prefix=', 'module=', 'help']) + fileprefix = None + prefix = None + module = None + for opt, arg in opts: + if opt in ('-f', '--file-prefix'): + fileprefix = arg + elif opt in ('-p', '--prefix'): + prefix = arg + elif opt in ('-m', '--module'): + module = arg + elif opt in ('-h', '--help'): + print 'usage: mkskel.py -f fileprefix -p prefix -m module' + sys.exit(0) + if not fileprefix or not prefix or not module: + print 'usage: mkskel.py -f fileprefix -p prefix -m module' + sys.exit(1) + write_skels(fileprefix, prefix, module) diff --git a/codegen/override.py b/codegen/override.py new file mode 100644 index 0000000..bba5e42 --- /dev/null +++ b/codegen/override.py @@ -0,0 +1,285 @@ +# -*- Mode: Python; py-indent-offset: 4 -*- + +# this file contains code for loading up an override file. The override file +# provides implementations of functions where the code generator could not +# do its job correctly. + +import fnmatch +import os +import re +import string +import sys + +def class2cname(klass, method): + c_name = '' + for c in klass: + if c.isupper(): + c_name += '_' + c.lower() + else: + c_name += c + return c_name[1:] + '_' + method + +# import python_type as c_name [for arg_type] +# Last ('for') clause is optional. If present, the type will be +# imported only if given 'arg_type' is registered. +import_pat = re.compile(r'\s*import\s+(\S+)\.([^\s.]+)\s+as\s+(\S+)(\s+for\s+(\S+))?') + +class Overrides: + def __init__(self, filename=None): + self.modulename = None + self.ignores = {} + self.glob_ignores = [] + self.type_ignores = {} + self.overrides = {} + self.overridden = {} + self.kwargs = {} + self.noargs = {} + self.onearg = {} + self.staticmethod = {} + self.classmethod = {} + self.startlines = {} + self.override_attrs = {} + self.override_slots = {} + self.headers = '' + self.body = '' + self.init = '' + self.imports = [] + self.defines = {} + self.functions = {} + self.newstyle_constructors = {} + self.dynamicnamespace = False + if filename: + self.handle_file(filename) + + def handle_file(self, filename): + oldpath = os.getcwd() + + fp = open(filename, 'r') + dirname = os.path.dirname(os.path.abspath(filename)) + + if dirname != oldpath: + os.chdir(dirname) + + # read all the components of the file ... + bufs = [] + startline = 1 + lines = [] + line = fp.readline() + linenum = 1 + while line: + if line == '%%\n' or line == '%%': + if lines: + bufs.append((string.join(lines, ''), startline)) + startline = linenum + 1 + lines = [] + else: + lines.append(line) + line = fp.readline() + linenum = linenum + 1 + if lines: + bufs.append((string.join(lines, ''), startline)) + if not bufs: return + + for buf, startline in bufs: + self.__parse_override(buf, startline, filename) + + os.chdir(oldpath) + + def __parse_override(self, buffer, startline, filename): + pos = string.find(buffer, '\n') + if pos >= 0: + line = buffer[:pos] + rest = buffer[pos+1:] + else: + line = buffer ; rest = '' + words = string.split(line) + command = words[0] + if (command == 'ignore' or + command == 'ignore-' + sys.platform): + "ignore/ignore-platform [functions..]" + for func in words[1:]: + self.ignores[func] = 1 + for func in string.split(rest): + self.ignores[func] = 1 + elif (command == 'ignore-glob' or + command == 'ignore-glob-' + sys.platform): + "ignore-glob/ignore-glob-platform [globs..]" + for func in words[1:]: + self.glob_ignores.append(func) + for func in string.split(rest): + self.glob_ignores.append(func) + elif (command == 'ignore-type' or + command == 'ignore-type-' + sys.platform): + "ignore-type/ignore-type-platform [typenames..]" + for typename in words[1:]: + self.type_ignores[typename] = 1 + for typename in string.split(rest): + self.type_ignores[typename] = 1 + elif command == 'override': + "override function/method [kwargs|noargs|onearg] [staticmethod|classmethod]" + func = words[1] + if 'kwargs' in words[1:]: + self.kwargs[func] = 1 + elif 'noargs' in words[1:]: + self.noargs[func] = 1 + elif 'onearg' in words[1:]: + self.onearg[func] = True + + if 'staticmethod' in words[1:]: + self.staticmethod[func] = True + elif 'classmethod' in words[1:]: + self.classmethod[func] = True + if func in self.overrides: + raise RuntimeError("Function %s is being overridden more than once" % (func,)) + self.overrides[func] = rest + self.startlines[func] = (startline + 1, filename) + elif command == 'override-attr': + "override-slot Class.attr" + attr = words[1] + self.override_attrs[attr] = rest + self.startlines[attr] = (startline + 1, filename) + elif command == 'override-slot': + "override-slot Class.slot" + slot = words[1] + self.override_slots[slot] = rest + self.startlines[slot] = (startline + 1, filename) + elif command == 'headers': + "headers" + self.headers = '%s\n#line %d "%s"\n%s' % \ + (self.headers, startline + 1, filename, rest) + elif command == 'body': + "body" + self.body = '%s\n#line %d "%s"\n%s' % \ + (self.body, startline + 1, filename, rest) + elif command == 'init': + "init" + self.init = '%s\n#line %d "%s"\n%s' % \ + (self.init, startline + 1, filename, rest) + elif command == 'modulename': + "modulename name" + self.modulename = words[1] + elif command == 'include': + "include filename" + for filename in words[1:]: + self.handle_file(filename) + for filename in string.split(rest): + self.handle_file(filename) + elif command == 'import': + "import module1 [\n module2, \n module3 ...]" + for line in string.split(buffer, '\n'): + match = import_pat.match(line) + if match: + module, pyname, cname, conditional, importing_for = match.groups() + self.imports.append((module, pyname, cname, importing_for or None)) + elif command == 'define': + "define funcname [kwargs|noargs|onearg] [classmethod|staticmethod]" + "define Class.method [kwargs|noargs|onearg] [classmethod|staticmethod]" + func = words[1] + klass = None + if func.find('.') != -1: + klass, func = func.split('.', 1) + + if not self.defines.has_key(klass): + self.defines[klass] = {} + self.defines[klass][func] = rest + else: + self.functions[func] = rest + + if 'kwargs' in words[1:]: + self.kwargs[func] = 1 + elif 'noargs' in words[1:]: + self.noargs[func] = 1 + elif 'onearg' in words[1:]: + self.onearg[func] = 1 + + if 'staticmethod' in words[1:]: + self.staticmethod[func] = True + elif 'classmethod' in words[1:]: + self.classmethod[func] = True + + self.startlines[func] = (startline + 1, filename) + + elif command == 'new-constructor': + "new-constructor GType" + gtype, = words[1:] + self.newstyle_constructors[gtype] = True + elif command == 'options': + for option in words[1:]: + if option == 'dynamicnamespace': + self.dynamicnamespace = True + + def is_ignored(self, name): + if self.ignores.has_key(name): + return 1 + for glob in self.glob_ignores: + if fnmatch.fnmatchcase(name, glob): + return 1 + return 0 + + def is_type_ignored(self, name): + return name in self.type_ignores + + def is_overriden(self, name): + return self.overrides.has_key(name) + + def is_already_included(self, name): + return self.overridden.has_key(name) + + def override(self, name): + self.overridden[name] = 1 + return self.overrides[name] + + def define(self, klass, name): + self.overridden[class2cname(klass, name)] = 1 + return self.defines[klass][name] + + def function(self, name): + return self.functions[name] + + def getstartline(self, name): + return self.startlines[name] + + def wants_kwargs(self, name): + return self.kwargs.has_key(name) + + def wants_noargs(self, name): + return self.noargs.has_key(name) + + def wants_onearg(self, name): + return self.onearg.has_key(name) + + def is_staticmethod(self, name): + return self.staticmethod.has_key(name) + + def is_classmethod(self, name): + return self.classmethod.has_key(name) + + def attr_is_overriden(self, attr): + return self.override_attrs.has_key(attr) + + def attr_override(self, attr): + return self.override_attrs[attr] + + def slot_is_overriden(self, slot): + return self.override_slots.has_key(slot) + + def slot_override(self, slot): + return self.override_slots[slot] + + def get_headers(self): + return self.headers + + def get_body(self): + return self.body + + def get_init(self): + return self.init + + def get_imports(self): + return self.imports + + def get_defines_for(self, klass): + return self.defines.get(klass, {}) + + def get_functions(self): + return self.functions diff --git a/codegen/pygobject-codegen-2.0.in b/codegen/pygobject-codegen-2.0.in new file mode 100644 index 0000000..c5c912e --- /dev/null +++ b/codegen/pygobject-codegen-2.0.in @@ -0,0 +1,11 @@ +#!/bin/sh + +prefix=@prefix@ +datarootdir=@datarootdir@ +datadir=@datadir@ +codegendir=${datadir}/pygobject/2.0/codegen + +PYTHONPATH=$codegendir +export PYTHONPATH + +exec @PYTHON@ $codegendir/codegen.py "$@" diff --git a/codegen/reversewrapper.py b/codegen/reversewrapper.py new file mode 100644 index 0000000..b96e12e --- /dev/null +++ b/codegen/reversewrapper.py @@ -0,0 +1,912 @@ +### -*- python -*- +### Code to generate "Reverse Wrappers", i.e. C->Python wrappers +### (C) 2004 Gustavo Carneiro <gjc@gnome.org> +import argtypes +import os + +DEBUG_MODE = ('PYGTK_CODEGEN_DEBUG' in os.environ) + +def join_ctype_name(ctype, name): + '''Joins a C type and a variable name into a single string''' + if ctype[-1] != '*': + return " ".join((ctype, name)) + else: + return "".join((ctype, name)) + + +class CodeSink(object): + def __init__(self): + self.indent_level = 0 # current indent level + self.indent_stack = [] # previous indent levels + + def _format_code(self, code): + assert isinstance(code, str) + l = [] + for line in code.split('\n'): + l.append(' '*self.indent_level + line) + if l[-1]: + l.append('') + return '\n'.join(l) + + def writeln(self, line=''): + raise NotImplementedError + + def indent(self, level=4): + '''Add a certain ammount of indentation to all lines written + from now on and until unindent() is called''' + self.indent_stack.append(self.indent_level) + self.indent_level += level + + def unindent(self): + '''Revert indentation level to the value before last indent() call''' + self.indent_level = self.indent_stack.pop() + + +class FileCodeSink(CodeSink): + def __init__(self, fp): + CodeSink.__init__(self) + assert isinstance(fp, file) + self.fp = fp + + def writeln(self, line=''): + self.fp.write(self._format_code(line)) + +class MemoryCodeSink(CodeSink): + def __init__(self): + CodeSink.__init__(self) + self.lines = [] + + def writeln(self, line=''): + self.lines.append(self._format_code(line)) + + def flush_to(self, sink): + assert isinstance(sink, CodeSink) + for line in self.lines: + sink.writeln(line.rstrip()) + self.lines = [] + + def flush(self): + l = [] + for line in self.lines: + l.append(self._format_code(line)) + self.lines = [] + return "".join(l) + +class ReverseWrapper(object): + '''Object that generates a C->Python wrapper''' + def __init__(self, cname, is_static=True): + assert isinstance(cname, str) + + self.cname = cname + ## function object we will call, or object whose method we will call + self.called_pyobj = None + ## name of method of self.called_pyobj we will call + self.method_name = None + self.is_static = is_static + + self.parameters = [] + self.declarations = MemoryCodeSink() + self.post_return_code = MemoryCodeSink() + self.body = MemoryCodeSink() + self.check_exception_code = MemoryCodeSink() + self.cleanup_actions = [] + self.pyargv_items = [] + self.pyargv_optional_items = [] + self.pyret_parse_items = [] # list of (format_spec, parameter) + self.code_sinks_stack = [self.body] + + def set_call_target(self, called_pyobj, method_name=None): + assert called_pyobj is not None + assert self.called_pyobj is None + self.called_pyobj = called_pyobj + self.method_name = method_name + + def set_return_type(self, return_type): + assert isinstance(return_type, ReturnType) + self.return_type = return_type + + def add_parameter(self, param): + assert isinstance(param, Parameter) + self.parameters.append(param) + + def add_declaration(self, decl_code): + self.declarations.writeln(decl_code) + + def add_pyargv_item(self, variable, optional=False): + if optional: + self.pyargv_optional_items.append(variable) + else: + self.pyargv_items.append(variable) + + def add_pyret_parse_item(self, format_specifier, parameter, prepend=False): + if prepend: + self.pyret_parse_items.insert(0, (format_specifier, parameter)) + else: + self.pyret_parse_items.append((format_specifier, parameter)) + + + def push_code_sink(self, code_sink): + self.code_sinks_stack.insert(0, code_sink) + + def pop_code_sink(self): + return self.code_sinks_stack.pop(0) + + + def write_code(self, code, + cleanup=None, + failure_expression=None, + failure_cleanup=None, + failure_exception=None, + code_sink=None): + '''Add a chunk of code with cleanup and error handling + + This method is to be used by TypeHandlers when generating code + + Keywork arguments: + code -- code to add + cleanup -- code to cleanup any dynamic resources created by @code + (except in case of failure) (default None) + failure_expression -- C boolean expression to indicate + if anything failed (default None) + failure_cleanup -- code to cleanup any dynamic resources + created by @code in case of failure (default None) + failure_exception -- code to raise an exception in case of + failure (which will be immediately + printed and cleared), (default None) + code_sink -- "code sink" to use; by default, + ReverseWrapper.body is used, which writes the + main body of the wrapper, before calling the + python method. Alternatively, + ReverseWrapper.after_pyret_parse can be used, to + write code after the PyArg_ParseTuple that + parses the python method return value. + ''' + if code_sink is None: + code_sink = self.code_sinks_stack[0] + if code is not None: + code_sink.writeln(code) + if failure_expression is not None: + code_sink.writeln("if (%s) {" % (failure_expression,)) + code_sink.indent() + if failure_exception is None: + code_sink.writeln("if (PyErr_Occurred())") + code_sink.indent() + code_sink.writeln("PyErr_Print();") + code_sink.unindent() + else: + code_sink.writeln(failure_exception) + code_sink.writeln("PyErr_Print();") + if failure_cleanup is not None: + code_sink.writeln(failure_cleanup) + for cleanup_action in self.cleanup_actions: + code_sink.writeln(cleanup_action) + + self.push_code_sink(code_sink) + try: + self.return_type.write_error_return() + finally: + self.pop_code_sink() + + code_sink.unindent() + code_sink.writeln("}") + if cleanup is not None: + self.cleanup_actions.insert(0, cleanup) + + def generate(self, sink): + '''Generate the code into a CodeSink object''' + assert isinstance(sink, CodeSink) + + if DEBUG_MODE: + self.declarations.writeln("/* begin declarations */") + self.body.writeln("/* begin main body */") + self.post_return_code.writeln("/* begin post-return code */") + + self.add_declaration("PyGILState_STATE __py_state;") + self.write_code(code="__py_state = pyg_gil_state_ensure();", + cleanup="pyg_gil_state_release(__py_state);") + + for param in self.parameters: + param.convert_c2py() + + assert self.called_pyobj is not None,\ + "Parameters failed to provide a target function or method." + + if self.is_static: + sink.writeln('static %s' % self.return_type.get_c_type()) + else: + sink.writeln(self.return_type.get_c_type()) + c_proto_params = map(Parameter.format_for_c_proto, self.parameters) + sink.writeln("%s(%s)\n{" % (self.cname, ", ".join(c_proto_params))) + + self.return_type.write_decl() + self.add_declaration("PyObject *py_retval;") + + ## Handle number of arguments + if self.pyargv_items: + self.add_declaration("PyObject *py_args;") + py_args = "py_args" + if self.pyargv_optional_items: + self.add_declaration("int argc = %i;" % len(self.pyargv_items)) + argc = "argc" + for arg in self.pyargv_optional_items: + self.body.writeln("if (%s)" % arg) + self.body.indent() + self.body.writeln("++argc;") + self.body.unindent() + else: + argc = str(len(self.pyargv_items)) + else: + if self.pyargv_optional_items: + self.add_declaration("PyObject *py_args;") + py_args = "py_args" + self.add_declaration("int argc = 0;") + argc = "argc" + for arg in self.pyargv_optional_items: + self.body.writeln("if (%s)" % arg) + self.body.indent() + self.body.writeln("++argc;") + self.body.unindent() + else: + py_args = "NULL" + argc = None + + self.body.writeln() + + if py_args != "NULL": + self.write_code("py_args = PyTuple_New(%s);" % argc, + cleanup="Py_DECREF(py_args);") + pos = 0 + for arg in self.pyargv_items: + try: # try to remove the Py_DECREF cleanup action, if we can + self.cleanup_actions.remove("Py_DECREF(%s);" % arg) + except ValueError: # otherwise we have to Py_INCREF.. + self.body.writeln("Py_INCREF(%s);" % arg) + self.body.writeln("PyTuple_SET_ITEM(%s, %i, %s);" % (py_args, pos, arg)) + pos += 1 + for arg in self.pyargv_optional_items: + self.body.writeln("if (%s) {" % arg) + self.body.indent() + try: # try to remove the Py_DECREF cleanup action, if we can + self.cleanup_actions.remove("Py_XDECREF(%s);" % arg) + except ValueError: # otherwise we have to Py_INCREF.. + self.body.writeln("Py_INCREF(%s);" % arg) + self.body.writeln("PyTuple_SET_ITEM(%s, %i, %s);" % (py_args, pos, arg)) + self.body.unindent() + self.body.writeln("}") + pos += 1 + + self.body.writeln() + + ## Call the python method + if self.method_name is None: + self.write_code("py_retval = PyObject_Call(%s, %s);" + % (self.called_pyobj, py_args), + cleanup="Py_XDECREF(py_retval);") + self.check_exception_code.flush_to(self.body) + self.write_code(None, failure_expression="!py_retval") + + else: + self.add_declaration("PyObject *py_method;") + self.write_code("py_method = PyObject_GetAttrString(%s, \"%s\");" + % (self.called_pyobj, self.method_name), + cleanup="Py_DECREF(py_method);", + failure_expression="!py_method") + self.write_code("py_retval = PyObject_CallObject(py_method, %s);" + % (py_args,), + cleanup="Py_XDECREF(py_retval);") + self.check_exception_code.flush_to(self.body) + self.write_code(None, failure_expression="!py_retval") + + ## -- Handle the return value -- + + ## we need to check if the return_type object is prepared to cooperate with multiple return values + len_before = len(self.pyret_parse_items) + self.return_type.write_conversion() + len_after = len(self.pyret_parse_items) + assert (self.return_type.get_c_type() == 'void' + or not (len_before == len_after and len_after > 0)),\ + ("Bug in reverse wrappers: return type handler %s" + " is not prepared to cooperate multiple return values") % (type(self.return_type),) + + sink.indent() + + if self.pyret_parse_items == [("", "")]: + ## special case when there are no return parameters + self.write_code( + code=None, + failure_expression='py_retval != Py_None', + failure_exception=('PyErr_SetString(PyExc_TypeError, ' + '"virtual method should return None");')) + else: + if len(self.pyret_parse_items) == 1: + ## if retval is one item only, pack it in a tuple so we + ## can use PyArg_ParseTuple as usual.. + self.write_code('py_retval = Py_BuildValue("(N)", py_retval);') + if len(self.pyret_parse_items) > 0: + ## Parse return values using PyArg_ParseTuple + params = ["py_retval", + '"%s"' % "".join([format for format, param in self.pyret_parse_items])] + params.extend([param for format, param in self.pyret_parse_items if param]) + self.write_code(code=None, failure_expression=( + '!PyArg_ParseTuple(%s)' % (', '.join(params),))) + + if DEBUG_MODE: + self.declarations.writeln("/* end declarations */") + self.declarations.flush_to(sink) + sink.writeln() + if DEBUG_MODE: + self.body.writeln("/* end main body */") + self.body.flush_to(sink) + sink.writeln() + if DEBUG_MODE: + self.post_return_code.writeln("/* end post-return code */") + self.post_return_code.flush_to(sink) + sink.writeln() + + for cleanup_action in self.cleanup_actions: + sink.writeln(cleanup_action) + if self.return_type.get_c_type() != 'void': + sink.writeln() + sink.writeln("return retval;") + sink.unindent() + sink.writeln("}") + +class TypeHandler(object): + def __init__(self, wrapper, **props): + assert isinstance(wrapper, ReverseWrapper) + self.wrapper = wrapper + self.props = props + +class ReturnType(TypeHandler): + + supports_optional = False + + def get_c_type(self): + raise NotImplementedError + + def write_decl(self): + raise NotImplementedError + + def write_error_return(self): + '''Write "return <value>" code in case of error''' + raise NotImplementedError + + def write_conversion(self): + '''Writes code to convert Python return value in 'py_retval' + into C 'retval'. Returns a string with C boolean expression + that determines if anything went wrong. ''' + raise NotImplementedError + +class Parameter(TypeHandler): + + def __init__(self, wrapper, name, **props): + TypeHandler.__init__(self, wrapper, **props) + self.name = name + + def get_c_type(self): + raise NotImplementedError + + def convert_c2py(self): + '''Write some code before calling the Python method.''' + pass + + def format_for_c_proto(self): + return join_ctype_name(self.get_c_type(), self.name) + + +###--- +class StringParam(Parameter): + + def get_c_type(self): + return self.props.get('c_type', 'char *').replace('const-', 'const ') + + def convert_c2py(self): + if self.props.get('optional', False): + self.wrapper.add_declaration("PyObject *py_%s = NULL;" % self.name) + self.wrapper.write_code(code=("if (%s)\n" + " py_%s = PyString_FromString(%s);\n" + % (self.name, self.name, self.name)), + cleanup=("Py_XDECREF(py_%s);" % self.name)) + self.wrapper.add_pyargv_item("py_%s" % self.name, optional=True) + elif self.props.get('nullok', False): + self.wrapper.add_declaration("PyObject *py_%s;" % self.name) + self.wrapper.write_code(code=("if (%s)\n" + " py_%s = PyString_FromString(%s);\n" + "else {\n" + " Py_INCREF(Py_None);\n" + " py_%s = Py_None;\n" + "}\n" + % (self.name, self.name, self.name, self.name)), + cleanup=("Py_DECREF(py_%s);" % self.name)) + self.wrapper.add_pyargv_item("py_%s" % self.name) + else: + self.wrapper.add_declaration("PyObject *py_%s = NULL;" % self.name) + self.wrapper.write_code(code=("if (%s)\n" + " py_%s = PyString_FromString(%s);\n" % + (self.name, self.name, self.name)), + cleanup=("Py_DECREF(py_%s);" % self.name), + failure_expression=("!py_%s" % self.name)) + self.wrapper.add_pyargv_item("py_%s" % self.name) + +for ctype in ('char*', 'gchar*', 'const-char*', 'char-const*', 'const-gchar*', + 'gchar-const*', 'string', 'static_string'): + argtypes.matcher.register_reverse(ctype, StringParam) +del ctype + +class StringReturn(ReturnType): + + def get_c_type(self): + return self.props.get('c_type', 'char *').replace('const-', 'const ') + #return "char *" + + def write_decl(self): + self.wrapper.add_declaration("%s retval;" % self.get_c_type()) + #self.wrapper.add_declaration("char *retval;") + + def write_error_return(self): + self.wrapper.write_code("return NULL;") + + def write_conversion(self): + self.wrapper.add_pyret_parse_item("s", "&retval", prepend=True) + self.wrapper.write_code("retval = g_strdup(retval);", code_sink=self.wrapper.post_return_code) + +for ctype in ('char*', 'gchar*', 'const-gchar*'): + argtypes.matcher.register_reverse_ret(ctype, StringReturn) +del ctype + + +class VoidReturn(ReturnType): + + def get_c_type(self): + return "void" + + def write_decl(self): + pass + + def write_error_return(self): + self.wrapper.write_code("return;") + + def write_conversion(self): + self.wrapper.add_pyret_parse_item("", "", prepend=True) + +argtypes.matcher.register_reverse_ret('void', VoidReturn) +argtypes.matcher.register_reverse_ret('none', VoidReturn) + +class GObjectParam(Parameter): + + def get_c_type(self): + return self.props.get('c_type', 'GObject *') + + def convert_c2py(self): + self.wrapper.add_declaration("PyObject *py_%s = NULL;" % self.name) + self.wrapper.write_code(code=("if (%s)\n" + " py_%s = pygobject_new((GObject *) %s);\n" + "else {\n" + " Py_INCREF(Py_None);\n" + " py_%s = Py_None;\n" + "}" + % (self.name, self.name, self.name, self.name)), + cleanup=("Py_DECREF(py_%s);" % self.name)) + self.wrapper.add_pyargv_item("py_%s" % self.name) + +argtypes.matcher.register_reverse('GObject*', GObjectParam) + +class GObjectReturn(ReturnType): + + supports_optional = True + + def get_c_type(self): + return self.props.get('c_type', 'GObject *') + + def write_decl(self): + if not self.props.get('optional'): + self.wrapper.add_declaration("%s retval;" % self.get_c_type()) + else: + self.wrapper.add_declaration("%s retval = NULL;" % self.get_c_type()) + + def write_error_return(self): + self.wrapper.write_code("return NULL;") + + def write_conversion(self): + if not self.props.get('optional'): + self.wrapper.write_code( + code=None, + failure_expression="!PyObject_TypeCheck(py_retval, &PyGObject_Type)", + failure_exception='PyErr_SetString(PyExc_TypeError, "retval should be a GObject");') + self.wrapper.write_code("retval = (%s) pygobject_get(py_retval);" + % self.get_c_type()) + self.wrapper.write_code("g_object_ref((GObject *) retval);") + else: + self.wrapper.write_code( + code=None, + failure_expression="py_retval != Py_None && !PyObject_TypeCheck(py_retval, &PyGObject_Type)", + failure_exception='PyErr_SetString(PyExc_TypeError, "retval should be None or a GObject");') + self.wrapper.write_code("if (py_retval != Py_None) {\n" + " retval = (%s) pygobject_get(py_retval);\n" + " g_object_ref((GObject *) retval);\n" + "}\n" + % self.get_c_type()) + +argtypes.matcher.register_reverse_ret('GObject*', GObjectReturn) + + + +class IntParam(Parameter): + + def get_c_type(self): + return self.props.get('c_type', 'int') + + def convert_c2py(self): + self.wrapper.add_declaration("PyObject *py_%s;" % self.name) + self.wrapper.write_code(code=("py_%s = PyInt_FromLong(%s);" % + (self.name, self.name)), + cleanup=("Py_DECREF(py_%s);" % self.name)) + self.wrapper.add_pyargv_item("py_%s" % self.name) + +class IntReturn(ReturnType): + def get_c_type(self): + return self.props.get('c_type', 'int') + def write_decl(self): + self.wrapper.add_declaration("%s retval;" % self.get_c_type()) + def write_error_return(self): + self.wrapper.write_code("return -G_MAXINT;") + def write_conversion(self): + self.wrapper.add_pyret_parse_item("i", "&retval", prepend=True) + +for argtype in ('int', 'gint', 'guint', 'short', 'gshort', 'gushort', 'long', + 'glong', 'gsize', 'gssize', 'guint8', 'gint8', 'guint16', + 'gint16', 'gint32', 'GTime'): + argtypes.matcher.register_reverse(argtype, IntParam) + argtypes.matcher.register_reverse_ret(argtype, IntReturn) +del argtype + +class IntPtrParam(Parameter): + def __init__(self, wrapper, name, **props): + if "direction" not in props: + raise argtypes.ArgTypeConfigurationError( + "cannot use int* parameter without direction") + if props["direction"] not in ("out", "inout"): + raise argtypes.ArgTypeConfigurationError( + "cannot use int* parameter with direction '%s'" + % (props["direction"],)) + Parameter.__init__(self, wrapper, name, **props) + def get_c_type(self): + return self.props.get('c_type', 'int*') + def convert_c2py(self): + if self.props["direction"] == "inout": + self.wrapper.add_declaration("PyObject *py_%s;" % self.name) + self.wrapper.write_code(code=("py_%s = PyInt_FromLong(*%s);" % + (self.name, self.name)), + cleanup=("Py_DECREF(py_%s);" % self.name)) + self.wrapper.add_pyargv_item("py_%s" % self.name) + self.wrapper.add_pyret_parse_item("i", self.name) +for argtype in ('int*', 'gint*'): + argtypes.matcher.register_reverse(argtype, IntPtrParam) +del argtype + + +class GEnumReturn(IntReturn): + def write_conversion(self): + self.wrapper.write_code( + code=None, + failure_expression=( + "pyg_enum_get_value(%s, py_retval, (gint *)&retval)" + % (self.props['typecode'],))) + +argtypes.matcher.register_reverse_ret("GEnum", GEnumReturn) + +class GEnumParam(IntParam): + def convert_c2py(self): + self.wrapper.add_declaration("PyObject *py_%s;" % self.name) + self.wrapper.write_code(code=("py_%s = pyg_enum_from_gtype(%s, %s);" % + (self.name, self.props['typecode'], self.name)), + cleanup=("Py_DECREF(py_%s);" % self.name), + failure_expression=("!py_%s" % self.name)) + self.wrapper.add_pyargv_item("py_%s" % self.name) + +argtypes.matcher.register_reverse("GEnum", GEnumParam) + +class GFlagsReturn(IntReturn): + def write_conversion(self): + self.wrapper.write_code( + code=None, + failure_expression=( + "pyg_flags_get_value(%s, py_retval, (gint *)&retval)" % + self.props['typecode'])) + +argtypes.matcher.register_reverse_ret("GFlags", GFlagsReturn) + +class GFlagsParam(IntParam): + def convert_c2py(self): + self.wrapper.add_declaration("PyObject *py_%s;" % self.name) + self.wrapper.write_code(code=( + "py_%s = pyg_flags_from_gtype(%s, %s);" % + (self.name, self.props['typecode'], self.name)), + cleanup=("Py_DECREF(py_%s);" % self.name), + failure_expression=("!py_%s" % self.name)) + self.wrapper.add_pyargv_item("py_%s" % self.name) + +argtypes.matcher.register_reverse("GFlags", GFlagsParam) + + +class GtkTreePathParam(IntParam): + def convert_c2py(self): + self.wrapper.add_declaration("PyObject *py_%s;" % self.name) + self.wrapper.write_code(code=( + "py_%s = pygtk_tree_path_to_pyobject(%s);" % + (self.name, self.name)), + cleanup=("Py_DECREF(py_%s);" % self.name), + failure_expression=("!py_%s" % self.name)) + self.wrapper.add_pyargv_item("py_%s" % self.name) + +argtypes.matcher.register_reverse("GtkTreePath*", GtkTreePathParam) + + +class GtkTreePathReturn(ReturnType): + def get_c_type(self): + return self.props.get('c_type', 'GtkTreePath *') + def write_decl(self): + self.wrapper.add_declaration("GtkTreePath * retval;") + def write_error_return(self): + self.wrapper.write_code("return NULL;") + def write_conversion(self): + self.wrapper.write_code( + "retval = pygtk_tree_path_from_pyobject(py_retval);\n", + failure_expression=('!retval'), + failure_exception=( + 'PyErr_SetString(PyExc_TypeError, "retval should be a GtkTreePath");')) + +argtypes.matcher.register_reverse_ret("GtkTreePath*", GtkTreePathReturn) + + +class BooleanReturn(ReturnType): + def get_c_type(self): + return "gboolean" + def write_decl(self): + self.wrapper.add_declaration("gboolean retval;") + self.wrapper.add_declaration("PyObject *py_main_retval;") + def write_error_return(self): + self.wrapper.write_code("return FALSE;") + def write_conversion(self): + self.wrapper.add_pyret_parse_item("O", "&py_main_retval", prepend=True) + self.wrapper.write_code( + "retval = PyObject_IsTrue(py_main_retval)? TRUE : FALSE;", + code_sink=self.wrapper.post_return_code) +argtypes.matcher.register_reverse_ret("gboolean", BooleanReturn) + +class BooleanParam(Parameter): + def get_c_type(self): + return "gboolean" + def convert_c2py(self): + self.wrapper.add_declaration("PyObject *py_%s;" % self.name) + self.wrapper.write_code("py_%s = %s? Py_True : Py_False;" + % (self.name, self.name)) + self.wrapper.add_pyargv_item("py_%s" % self.name) + +argtypes.matcher.register_reverse("gboolean", BooleanParam) + + +class DoubleParam(Parameter): + def get_c_type(self): + return self.props.get('c_type', 'gdouble') + def convert_c2py(self): + self.wrapper.add_declaration("PyObject *py_%s;" % self.name) + self.wrapper.write_code(code=("py_%s = PyFloat_FromDouble(%s);" % + (self.name, self.name)), + cleanup=("Py_DECREF(py_%s);" % self.name)) + self.wrapper.add_pyargv_item("py_%s" % self.name) + +class DoublePtrParam(Parameter): + def __init__(self, wrapper, name, **props): + if "direction" not in props: + raise argtypes.ArgTypeConfigurationError( + "cannot use double* parameter without direction") + if props["direction"] not in ("out", ): # inout not yet implemented + raise argtypes.ArgTypeConfigurationError( + "cannot use double* parameter with direction '%s'" + % (props["direction"],)) + Parameter.__init__(self, wrapper, name, **props) + def get_c_type(self): + return self.props.get('c_type', 'double*') + def convert_c2py(self): + self.wrapper.add_pyret_parse_item("d", self.name) +for argtype in ('double*', 'gdouble*'): + argtypes.matcher.register_reverse(argtype, DoublePtrParam) +del argtype + +class DoubleReturn(ReturnType): + def get_c_type(self): + return self.props.get('c_type', 'gdouble') + def write_decl(self): + self.wrapper.add_declaration("%s retval;" % self.get_c_type()) + def write_error_return(self): + self.wrapper.write_code("return -G_MAXFLOAT;") + def write_conversion(self): + self.wrapper.add_pyret_parse_item("d", "&retval", prepend=True) + +for argtype in ('float', 'double', 'gfloat', 'gdouble'): + argtypes.matcher.register_reverse(argtype, DoubleParam) + argtypes.matcher.register_reverse_ret(argtype, DoubleReturn) + + +class GBoxedParam(Parameter): + def get_c_type(self): + return self.props.get('c_type').replace('const-', 'const ') + def convert_c2py(self): + self.wrapper.add_declaration("PyObject *py_%s;" % self.name) + ctype = self.get_c_type() + if ctype.startswith('const '): + ctype_no_const = ctype[len('const '):] + self.wrapper.write_code( + code=('py_%s = pyg_boxed_new(%s, (%s) %s, TRUE, TRUE);' % + (self.name, self.props['typecode'], + ctype_no_const, self.name)), + cleanup=("Py_DECREF(py_%s);" % self.name)) + else: + self.wrapper.write_code( + code=('py_%s = pyg_boxed_new(%s, %s, FALSE, FALSE);' % + (self.name, self.props['typecode'], self.name)), + cleanup=("Py_DECREF(py_%s);" % self.name)) + self.wrapper.add_pyargv_item("py_%s" % self.name) + +argtypes.matcher.register_reverse("GBoxed", GBoxedParam) + + +class GBoxedReturn(ReturnType): + def get_c_type(self): + return self.props.get('c_type') + def write_decl(self): + self.wrapper.add_declaration("%s retval;" % self.get_c_type()) + def write_error_return(self): + self.wrapper.write_code("return retval;") + def write_conversion(self): + self.wrapper.write_code(code = None, + failure_expression=("!pyg_boxed_check(py_retval, %s)" % + (self.props['typecode'],)), + failure_exception=( + 'PyErr_SetString(PyExc_TypeError, "retval should be a %s");' + % (self.props['typename'],))) + self.wrapper.write_code('retval = pyg_boxed_get(py_retval, %s);' % + self.props['typename']) + +argtypes.matcher.register_reverse_ret("GBoxed", GBoxedReturn) + + +class GdkRegionPtrReturn(GBoxedReturn): + def write_error_return(self): + self.wrapper.write_code("return gdk_region_new();") + def write_conversion(self): + self.props['typecode'] = 'PYGDK_TYPE_REGION' + self.props['typename'] = 'GdkRegion' + super(GdkRegionPtrReturn, self).write_conversion() + +argtypes.matcher.register_reverse_ret("GdkRegion*", GdkRegionPtrReturn) + + +class PangoFontDescriptionReturn(GBoxedReturn): + def write_error_return(self): + self.wrapper.write_code("return pango_font_description_new();") + def write_conversion(self): + self.props['typecode'] = 'PANGO_TYPE_FONT_DESCRIPTION' + self.props['typename'] = 'PangoFontDescription' + super(PangoFontDescriptionReturn, self).write_conversion() + +argtypes.matcher.register_reverse_ret("PangoFontDescription*", + PangoFontDescriptionReturn) + + +class PangoFontMetricsReturn(GBoxedReturn): + def write_error_return(self): + self.wrapper.write_code("return pango_font_metrics_new();") + def write_conversion(self): + self.props['typecode'] = 'PANGO_TYPE_FONT_METRICS' + self.props['typename'] = 'PangoFontMetrics' + super(PangoFontMetricsReturn, self).write_conversion() + +argtypes.matcher.register_reverse_ret("PangoFontMetrics*", + PangoFontMetricsReturn) + + +class PangoLanguageReturn(GBoxedReturn): + def write_error_return(self): + self.wrapper.write_code("return pango_language_from_string(\"\");") + def write_conversion(self): + self.props['typecode'] = 'PANGO_TYPE_LANGUAGE' + self.props['typename'] = 'PangoLanguage' + super(PangoLanguageReturn, self).write_conversion() + +argtypes.matcher.register_reverse_ret("PangoLanguage*", PangoLanguageReturn) + + +class GdkRectanglePtrParam(Parameter): + def get_c_type(self): + return self.props.get('c_type').replace('const-', 'const ') + def convert_c2py(self): + self.wrapper.add_declaration("PyObject *py_%s;" % self.name) + self.wrapper.write_code( + code=('py_%s = pyg_boxed_new(GDK_TYPE_RECTANGLE, %s, TRUE, TRUE);' % + (self.name, self.name)), + cleanup=("Py_DECREF(py_%s);" % self.name)) + self.wrapper.add_pyargv_item("py_%s" % self.name) + +argtypes.matcher.register_reverse("GdkRectangle*", GdkRectanglePtrParam) +argtypes.matcher.register_reverse('GtkAllocation*', GdkRectanglePtrParam) + + +class GErrorParam(Parameter): + def get_c_type(self): + return self.props.get('c_type').replace('**', ' **') + def convert_c2py(self): + self.wrapper.write_code(code=None, + failure_expression=("pyg_gerror_exception_check(%s)" % self.name), + code_sink=self.wrapper.check_exception_code) + +argtypes.matcher.register_reverse('GError**', GErrorParam) + + +class PyGObjectMethodParam(Parameter): + def __init__(self, wrapper, name, method_name, **props): + Parameter.__init__(self, wrapper, name, **props) + self.method_name = method_name + + def get_c_type(self): + return self.props.get('c_type', 'GObject *') + + def convert_c2py(self): + self.wrapper.add_declaration("PyObject *py_%s;" % self.name) + self.wrapper.write_code(code=("py_%s = pygobject_new((GObject *) %s);" % + (self.name, self.name)), + cleanup=("Py_DECREF(py_%s);" % self.name), + failure_expression=("!py_%s" % self.name)) + self.wrapper.set_call_target("py_%s" % self.name, self.method_name) + + +class CallbackInUserDataParam(Parameter): + def __init__(self, wrapper, name, free_it, **props): + Parameter.__init__(self, wrapper, name, **props) + self.free_it = free_it + + def get_c_type(self): + return "gpointer" + + def convert_c2py(self): + self.wrapper.add_declaration("PyObject **_user_data;") + cleanup = self.free_it and ("g_free(%s);" % self.name) or None + self.wrapper.write_code(code=("_real_user_data = (PyObject **) %s;" + % self.name), + cleanup=cleanup) + + self.wrapper.add_declaration("PyObject *py_func;") + cleanup = self.free_it and "Py_DECREF(py_func);" or None + self.wrapper.write_code(code="py_func = _user_data[0];", + cleanup=cleanup) + self.wrapper.set_call_target("py_func") + + self.wrapper.add_declaration("PyObject *py_user_data;") + cleanup = self.free_it and "Py_XDECREF(py_user_data);" or None + self.wrapper.write_code(code="py_user_data = _user_data[1];", + cleanup=cleanup) + self.wrapper.add_pyargv_item("py_user_data", optional=True) + +def _test(): + import sys + + if 1: + wrapper = ReverseWrapper("this_is_the_c_function_name", is_static=True) + wrapper.set_return_type(StringReturn(wrapper)) + wrapper.add_parameter(PyGObjectMethodParam(wrapper, "self", method_name="do_xxx")) + wrapper.add_parameter(StringParam(wrapper, "param2", optional=True)) + wrapper.add_parameter(GObjectParam(wrapper, "param3")) + #wrapper.add_parameter(InoutIntParam(wrapper, "param4")) + wrapper.generate(FileCodeSink(sys.stderr)) + + if 0: + wrapper = ReverseWrapper("this_a_callback_wrapper") + wrapper.set_return_type(VoidReturn(wrapper)) + wrapper.add_parameter(StringParam(wrapper, "param1", optional=False)) + wrapper.add_parameter(GObjectParam(wrapper, "param2")) + wrapper.add_parameter(CallbackInUserDataParam(wrapper, "data", free_it=True)) + wrapper.generate(FileCodeSink(sys.stderr)) + +if __name__ == '__main__': + _test() diff --git a/codegen/scanvirtuals.py b/codegen/scanvirtuals.py new file mode 100755 index 0000000..c108737 --- /dev/null +++ b/codegen/scanvirtuals.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +import re +import sys + + +def main(): + rx = re.compile(r'^\s*([\w\s\*]+)\(\s*\*\s*(\w+)\s*\)\s*\(([^()]*)\);', + re.DOTALL|re.MULTILINE) + for f in sys.argv[1:]: + #print ";; From", f + buf = file(f).read() + for m in rx.findall(buf): + return_type = m[0].strip() + if 'typedef' in return_type: + continue + if return_type == 'void': + return_type = 'none' + return_type = return_type.replace(' ', '') + virtual_name = m[1] + if 'reserved' in virtual_name: + continue + params = [] + if not m[2]: + print >> sys.stderr, repr(m) + continue + for param in map(str.strip, m[2].split(',')): + if '*' in param: + tokens = param.split('*') + ptype = tokens[0].strip() + '*'*(len(tokens) - 1) + pname = tokens[-1].strip() + else: + if param == 'void': + continue + ptype, pname = map(str.strip, param.split()) + ptype = ptype.replace('const ', 'const-') + while '[]' in pname: + pname = pname.replace('[]', '') + ptype += '[]' + params.append((ptype, pname)) + if not params: + continue + objname = params[0][0].replace('*', '') + print '(define-virtual', virtual_name + print ' (of-object "%s")' % objname + print ' (return-type "%s")' % return_type + if len(params) > 1: + print ' (parameters' + for param in params[1:]: + print ' \'("%s" "%s")' % param + print ' )' + print ')' + +if __name__ == '__main__': + main() diff --git a/codegen/scmexpr.py b/codegen/scmexpr.py new file mode 100755 index 0000000..02f2e4b --- /dev/null +++ b/codegen/scmexpr.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python +# -*- Mode: Python; py-indent-offset: 4 -*- +from __future__ import generators + +import string +from cStringIO import StringIO + +class error(Exception): + def __init__(self, filename, lineno, msg): + Exception.__init__(self, msg) + self.filename = filename + self.lineno = lineno + self.msg = msg + def __str__(self): + return '%s:%d: error: %s' % (self.filename, self.lineno, self.msg) + +trans = [' '] * 256 +for i in range(256): + if chr(i) in string.letters + string.digits + '_': + trans[i] = chr(i) + else: + trans[i] = '_' +trans = string.join(trans, '') + +def parse(filename): + if isinstance(filename, str): + fp = open(filename, 'r') + else: # if not string, assume it is some kind of iterator + fp = filename + filename = getattr(fp, 'name', '<unknown>') + whitespace = ' \t\n\r\x0b\x0c' + nonsymbol = whitespace + '();\'"' + stack = [] + openlines = [] + lineno = 0 + for line in fp: + pos = 0 + lineno += 1 + while pos < len(line): + if line[pos] in whitespace: # ignore whitespace + pass + elif line[pos] == ';': # comment + break + elif line[pos:pos+2] == "'(": + pass # the open parenthesis will be handled next iteration + elif line[pos] == '(': + stack.append(()) + openlines.append(lineno) + elif line[pos] == ')': + if len(stack) == 0: + raise error(filename, lineno, 'close parenthesis found when none open') + closed = stack[-1] + del stack[-1] + del openlines[-1] + if stack: + stack[-1] += (closed,) + else: + yield closed + elif line[pos] == '"': # quoted string + if not stack: + raise error(filename, lineno, + 'string found outside of s-expression') + endpos = pos + 1 + chars = [] + while endpos < len(line): + if endpos+1 < len(line) and line[endpos] == '\\': + endpos += 1 + if line[endpos] == 'n': + chars.append('\n') + elif line[endpos] == 'r': + chars.append('\r') + elif line[endpos] == 't': + chars.append('\t') + else: + chars.append('\\') + chars.append(line[endpos]) + elif line[endpos] == '"': + break + else: + chars.append(line[endpos]) + endpos += 1 + if endpos >= len(line): + raise error(filename, lineno, "unclosed quoted string") + pos = endpos + stack[-1] += (''.join(chars),) + else: # symbol/number + if not stack: + raise error(filename, lineno, + 'identifier found outside of s-expression') + endpos = pos + while endpos < len(line) and line[endpos] not in nonsymbol: + endpos += 1 + symbol = line[pos:endpos] + pos = max(pos, endpos-1) + try: symbol = int(symbol) + except ValueError: + try: symbol = float(symbol) + except ValueError: pass + stack[-1] += (symbol,) + pos += 1 + if len(stack) != 0: + msg = '%d unclosed parentheses found at end of ' \ + 'file (opened on line(s) %s)' % (len(stack), + ', '.join(map(str, openlines))) + raise error(filename, lineno, msg) + +class Parser: + def __init__(self, filename): + """Argument is either a string, a parse tree, or file object""" + self.filename = filename + def startParsing(self, filename=None): + statements = parse(filename or self.filename) + for statement in statements: + self.handle(statement) + def handle(self, tup): + cmd = string.translate(tup[0], trans) + if hasattr(self, cmd): + getattr(self, cmd)(*tup[1:]) + else: + self.unknown(tup) + def unknown(self, tup): + pass + +_testString = """; a scheme file +(define-func gdk_font_load ; a comment at end of line + GdkFont + ((string name))) + +(define-boxed GdkEvent + gdk_event_copy + gdk_event_free + "sizeof(GdkEvent)") +""" + +if __name__ == '__main__': + import sys + if sys.argv[1:]: + fp = open(sys.argv[1]) + else: + fp = StringIO(_testString) + statements = parse(fp) + for s in statements: + print `s` |