7 from __future__ import absolute_import |
7 from __future__ import absolute_import |
8 |
8 |
9 import cffi |
9 import cffi |
10 import distutils.ccompiler |
10 import distutils.ccompiler |
11 import os |
11 import os |
|
12 import re |
12 import subprocess |
13 import subprocess |
13 import tempfile |
14 import tempfile |
14 |
15 |
15 |
16 |
16 HERE = os.path.abspath(os.path.dirname(__file__)) |
17 HERE = os.path.abspath(os.path.dirname(__file__)) |
17 |
18 |
18 SOURCES = ['zstd/%s' % p for p in ( |
19 SOURCES = ['zstd/%s' % p for p in ( |
19 'common/entropy_common.c', |
20 'common/entropy_common.c', |
20 'common/error_private.c', |
21 'common/error_private.c', |
21 'common/fse_decompress.c', |
22 'common/fse_decompress.c', |
|
23 'common/pool.c', |
|
24 'common/threading.c', |
22 'common/xxhash.c', |
25 'common/xxhash.c', |
23 'common/zstd_common.c', |
26 'common/zstd_common.c', |
24 'compress/fse_compress.c', |
27 'compress/fse_compress.c', |
25 'compress/huf_compress.c', |
28 'compress/huf_compress.c', |
26 'compress/zstd_compress.c', |
29 'compress/zstd_compress.c', |
27 'decompress/huf_decompress.c', |
30 'decompress/huf_decompress.c', |
28 'decompress/zstd_decompress.c', |
31 'decompress/zstd_decompress.c', |
|
32 'dictBuilder/cover.c', |
29 'dictBuilder/divsufsort.c', |
33 'dictBuilder/divsufsort.c', |
30 'dictBuilder/zdict.c', |
34 'dictBuilder/zdict.c', |
|
35 )] |
|
36 |
|
37 HEADERS = [os.path.join(HERE, 'zstd', *p) for p in ( |
|
38 ('zstd.h',), |
|
39 ('common', 'pool.h'), |
|
40 ('dictBuilder', 'zdict.h'), |
31 )] |
41 )] |
32 |
42 |
33 INCLUDE_DIRS = [os.path.join(HERE, d) for d in ( |
43 INCLUDE_DIRS = [os.path.join(HERE, d) for d in ( |
34 'zstd', |
44 'zstd', |
35 'zstd/common', |
45 'zstd/common', |
51 if compiler.compiler_type == 'unix': |
61 if compiler.compiler_type == 'unix': |
52 args = list(compiler.executables['compiler']) |
62 args = list(compiler.executables['compiler']) |
53 args.extend([ |
63 args.extend([ |
54 '-E', |
64 '-E', |
55 '-DZSTD_STATIC_LINKING_ONLY', |
65 '-DZSTD_STATIC_LINKING_ONLY', |
|
66 '-DZDICT_STATIC_LINKING_ONLY', |
56 ]) |
67 ]) |
57 elif compiler.compiler_type == 'msvc': |
68 elif compiler.compiler_type == 'msvc': |
58 args = [compiler.cc] |
69 args = [compiler.cc] |
59 args.extend([ |
70 args.extend([ |
60 '/EP', |
71 '/EP', |
61 '/DZSTD_STATIC_LINKING_ONLY', |
72 '/DZSTD_STATIC_LINKING_ONLY', |
|
73 '/DZDICT_STATIC_LINKING_ONLY', |
62 ]) |
74 ]) |
63 else: |
75 else: |
64 raise Exception('unsupported compiler type: %s' % compiler.compiler_type) |
76 raise Exception('unsupported compiler type: %s' % compiler.compiler_type) |
65 |
77 |
66 # zstd.h includes <stddef.h>, which is also included by cffi's boilerplate. |
78 def preprocess(path): |
67 # This can lead to duplicate declarations. So we strip this include from the |
79 # zstd.h includes <stddef.h>, which is also included by cffi's boilerplate. |
68 # preprocessor invocation. |
80 # This can lead to duplicate declarations. So we strip this include from the |
|
81 # preprocessor invocation. |
|
82 with open(path, 'rb') as fh: |
|
83 lines = [l for l in fh if not l.startswith(b'#include <stddef.h>')] |
69 |
84 |
70 with open(os.path.join(HERE, 'zstd', 'zstd.h'), 'rb') as fh: |
85 fd, input_file = tempfile.mkstemp(suffix='.h') |
71 lines = [l for l in fh if not l.startswith(b'#include <stddef.h>')] |
86 os.write(fd, b''.join(lines)) |
|
87 os.close(fd) |
72 |
88 |
73 fd, input_file = tempfile.mkstemp(suffix='.h') |
89 try: |
74 os.write(fd, b''.join(lines)) |
90 process = subprocess.Popen(args + [input_file], stdout=subprocess.PIPE) |
75 os.close(fd) |
91 output = process.communicate()[0] |
|
92 ret = process.poll() |
|
93 if ret: |
|
94 raise Exception('preprocessor exited with error') |
76 |
95 |
77 args.append(input_file) |
96 return output |
|
97 finally: |
|
98 os.unlink(input_file) |
78 |
99 |
79 try: |
|
80 process = subprocess.Popen(args, stdout=subprocess.PIPE) |
|
81 output = process.communicate()[0] |
|
82 ret = process.poll() |
|
83 if ret: |
|
84 raise Exception('preprocessor exited with error') |
|
85 finally: |
|
86 os.unlink(input_file) |
|
87 |
100 |
88 def normalize_output(): |
101 def normalize_output(output): |
89 lines = [] |
102 lines = [] |
90 for line in output.splitlines(): |
103 for line in output.splitlines(): |
91 # CFFI's parser doesn't like __attribute__ on UNIX compilers. |
104 # CFFI's parser doesn't like __attribute__ on UNIX compilers. |
92 if line.startswith(b'__attribute__ ((visibility ("default"))) '): |
105 if line.startswith(b'__attribute__ ((visibility ("default"))) '): |
93 line = line[len(b'__attribute__ ((visibility ("default"))) '):] |
106 line = line[len(b'__attribute__ ((visibility ("default"))) '):] |
94 |
107 |
|
108 if line.startswith(b'__attribute__((deprecated('): |
|
109 continue |
|
110 elif b'__declspec(deprecated(' in line: |
|
111 continue |
|
112 |
95 lines.append(line) |
113 lines.append(line) |
96 |
114 |
97 return b'\n'.join(lines) |
115 return b'\n'.join(lines) |
98 |
116 |
|
117 |
99 ffi = cffi.FFI() |
118 ffi = cffi.FFI() |
100 ffi.set_source('_zstd_cffi', ''' |
119 ffi.set_source('_zstd_cffi', ''' |
|
120 #include "mem.h" |
101 #define ZSTD_STATIC_LINKING_ONLY |
121 #define ZSTD_STATIC_LINKING_ONLY |
102 #include "zstd.h" |
122 #include "zstd.h" |
|
123 #define ZDICT_STATIC_LINKING_ONLY |
|
124 #include "pool.h" |
|
125 #include "zdict.h" |
103 ''', sources=SOURCES, include_dirs=INCLUDE_DIRS) |
126 ''', sources=SOURCES, include_dirs=INCLUDE_DIRS) |
104 |
127 |
105 ffi.cdef(normalize_output().decode('latin1')) |
128 DEFINE = re.compile(b'^\\#define ([a-zA-Z0-9_]+) ') |
|
129 |
|
130 sources = [] |
|
131 |
|
132 for header in HEADERS: |
|
133 preprocessed = preprocess(header) |
|
134 sources.append(normalize_output(preprocessed)) |
|
135 |
|
136 # Do another pass over source and find constants that were preprocessed |
|
137 # away. |
|
138 with open(header, 'rb') as fh: |
|
139 for line in fh: |
|
140 line = line.strip() |
|
141 m = DEFINE.match(line) |
|
142 if not m: |
|
143 continue |
|
144 |
|
145 # The parser doesn't like some constants with complex values. |
|
146 if m.group(1) in (b'ZSTD_LIB_VERSION', b'ZSTD_VERSION_STRING'): |
|
147 continue |
|
148 |
|
149 sources.append(m.group(0) + b' ...') |
|
150 |
|
151 ffi.cdef(u'\n'.join(s.decode('latin1') for s in sources)) |
106 |
152 |
107 if __name__ == '__main__': |
153 if __name__ == '__main__': |
108 ffi.compile() |
154 ffi.compile() |