1
1
import itertools
2
2
import pathlib
3
- import shutil
4
3
import sys
5
4
import sysconfig
6
5
import tempfile
@@ -33,7 +32,8 @@ def compile_c_extension(
33
32
build_dir : Optional [str ] = None ,
34
33
verbose : bool = False ,
35
34
keep_asserts : bool = True ,
36
- disable_optimization : bool = True , # Significant test_peg_generator speedup.
35
+ disable_optimization : bool = False ,
36
+ library_dir : Optional [str ] = None ,
37
37
) -> str :
38
38
"""Compile the generated source for a parser generator into an extension module.
39
39
@@ -44,15 +44,21 @@ def compile_c_extension(
44
44
45
45
If *build_dir* is provided, that path will be used as the temporary build directory
46
46
of distutils (this is useful in case you want to use a temporary directory).
47
+
48
+ If *library_dir* is provided, that path will be used as the directory for a
49
+ static library of the common parser sources (this is useful in case you are
50
+ creating multiple extensions).
47
51
"""
48
52
import distutils .log
49
- from distutils .command .build_ext import build_ext # type: ignore
50
- from distutils .command .clean import clean # type: ignore
51
53
from distutils .core import Distribution , Extension
52
54
from distutils .tests .support import fixup_build_ext # type: ignore
53
55
56
+ from distutils .ccompiler import new_compiler
57
+ from distutils .dep_util import newer_group
58
+ from distutils .sysconfig import customize_compiler
59
+
54
60
if verbose :
55
- distutils .log .set_verbosity (distutils .log .DEBUG )
61
+ distutils .log .set_threshold (distutils .log .DEBUG )
56
62
57
63
source_file_path = pathlib .Path (generated_source_path )
58
64
extension_name = source_file_path .stem
@@ -71,46 +77,92 @@ def compile_c_extension(
71
77
extra_compile_args .append ("-O0" )
72
78
if sysconfig .get_config_var ("GNULD" ) == "yes" :
73
79
extra_link_args .append ("-fno-lto" )
74
- extension = [
75
- Extension (
76
- extension_name ,
77
- sources = [
78
- str (MOD_DIR .parent .parent .parent / "Python" / "Python-ast.c" ),
79
- str (MOD_DIR .parent .parent .parent / "Python" / "asdl.c" ),
80
- str (MOD_DIR .parent .parent .parent / "Parser" / "tokenizer.c" ),
81
- str (MOD_DIR .parent .parent .parent / "Parser" / "pegen.c" ),
82
- str (MOD_DIR .parent .parent .parent / "Parser" / "pegen_errors.c" ),
83
- str (MOD_DIR .parent .parent .parent / "Parser" / "action_helpers.c" ),
84
- str (MOD_DIR .parent .parent .parent / "Parser" / "string_parser.c" ),
85
- str (MOD_DIR .parent / "peg_extension" / "peg_extension.c" ),
86
- generated_source_path ,
87
- ],
88
- include_dirs = [
89
- str (MOD_DIR .parent .parent .parent / "Include" / "internal" ),
90
- str (MOD_DIR .parent .parent .parent / "Parser" ),
91
- ],
92
- extra_compile_args = extra_compile_args ,
93
- extra_link_args = extra_link_args ,
94
- )
80
+
81
+ common_sources = [
82
+ str (MOD_DIR .parent .parent .parent / "Python" / "Python-ast.c" ),
83
+ str (MOD_DIR .parent .parent .parent / "Python" / "asdl.c" ),
84
+ str (MOD_DIR .parent .parent .parent / "Parser" / "tokenizer.c" ),
85
+ str (MOD_DIR .parent .parent .parent / "Parser" / "pegen.c" ),
86
+ str (MOD_DIR .parent .parent .parent / "Parser" / "pegen_errors.c" ),
87
+ str (MOD_DIR .parent .parent .parent / "Parser" / "action_helpers.c" ),
88
+ str (MOD_DIR .parent .parent .parent / "Parser" / "string_parser.c" ),
89
+ str (MOD_DIR .parent / "peg_extension" / "peg_extension.c" ),
90
+ ]
91
+ include_dirs = [
92
+ str (MOD_DIR .parent .parent .parent / "Include" / "internal" ),
93
+ str (MOD_DIR .parent .parent .parent / "Parser" ),
95
94
]
96
- dist = Distribution ({"name" : extension_name , "ext_modules" : extension })
97
- cmd = build_ext (dist )
95
+ extension = Extension (
96
+ extension_name ,
97
+ sources = [generated_source_path ],
98
+ extra_compile_args = extra_compile_args ,
99
+ extra_link_args = extra_link_args ,
100
+ )
101
+ dist = Distribution ({"name" : extension_name , "ext_modules" : [extension ]})
102
+ cmd = dist .get_command_obj ("build_ext" )
98
103
fixup_build_ext (cmd )
99
- cmd .inplace = True
104
+ cmd .build_lib = str (source_file_path .parent )
105
+ cmd .include_dirs = include_dirs
100
106
if build_dir :
101
107
cmd .build_temp = build_dir
102
- cmd .build_lib = build_dir
103
108
cmd .ensure_finalized ()
104
- cmd .run ()
105
-
106
- extension_path = source_file_path .parent / cmd .get_ext_filename (extension_name )
107
- shutil .move (cmd .get_ext_fullpath (extension_name ), extension_path )
108
-
109
- cmd = clean (dist )
110
- cmd .finalize_options ()
111
- cmd .run ()
112
109
113
- return extension_path
110
+ compiler = new_compiler ()
111
+ customize_compiler (compiler )
112
+ compiler .set_include_dirs (cmd .include_dirs )
113
+ compiler .set_library_dirs (cmd .library_dirs )
114
+ # build static lib
115
+ if library_dir :
116
+ library_filename = compiler .library_filename (extension_name ,
117
+ output_dir = library_dir )
118
+ if newer_group (common_sources , library_filename , 'newer' ):
119
+ if sys .platform == 'win32' :
120
+ pdb = compiler .static_lib_format % (extension_name , '.pdb' )
121
+ compile_opts = [f"/Fd{ library_dir } \\ { pdb } " ]
122
+ compile_opts .extend (extra_compile_args )
123
+ else :
124
+ compile_opts = extra_compile_args
125
+ objects = compiler .compile (common_sources ,
126
+ output_dir = library_dir ,
127
+ debug = cmd .debug ,
128
+ extra_postargs = compile_opts )
129
+ compiler .create_static_lib (objects , extension_name ,
130
+ output_dir = library_dir ,
131
+ debug = cmd .debug )
132
+ if sys .platform == 'win32' :
133
+ compiler .add_library_dir (library_dir )
134
+ extension .libraries = [extension_name ]
135
+ elif sys .platform == 'darwin' :
136
+ compiler .set_link_objects ([
137
+ '-Wl,-force_load' , library_filename ,
138
+ ])
139
+ else :
140
+ compiler .set_link_objects ([
141
+ '-Wl,--whole-archive' , library_filename , '-Wl,--no-whole-archive' ,
142
+ ])
143
+ else :
144
+ extension .sources [0 :0 ] = common_sources
145
+
146
+ # Compile the source code to object files.
147
+ ext_path = cmd .get_ext_fullpath (extension_name )
148
+ if newer_group (extension .sources , ext_path , 'newer' ):
149
+ objects = compiler .compile (extension .sources ,
150
+ output_dir = cmd .build_temp ,
151
+ debug = cmd .debug ,
152
+ extra_postargs = extra_compile_args )
153
+ else :
154
+ objects = compiler .object_filenames (extension .sources ,
155
+ output_dir = cmd .build_temp )
156
+ # Now link the object files together into a "shared object"
157
+ compiler .link_shared_object (
158
+ objects , ext_path ,
159
+ libraries = cmd .get_libraries (extension ),
160
+ extra_postargs = extra_link_args ,
161
+ export_symbols = cmd .get_export_symbols (extension ),
162
+ debug = cmd .debug ,
163
+ build_temp = cmd .build_temp )
164
+
165
+ return pathlib .Path (ext_path )
114
166
115
167
116
168
def build_parser (
0 commit comments