0
# Source Generation and Compilation
1
2
Advanced features for generating and compiling C extensions at runtime. These capabilities enable complex integration scenarios and performance optimization through automatic code generation and compilation.
3
4
## Capabilities
5
6
### Source Code Assignment
7
8
Associates C source code with FFI declarations for compilation into Python extensions.
9
10
```python { .api }
11
def set_source(self, module_name, source, source_extension='.c', **kwds):
12
"""
13
Set C source code for compilation.
14
15
Parameters:
16
- module_name (str): Name of the generated module
17
- source (str): C source code
18
- source_extension (str): File extension ('.c' or '.cpp')
19
- **kwds: Compilation options (include_dirs, libraries, etc.)
20
21
Returns:
22
None (configures FFI for compilation)
23
"""
24
```
25
26
**Usage Examples:**
27
28
```python
29
ffi = FFI()
30
ffi.cdef("""
31
int add(int a, int b);
32
double calculate_pi(int iterations);
33
""")
34
35
# Set C source code
36
ffi.set_source("_math_ext", """
37
#include <math.h>
38
39
int add(int a, int b) {
40
return a + b;
41
}
42
43
double calculate_pi(int iterations) {
44
double pi = 0.0;
45
for (int i = 0; i < iterations; i++) {
46
pi += (i % 2 == 0 ? 1.0 : -1.0) / (2 * i + 1);
47
}
48
return pi * 4.0;
49
}
50
""",
51
libraries=['m'], # Link with math library
52
include_dirs=['/usr/local/include']
53
)
54
```
55
56
### Pkg-config Integration
57
58
Automatically configures compilation flags using pkg-config for system libraries.
59
60
```python { .api }
61
def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, source_extension='.c', **kwds):
62
"""
63
Set source with pkg-config library detection.
64
65
Parameters:
66
- module_name (str): Generated module name
67
- pkgconfig_libs (list): List of pkg-config package names
68
- source (str): C source code
69
- source_extension (str): File extension
70
- **kwds: Additional compilation options
71
72
Returns:
73
None
74
"""
75
```
76
77
**Usage Example:**
78
79
```python
80
ffi = FFI()
81
ffi.cdef("""
82
// OpenSSL functions
83
void SHA256_Init(void *ctx);
84
void SHA256_Update(void *ctx, const void *data, size_t len);
85
void SHA256_Final(unsigned char *md, void *ctx);
86
""")
87
88
# Use pkg-config to find OpenSSL
89
ffi.set_source_pkgconfig("_crypto_ext",
90
["openssl"],
91
"""
92
#include <openssl/sha.h>
93
94
// Wrapper functions if needed
95
""")
96
```
97
98
### Compilation
99
100
Compiles the configured source code into a loadable Python extension module.
101
102
```python { .api }
103
def compile(self, tmpdir='.', verbose=0, target=None, debug=None):
104
"""
105
Compile configured source into extension module.
106
107
Parameters:
108
- tmpdir (str): Directory for temporary and output files
109
- verbose (int): Verbosity level for compilation
110
- target (str): Target filename ('*' for default, '*.ext' for custom)
111
- debug (bool): Enable debug compilation
112
113
Returns:
114
str: Path to compiled extension module
115
"""
116
```
117
118
**Usage Example:**
119
120
```python
121
# After set_source(), compile the extension
122
extension_path = ffi.compile(tmpdir='./build', verbose=1)
123
124
# Load the compiled module
125
spec = importlib.util.spec_from_file_location("_math_ext", extension_path)
126
math_ext = importlib.util.module_from_spec(spec)
127
spec.loader.exec_module(math_ext)
128
129
# Use the compiled functions
130
result = math_ext.lib.add(5, 3) # 8
131
pi_approx = math_ext.lib.calculate_pi(100000)
132
```
133
134
### Code Generation
135
136
Generates C or Python source code files without compilation.
137
138
```python { .api }
139
def emit_c_code(self, filename):
140
"""
141
Generate C source code file.
142
143
Parameters:
144
- filename (str): Output C file path
145
146
Returns:
147
None
148
"""
149
150
def emit_python_code(self, filename):
151
"""
152
Generate Python wrapper code file.
153
154
Parameters:
155
- filename (str): Output Python file path
156
157
Returns:
158
None
159
"""
160
```
161
162
**Usage Examples:**
163
164
```python
165
# Generate C extension code
166
ffi.emit_c_code("generated_extension.c")
167
168
# Generate Python wrapper for dlopen() style
169
ffi_pure = FFI()
170
ffi_pure.cdef("int add(int a, int b);")
171
ffi_pure.set_source("_math_pure", None) # None for dlopen() style
172
ffi_pure.emit_python_code("math_wrapper.py")
173
```
174
175
### Distutils Integration
176
177
Creates distutils Extension objects for integration with setup.py build systems.
178
179
```python { .api }
180
def distutils_extension(self, tmpdir='build', verbose=True):
181
"""
182
Create distutils Extension object.
183
184
Parameters:
185
- tmpdir (str): Build directory
186
- verbose (bool): Print generation status
187
188
Returns:
189
distutils.extension.Extension: Extension object for setup.py
190
"""
191
```
192
193
**Usage Example:**
194
195
```python
196
# In build script or setup.py
197
ffi = FFI()
198
ffi.cdef("int multiply(int a, int b);")
199
ffi.set_source("_calc_ext", "int multiply(int a, int b) { return a * b; }")
200
201
# Get extension for setup.py
202
ext = ffi.distutils_extension()
203
204
# Use in setup.py
205
from setuptools import setup
206
setup(
207
name="my_package",
208
ext_modules=[ext],
209
zip_safe=False,
210
)
211
```
212
213
## Advanced Source Generation Patterns
214
215
### Multi-File Extensions
216
217
```python
218
def create_complex_extension():
219
ffi = FFI()
220
221
# Define complete interface
222
ffi.cdef("""
223
// Data structures
224
typedef struct {
225
double x, y, z;
226
} vector3_t;
227
228
// Core functions
229
vector3_t vector_add(vector3_t a, vector3_t b);
230
double vector_length(vector3_t v);
231
232
// Utility functions
233
void print_vector(vector3_t v);
234
""")
235
236
# Multi-part source code
237
includes = """
238
#include <math.h>
239
#include <stdio.h>
240
"""
241
242
data_structures = """
243
typedef struct {
244
double x, y, z;
245
} vector3_t;
246
"""
247
248
implementations = """
249
vector3_t vector_add(vector3_t a, vector3_t b) {
250
vector3_t result = {a.x + b.x, a.y + b.y, a.z + b.z};
251
return result;
252
}
253
254
double vector_length(vector3_t v) {
255
return sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
256
}
257
258
void print_vector(vector3_t v) {
259
printf("Vector(%.2f, %.2f, %.2f)\\n", v.x, v.y, v.z);
260
}
261
"""
262
263
full_source = includes + data_structures + implementations
264
265
ffi.set_source("_vector_ext", full_source,
266
libraries=['m'])
267
268
return ffi
269
270
# Build and use
271
vector_ffi = create_complex_extension()
272
lib_path = vector_ffi.compile()
273
```
274
275
### Conditional Compilation
276
277
```python
278
def create_platform_extension():
279
ffi = FFI()
280
281
# Platform-specific declarations
282
ffi.cdef("""
283
#ifdef _WIN32
284
void windows_specific_func();
285
#else
286
void unix_specific_func();
287
#endif
288
289
void cross_platform_func();
290
""")
291
292
# Platform-specific source
293
source = """
294
#ifdef _WIN32
295
#include <windows.h>
296
void windows_specific_func() {
297
// Windows implementation
298
OutputDebugStringA("Windows function called\\n");
299
}
300
#else
301
#include <unistd.h>
302
void unix_specific_func() {
303
// Unix implementation
304
write(STDOUT_FILENO, "Unix function called\\n", 22);
305
}
306
#endif
307
308
void cross_platform_func() {
309
// Cross-platform implementation
310
}
311
"""
312
313
# Platform-specific compilation options
314
import sys
315
if sys.platform == "win32":
316
libraries = ['kernel32']
317
define_macros = [('_WIN32', '1')]
318
else:
319
libraries = []
320
define_macros = []
321
322
ffi.set_source("_platform_ext", source,
323
libraries=libraries,
324
define_macros=define_macros)
325
326
return ffi
327
```
328
329
### Template-Based Generation
330
331
```python
332
def create_templated_extension(type_name, c_type):
333
"""Generate type-specific extensions"""
334
ffi = FFI()
335
336
# Template declarations
337
declarations = f"""
338
typedef struct {{
339
{c_type} data;
340
size_t size;
341
}} {type_name}_container_t;
342
343
{type_name}_container_t* {type_name}_create(size_t size);
344
void {type_name}_destroy({type_name}_container_t* container);
345
{c_type} {type_name}_get({type_name}_container_t* container, size_t index);
346
void {type_name}_set({type_name}_container_t* container, size_t index, {c_type} value);
347
"""
348
349
ffi.cdef(declarations)
350
351
# Template implementation
352
source = f"""
353
#include <stdlib.h>
354
355
typedef struct {{
356
{c_type}* data;
357
size_t size;
358
}} {type_name}_container_t;
359
360
{type_name}_container_t* {type_name}_create(size_t size) {{
361
{type_name}_container_t* container = malloc(sizeof({type_name}_container_t));
362
container->data = calloc(size, sizeof({c_type}));
363
container->size = size;
364
return container;
365
}}
366
367
void {type_name}_destroy({type_name}_container_t* container) {{
368
if (container) {{
369
free(container->data);
370
free(container);
371
}}
372
}}
373
374
{c_type} {type_name}_get({type_name}_container_t* container, size_t index) {{
375
if (index < container->size) {{
376
return container->data[index];
377
}}
378
return 0; // Error value
379
}}
380
381
void {type_name}_set({type_name}_container_t* container, size_t index, {c_type} value) {{
382
if (index < container->size) {{
383
container->data[index] = value;
384
}}
385
}}
386
"""
387
388
ffi.set_source(f"_{type_name}_container", source)
389
return ffi
390
391
# Generate specialized containers
392
int_container = create_templated_extension("int", "int")
393
float_container = create_templated_extension("float", "float")
394
```
395
396
## Build System Integration
397
398
### Setup.py Integration
399
400
```python
401
# build_extensions.py
402
from cffi import FFI
403
404
def build_math_extension():
405
ffi = FFI()
406
ffi.cdef("double fast_sin(double x);")
407
ffi.set_source("_fast_math", """
408
#include <math.h>
409
double fast_sin(double x) {
410
// Optimized sine implementation
411
return sin(x);
412
}
413
""", libraries=['m'])
414
415
return ffi.distutils_extension()
416
417
# setup.py
418
from setuptools import setup
419
from build_extensions import build_math_extension
420
421
setup(
422
name="my_math_package",
423
ext_modules=[build_math_extension()],
424
cffi_modules=["build_extensions.py:build_math_extension"],
425
setup_requires=["cffi>=1.0.0"],
426
install_requires=["cffi>=1.0.0"],
427
)
428
```
429
430
### CMake Integration
431
432
```python
433
def generate_cmake_compatible():
434
"""Generate extensions compatible with CMake builds"""
435
ffi = FFI()
436
ffi.cdef("void process_data(double* input, double* output, size_t count);")
437
438
# Generate source that can be built with CMake
439
ffi.emit_c_code("cffi_generated.c")
440
ffi.emit_c_code("cffi_generated.h") # If header generation supported
441
442
# Create CMakeLists.txt content
443
cmake_content = """
444
cmake_minimum_required(VERSION 3.10)
445
project(CFfiExtension)
446
447
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
448
449
add_library(cffi_extension SHARED
450
cffi_generated.c
451
)
452
453
target_link_libraries(cffi_extension Python3::Python)
454
"""
455
456
with open("CMakeLists.txt", "w") as f:
457
f.write(cmake_content)
458
```
459
460
### Setuptools Extension Utilities
461
462
CFFI provides utilities for integrating with setuptools build systems.
463
464
```python { .api }
465
# From cffi.setuptools_ext module
466
def add_cffi_module(dist, mod_spec):
467
"""
468
Add CFFI module to distribution.
469
470
Parameters:
471
- dist: Distribution object
472
- mod_spec (str): Module specification in format "path/build.py:ffi_variable"
473
474
Returns:
475
None (modifies distribution)
476
"""
477
```
478
479
**Usage Example:**
480
481
```python
482
# myproject_build.py
483
from cffi import FFI
484
485
ffi = FFI()
486
ffi.cdef("int calculate(int x, int y);")
487
ffi.set_source("_myproject", """
488
int calculate(int x, int y) {
489
return x * y + x + y;
490
}
491
""")
492
493
# setup.py
494
from setuptools import setup
495
496
setup(
497
name="myproject",
498
cffi_modules=["myproject_build.py:ffi"],
499
setup_requires=["cffi>=1.0.0"],
500
install_requires=["cffi>=1.0.0"],
501
)
502
```
503
504
## Performance Optimization
505
506
### Inline Functions
507
508
```python
509
def create_optimized_extension():
510
ffi = FFI()
511
512
ffi.cdef("""
513
double fast_math_operation(double x, double y);
514
""")
515
516
# Use inline functions for performance
517
source = """
518
#include <math.h>
519
520
static inline double inline_helper(double x) {
521
return x * x + 2 * x + 1;
522
}
523
524
double fast_math_operation(double x, double y) {
525
return inline_helper(x) + inline_helper(y);
526
}
527
"""
528
529
ffi.set_source("_optimized_math", source,
530
extra_compile_args=['-O3', '-finline-functions'])
531
532
return ffi
533
```
534
535
### SIMD Instructions
536
537
```python
538
def create_simd_extension():
539
ffi = FFI()
540
541
ffi.cdef("""
542
void vector_add_simd(float* a, float* b, float* result, size_t count);
543
""")
544
545
source = """
546
#ifdef __SSE__
547
#include <xmmintrin.h>
548
#endif
549
550
void vector_add_simd(float* a, float* b, float* result, size_t count) {
551
#ifdef __SSE__
552
size_t simd_count = count & ~3; // Process 4 elements at a time
553
for (size_t i = 0; i < simd_count; i += 4) {
554
__m128 va = _mm_load_ps(&a[i]);
555
__m128 vb = _mm_load_ps(&b[i]);
556
__m128 vr = _mm_add_ps(va, vb);
557
_mm_store_ps(&result[i], vr);
558
}
559
// Handle remaining elements
560
for (size_t i = simd_count; i < count; i++) {
561
result[i] = a[i] + b[i];
562
}
563
#else
564
for (size_t i = 0; i < count; i++) {
565
result[i] = a[i] + b[i];
566
}
567
#endif
568
}
569
"""
570
571
ffi.set_source("_simd_math", source,
572
extra_compile_args=['-msse', '-O3'])
573
574
return ffi
575
```