Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Api mode cffi compile #61

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d341072
Add Font, FontMetrics etc, update ffi gen for new headers
georgeharker Jun 11, 2024
0383881
Ensure private defs for PangoAttribute and PangoColor flow thru
georgeharker Jun 11, 2024
da9d01a
Add Font, FontMetrics etc, update ffi gen for new headers
georgeharker Jun 11, 2024
f15b3c5
Ensure private defs for PangoAttribute and PangoColor flow thru
georgeharker Jun 11, 2024
c928f8e
Lint fixes
georgeharker Jun 13, 2024
7b1584c
Additional lint fixes
georgeharker Jun 13, 2024
15e87d2
Merge branch 'master' of github.com:leifgehrmann/pangocffi
georgeharker Jun 13, 2024
6f5d51c
Merge branch 'master' of github.com:georgeharker/pangocffi
georgeharker Jun 13, 2024
e291661
Git git merge issue in pangocffi/context.py
leifgehrmann Jun 15, 2024
42d6b2a
Fix missing merge fix
georgeharker Jun 16, 2024
68437dd
Merge branch 'master' of github.com:georgeharker/pangocffi
georgeharker Jun 16, 2024
6abb703
Merge branch 'master' of github.com:leifgehrmann/pangocffi
georgeharker Nov 13, 2024
aef52b1
api mode
georgeharker Nov 23, 2024
997ea59
remove script engine
georgeharker Nov 23, 2024
7deee56
fix up generate script to enable regeneration
georgeharker Nov 23, 2024
593409f
fix attr enum
georgeharker Nov 23, 2024
14c445d
remove enum definition
georgeharker Nov 23, 2024
2f4efdc
Fixes for debian
georgeharker Nov 25, 2024
35cf1fa
bump versions
georgeharker Nov 25, 2024
4bb9864
Fix editable builds
georgeharker Nov 25, 2024
edee503
Darwin fixes
georgeharker Nov 25, 2024
947b672
Fix non api build
georgeharker Nov 25, 2024
3129521
clean up API mode
georgeharker Dec 2, 2024
ee7a4b8
lint fixes
georgeharker Dec 2, 2024
1751247
lint fix
georgeharker Dec 2, 2024
9fb5c61
simplify api vs abi mode and auto pick
georgeharker Dec 9, 2024
79cea3e
use setuputils for exceptions
georgeharker Dec 9, 2024
71db472
fix setuptools import
georgeharker Dec 9, 2024
9df3353
fix setuputils imports
georgeharker Dec 9, 2024
92a0aad
support failures in pkgconfig
georgeharker Dec 9, 2024
29f0aed
compile in temp dirs, compiled ext nests inside module
georgeharker Dec 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ venv

# Mac OS
.DS_store

# Build temporaries
_*.o
_*.so
_*.c
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ clean: clean-generated-ffi clean-build clean-pyc clean-test ## remove all build,

clean-generated-ffi: ## remove FFI generated artifacts
rm -fr pangocffi/_generated/
rm -f _pangocffi*.so
rm -f _pangocffi*.c
rm -f _pangocffi*.o

clean-build: ## remove build artifacts
rm -fr build/
Expand Down
42 changes: 33 additions & 9 deletions pangocffi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,40 @@
import ctypes.util
import os
import warnings
from typing import List
import ctypes.util
from .ffi_build import ffi


# Attempt api mode, then precompiled abi mode, then import time abi
cffi_mode = "(unknown)"
try:
# Note in ABI mode lib is already available, no dlopen() needed
from ._pangocffi import ffi, lib as pango
gobject = pango
glib = pango
cffi_mode = "api"
except ImportError:
try:
# Note in ABI mode lib will be missing
from ._pangocffi import ffi
cffi_mode = "abi_precompiled"
except ImportError:
# Fall back to importing and parsing cffi defs
from .ffi_build import ffi_for_mode
ffi = ffi_for_mode("abi")
cffi_mode = "abi"


def _dlopen(dl_name: str, generated_ffi, names: List[str]):
"""
:param dl_name:
The name of the dynamic library. This is also used to determine the
environment variable name to lookup. For example, if dl_name is "glib",
this function will attempt to load "GLIB_LOCATION".
The name of the dynamic library. This is also used to determine
the environment variable name to lookup. For example, if dl_name
is "glib", this function will attempt to load "GLIB_LOCATION".
:param generated_ffi:
The FFI for pango/gobject/glib, generated by pangocffi.
:param names:
An array of library names commonly used across different platforms.
An array of library names commonly used across different
platforms.
:return:
A FFILibrary instance for the library.
"""
Expand Down Expand Up @@ -44,9 +64,13 @@ def _dlopen(dl_name: str, generated_ffi, names: List[str]):
)


