44# Setup script for PyPI; use CMakeFile.txt to build extension modules
55
66import contextlib
7- import glob
87import os
9- import re
108import shutil
119import subprocess
1210import sys
1311import tempfile
14- from distutils .command .install_headers import install_headers
1512
16- from setuptools import setup
13+ # PYBIND11_ALT_SDIST will build a different sdist, with the python-headers
14+ # files, and the sys.prefix files (CMake and headers).
1715
18- # For now, there are three parts to this package. Besides the "normal" module:
19- # PYBIND11_USE_HEADERS will include the python-headers files.
20- # PYBIND11_USE_SYSTEM will include the sys.prefix files (CMake and headers).
21- # The final version will likely only include the normal module or come in
22- # different versions.
23-
24- use_headers = os .environ .get ("PYBIND11_USE_HEADERS" , False )
25- use_system = os .environ .get ("PYBIND11_USE_SYSTEM" , False )
26-
27- setup_opts = dict ()
16+ alt_sdist = os .environ .get ("PYBIND11_ALT_SDIST" , False )
17+ setup_py = "tools/setup_alt.py" if alt_sdist else "tools/setup_main.py"
18+ pyproject_toml = "tools/pyproject.toml"
2819
2920# In a PEP 518 build, this will be in its own environment, so it will not
3021# create extra files in the source
3122
3223DIR = os .path .abspath (os .path .dirname (__file__ ))
3324
34- prexist_include = os .path .exists ("pybind11/include" )
35- prexist_share = os .path .exists ("pybind11/share" )
36-
3725
3826@contextlib .contextmanager
39- def monkey_patch_file (input_file ):
40- "Allow a file to be temporarily modified"
27+ def monkey_patch_file (input_file , replacement_file ):
28+ "Allow a file to be temporarily replaced"
29+ inp_file = os .path .abspath (os .path .join (DIR , input_file ))
30+ rep_file = os .path .abspath (os .path .join (DIR , replacement_file ))
4131
42- with open (os . path . join ( DIR , input_file ), "r " ) as f :
32+ with open (inp_file , "rb " ) as f :
4333 contents = f .read ()
34+ with open (rep_file , "rb" ) as f :
35+ replacement = f .read ()
4436 try :
45- yield contents
37+ with open (inp_file , "wb" ) as f :
38+ f .write (replacement )
39+ yield
4640 finally :
47- with open (os . path . join ( DIR , input_file ), "w " ) as f :
41+ with open (inp_file , "wb " ) as f :
4842 f .write (contents )
4943
5044
@@ -67,118 +61,20 @@ def remove_output(*sources):
6761 shutil .rmtree (src )
6862
6963
70- def check_compare (input_set , * patterns ):
71- "Just a quick way to make sure all files are present"
72- disk_files = set ()
73- for pattern in patterns :
74- disk_files |= set (glob .glob (pattern , recursive = True ))
75-
76- assert input_set == disk_files , "{} setup.py only, {} on disk only" .format (
77- input_set - disk_files , disk_files - input_set
78- )
79-
80-
81- class InstallHeadersNested (install_headers ):
82- def run (self ):
83- headers = self .distribution .headers or []
84- for header in headers :
85- # Remove include/*/
86- short_header = header .split ("/" , 2 )[- 1 ]
87-
88- dst = os .path .join (self .install_dir , os .path .dirname (short_header ))
89- self .mkpath (dst )
90- (out , _ ) = self .copy_file (header , dst )
91- self .outfiles .append (out )
92-
93-
94- main_headers = {
95- "include/pybind11/attr.h" ,
96- "include/pybind11/buffer_info.h" ,
97- "include/pybind11/cast.h" ,
98- "include/pybind11/chrono.h" ,
99- "include/pybind11/common.h" ,
100- "include/pybind11/complex.h" ,
101- "include/pybind11/eigen.h" ,
102- "include/pybind11/embed.h" ,
103- "include/pybind11/eval.h" ,
104- "include/pybind11/functional.h" ,
105- "include/pybind11/iostream.h" ,
106- "include/pybind11/numpy.h" ,
107- "include/pybind11/operators.h" ,
108- "include/pybind11/options.h" ,
109- "include/pybind11/pybind11.h" ,
110- "include/pybind11/pytypes.h" ,
111- "include/pybind11/stl.h" ,
112- "include/pybind11/stl_bind.h" ,
113- }
114-
115- detail_headers = {
116- "include/pybind11/detail/class.h" ,
117- "include/pybind11/detail/common.h" ,
118- "include/pybind11/detail/descr.h" ,
119- "include/pybind11/detail/init.h" ,
120- "include/pybind11/detail/internals.h" ,
121- "include/pybind11/detail/typeid.h" ,
122- }
123-
124- headers = main_headers | detail_headers
125- check_compare (headers , "include/**/*.h" )
126-
127- if use_headers :
128- setup_opts ["headers" ] = headers
129- setup_opts ["cmdclass" ] = {"install_headers" : InstallHeadersNested }
130-
131- cmake_files = {
132- "pybind11/share/cmake/pybind11/FindPythonLibsNew.cmake" ,
133- "pybind11/share/cmake/pybind11/pybind11Common.cmake" ,
134- "pybind11/share/cmake/pybind11/pybind11Config.cmake" ,
135- "pybind11/share/cmake/pybind11/pybind11ConfigVersion.cmake" ,
136- "pybind11/share/cmake/pybind11/pybind11NewTools.cmake" ,
137- "pybind11/share/cmake/pybind11/pybind11Targets.cmake" ,
138- "pybind11/share/cmake/pybind11/pybind11Tools.cmake" ,
139- }
140-
141-
142- package_headers = set ("pybind11/{}" .format (h ) for h in headers )
143- package_files = package_headers | cmake_files
144-
145- # Generate the files if they are not generated (will be present in tarball)
146- GENERATED = (
147- []
148- if all (os .path .exists (h ) for h in package_files )
149- else ["pybind11/include" , "pybind11/share" ]
150- )
151- with remove_output (* GENERATED ):
64+ with remove_output ("pybind11/include" , "pybind11/share" ):
15265 # Generate the files if they are not present.
153- if GENERATED :
154- with TemporaryDirectory () as tmpdir :
155- cmd = ["cmake" , "-S" , "." , "-B" , tmpdir ] + [
156- "-DCMAKE_INSTALL_PREFIX=pybind11" ,
157- "-DBUILD_TESTING=OFF" ,
158- "-DPYBIND11_NOPYTHON=ON" ,
159- ]
160- cmake_opts = dict (cwd = DIR , stdout = sys .stdout , stderr = sys .stderr )
161- subprocess .check_call (cmd , ** cmake_opts )
162- subprocess .check_call (["cmake" , "--install" , tmpdir ], ** cmake_opts )
163-
164- # Make sure all files are present
165- check_compare (package_files , "pybind11/include/**/*.h" , "pybind11/share/**/*.cmake" )
166-
167- if use_system :
168- setup_opts ["data_files" ] = [
169- ("share/cmake" , cmake_files ),
170- ("include/pybind11" , main_headers ),
171- ("include/pybind11/detail" , detail_headers ),
66+ with TemporaryDirectory () as tmpdir :
67+ cmd = ["cmake" , "-S" , "." , "-B" , tmpdir ] + [
68+ "-DCMAKE_INSTALL_PREFIX=pybind11" ,
69+ "-DBUILD_TESTING=OFF" ,
70+ "-DPYBIND11_NOPYTHON=ON" ,
17271 ]
173-
174- # Remove the cmake / ninja requirements as now all files are guaranteed to exist
175- if GENERATED :
176- REQUIRES = re .compile (r"requires\s*=.+?\]" , re .DOTALL | re .MULTILINE )
177- with monkey_patch_file ("pyproject.toml" ) as txt :
178- with open ("pyproject.toml" , "w" ) as f :
179- new_txt = REQUIRES .sub ('requires = ["setuptools", "wheel"]' , txt )
180- f .write (new_txt )
181-
182- setup (** setup_opts )
183- else :
184- setup (** setup_opts )
72+ cmake_opts = dict (cwd = DIR , stdout = sys .stdout , stderr = sys .stderr )
73+ subprocess .check_call (cmd , ** cmake_opts )
74+ subprocess .check_call (["cmake" , "--install" , tmpdir ], ** cmake_opts )
75+
76+ with monkey_patch_file ("pyproject.toml" , pyproject_toml ):
77+ with monkey_patch_file ("setup.py" , setup_py ):
78+ with open (setup_py ) as f :
79+ code = compile (f .read (), setup_py , "exec" )
80+ exec (code , globals (), {})
0 commit comments