pango = _dlopen('pango', ffi, ['pango', 'pango-1', 'pango-1.0', 'pango-1.0-0'])
gobject = _dlopen('gobject', ffi, ['gobject-2.0', 'gobject-2.0-0'])
glib = _dlopen('glib', ffi, ['glib-2.0', 'glib-2.0-0'])
# Fall back to non api mode
if cffi_mode != "api":
pango = _dlopen('pango', ffi, ['pango', 'pango-1', 'pango-1.0',
'pango-1.0-0'])
gobject = _dlopen('gobject', ffi, ['gobject-2.0', 'gobject-2.0-0'])
glib = _dlopen('glib', ffi, ['glib-2.0', 'glib-2.0-0'])


# Imports are normally always put at the top of the file.
# But the wrapper API requires that the pango library be loaded first.
Expand Down
9 changes: 5 additions & 4 deletions pangocffi/attribute.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from . import FontDescription, Underline, ffi, pango, PangoObject
from .enums import Gravity, GravityHint, Stretch, Style, Variant, Weight
from .enums import (AttrIndex, Gravity, GravityHint, Stretch, Style,
Variant, Weight)
from .rectangle import Rectangle


Expand All @@ -20,9 +21,9 @@ def _get_start_index(self):
return self._pointer.start_index

def _set_start_index(self, start_index: int):
assert start_index >= pango.PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING, \
assert start_index >= AttrIndex.PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING, \
"start_index is too low"
assert start_index < pango.PANGO_ATTR_INDEX_TO_TEXT_END, \
assert start_index < AttrIndex.PANGO_ATTR_INDEX_TO_TEXT_END, \
"start_index is too high"
start = ffi.cast("guint", start_index)
self._pointer.start_index = start
Expand All @@ -35,7 +36,7 @@ def _get_end_index(self):

def _set_end_index(self, end_index: int):
assert isinstance(end_index, int), "end_index isn't a int"
assert end_index <= pango.PANGO_ATTR_INDEX_TO_TEXT_END, \
assert end_index <= AttrIndex.PANGO_ATTR_INDEX_TO_TEXT_END, \
"end_index is too high"
end = ffi.cast("guint", end_index)
self._pointer.end_index = end
Expand Down
5 changes: 2 additions & 3 deletions pangocffi/c_definitions_glib.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
typedef ... GType;
typedef unsigned int guint8;
typedef unsigned int guint16;
typedef unsigned int guint32;
Expand All @@ -10,8 +9,8 @@ typedef char gchar;
typedef unsigned char guchar;
typedef guint32 gunichar;
typedef void* gpointer;
typedef ... gconstpointer;
typedef ... GObject;
typedef const void* gconstpointer;
typedef void* GObject;
typedef ... GObjectClass;
typedef ... GString;
typedef ... GDestroyNotify;
Expand Down
26 changes: 14 additions & 12 deletions pangocffi/c_definitions_pango.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
typedef enum {
PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING = 0,
PANGO_ATTR_INDEX_TO_TEXT_END = 4294967295
} PangoAttrIndex;
typedef ... hb_feature_t;
typedef ... hb_font_t;
typedef ... GBytes;
typedef ... GQuark;
typedef guint32 GQuark;
typedef ... PangoAttrClass;
typedef ... PangoAttrString;
typedef ... PangoAttrLanguage;
Expand Down Expand Up @@ -40,7 +36,6 @@ typedef ... PangoGlyphGeometry;
typedef ... PangoGlyphVisAttr;
typedef ... PangoGlyphInfo;
typedef ... PangoGlyphString;
typedef ... PangoAnalysis;
typedef ... PangoLayout;
typedef ... PangoLayoutClass;
typedef ... PangoLayoutLine;
Expand All @@ -66,11 +61,23 @@ typedef struct
int height;
} PangoRectangle;
typedef struct
{
PangoEngineShape *shape_engine;
PangoEngineLang *lang_engine;
PangoFont *font;
guint8 level;
guint8 gravity;
guint8 flags;
guint8 script;
PangoLanguage *language;
GSList *extra_attrs;
} PangoAnalysis;
typedef struct
{
int offset;
int length;
int num_chars;
void * analysis;
PangoAnalysis analysis;
} PangoItem;
typedef struct
{
Expand Down Expand Up @@ -661,11 +668,6 @@ PangoCoverage *pango_coverage_from_bytes (guchar *bytes,
GType pango_engine_get_type (void) ;
GType pango_engine_lang_get_type (void) ;
GType pango_engine_shape_get_type (void) ;
void script_engine_list (PangoEngineInfo **engines,
int *n_engines);
void script_engine_init (GTypeModule *module);
void script_engine_exit (void);
PangoEngine *script_engine_create (const char *id);
GType pango_font_description_get_type (void) ;
PangoFontDescription *pango_font_description_new (void);
PangoFontDescription *pango_font_description_copy (const PangoFontDescription *desc);
Expand Down
20 changes: 19 additions & 1 deletion pangocffi/enums.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from enum import Enum, IntEnum

from pangocffi import pango
from enum import Enum


class Style(Enum):
Expand Down Expand Up @@ -270,3 +271,20 @@ class TabAlign(Enum):
appears to the left of the tab stop position (until the available
space is filled), the rest to the right. Since: 1.50
"""


class AttrIndex(IntEnum):
"""
:class:`AttrIndex` is used for specifying relative indexing of
attributes.
"""

PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING = 0
"""
Index from the begining of the text.
"""

PANGO_ATTR_INDEX_TO_TEXT_END = 4294967295
"""
Index to the end of the text.
"""
106 changes: 90 additions & 16 deletions pangocffi/ffi_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,103 @@

"""

import platform
import shutil
import sys
import tempfile
from pathlib import Path
import importlib.util
from warnings import warn

from cffi import FFI
from cffi.error import PkgConfigError, VerificationError
from setuptools.errors import CCompilerError, ExecError, PlatformError


sys.path.append(str(Path(__file__).parent))

# Create an empty _generated folder if needed
(Path(__file__).parent / '_generated').mkdir(exist_ok=True)

# Because we can't directly load the instance builder (it would run
# ``__init__.py`` for any module import) we have to do this dubious import.
spec = importlib.util.spec_from_file_location(
'ffi_instance_builder',
str(Path(__file__).parent / 'ffi_instance_builder.py')
)
ffi_instance_builder = importlib.util.module_from_spec(spec)
spec.loader.exec_module(ffi_instance_builder)
def ffi_for_mode(mode):
# Read the C definitions
c_definitions_glib_file = open(
str(Path(__file__).parent / 'c_definitions_glib.txt'),
'r'
)
c_definitions_pango_file = open(
str(Path(__file__).parent / 'c_definitions_pango.txt'),
'r'
)
c_definitions_glib = c_definitions_glib_file.read()
c_definitions_pango = c_definitions_pango_file.read()

ffi = FFI()
# Mirror the GType setup in gobject/gtypes.h
if ffi.sizeof('void*') > ffi.sizeof('long'):
ffi.cdef('typedef unsigned int* GType;')
else:
ffi.cdef('typedef unsigned long GType;')
ffi.cdef(c_definitions_glib)
ffi.cdef(c_definitions_pango)
if mode == "api":
ffi.set_source_pkgconfig(
"pangocffi._pangocffi",
['pango', 'glib-2.0', 'pangoft2'] +
(['pangoxft'] if platform.system() == 'Linux' else []),
r"""
#include "glib.h"
#include "glib-object.h"
#include "pango/pango.h"
#include "pango/pango-fontmap.h"

#include <stdio.h>
#if PANGO_VERSION < G_ENCODE_VERSION(1, 54)
int pango_item_get_char_offset (
PangoItem* item) {
fprintf(stderr, "Unimplemented!!\n");
return -1;
}
#endif
#if PANGO_VERSION < G_ENCODE_VERSION(1, 52)
PangoFont*
pango_font_map_reload_font (
PangoFontMap* fontmap,
PangoFont* font,
double scale,
PangoContext* context,
const char* variations) {
fprintf(stderr, "Unimplemented!!\n");
return NULL;
}

#endif
""",
sources=[]
)
else:
ffi.set_source("pangocffi._pangocffi", None)
return ffi


def build_ffi():
"""
This will be called from setup() to return an FFI
which it will compile - work out here which type is
possible and return it.
"""
try:
ffi_api = ffi_for_mode("api")
file = ffi_api.compile(verbose=True, tmpdir=tempfile.gettempdir())
shutil.copy(file, "pangocffi")
return ffi_api
except (CCompilerError, ExecError, PlatformError,
PkgConfigError, VerificationError) as e:
warn("Falling back to precompiled python mode: {}".format(str(e)))

ffi_abi = ffi_for_mode("abi")
file = ffi_abi.compile(verbose=True, tmpdir=tempfile.gettempdir())
shutil.copy(file, "pangocffi")
return ffi_abi

# Generate the bindings
ffiBuilder = ffi_instance_builder.FFIInstanceBuilder(
source='pangocffi._generated.ffi'
)
ffi = ffiBuilder.generate()

if __name__ == '__main__':
ffi = build_ffi()
ffi.compile()
37 changes: 0 additions & 37 deletions pangocffi/ffi_instance_builder.py

This file was deleted.

3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools >= 64"]
build-backend = "setuptools.build_meta"
3 changes: 3 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ project_urls =
packages = find:
setup_requires =
setuptools
cffi >= 1.1.0
cairocffi >= 1.7.2
install_requires =
cffi >= 1.1.0
cairocffi >= 1.7.2
python_requires = >= 3.8

[options.package_data]
Expand Down
Loading
Loading