@@ -22,6 +22,59 @@ def __dir__() -> list[str]:
2222 return __all__
2323
2424
25+ class FileLockIfUnix :
26+ def __init__ (self , lock_file : str ) -> None :
27+ self .lock_file = lock_file
28+ self .lock_file_fd : int | None = None
29+
30+ def acquire (self ) -> None :
31+ # Based on filelock.BaseFileLock.acquire and filelock.UnixFileLock._acquire
32+ try :
33+ import fcntl
34+ except ImportError :
35+ return
36+ import contextlib
37+ import time
38+
39+ poll_interval = 0.05
40+ log_interval = 60
41+ last_log = time .perf_counter ()
42+
43+ while True :
44+ os .makedirs (os .path .dirname (self .lock_file ), exist_ok = True )
45+ open_flags = os .O_RDWR | os .O_TRUNC
46+ if not os .path .exists (self .lock_file ):
47+ open_flags |= os .O_CREAT
48+
49+ fd = os .open (self .lock_file , open_flags , 0o644 )
50+ with contextlib .suppress (PermissionError ): # Lock is not owned by this UID
51+ os .fchmod (fd , 0o644 )
52+ try :
53+ fcntl .flock (fd , fcntl .LOCK_EX | fcntl .LOCK_NB )
54+ except OSError :
55+ os .close (fd )
56+ else :
57+ self .lock_file_fd = fd
58+ return
59+
60+ now = time .perf_counter ()
61+ if now - last_log > log_interval :
62+ last_log = now
63+ print (f"Still waiting to acquire lock { self .lock_file } ..." ) # noqa: T201
64+
65+ time .sleep (poll_interval )
66+
67+ def release (self ) -> None :
68+ try :
69+ import fcntl
70+ except ImportError :
71+ return
72+
73+ assert isinstance (self .lock_file_fd , int )
74+ fcntl .flock (self .lock_file_fd , fcntl .LOCK_UN )
75+ os .close (self .lock_file_fd )
76+
77+
2578class ScikitBuildRedirectingFinder (importlib .abc .MetaPathFinder ):
2679 def __init__ (
2780 self ,
@@ -128,42 +181,49 @@ def rebuild(self) -> None:
128181 if verbose :
129182 print (f"Running cmake --build & --install in { self .path } " ) # noqa: T201
130183
131- result = subprocess . run (
132- [ "cmake" , "--build" , "." , * self . build_options ],
133- cwd = self . path ,
134- stdout = sys . stderr if verbose else subprocess . PIPE ,
135- env = env ,
136- check = False ,
137- text = True ,
138- )
139- if result . returncode and verbose :
140- print ( # noqa: T201
141- f"ERROR: { result . stdout } " ,
142- file = sys . stderr ,
184+ lock = FileLockIfUnix ( os . path . join ( self . path , "editable_rebuild.lock" ))
185+
186+ try :
187+ lock . acquire ()
188+
189+ result = subprocess . run (
190+ [ "cmake" , "--build" , "." , * self . build_options ] ,
191+ cwd = self . path ,
192+ stdout = sys . stderr if verbose else subprocess . PIPE ,
193+ env = env ,
194+ check = False ,
195+ text = True ,
143196 )
144- result .check_returncode ()
145-
146- result = subprocess . run (
147- [
148- "cmake" ,
149- "--install" ,
150- "." ,
151- "--prefix" ,
152- self . install_dir ,
153- * self . install_options ,
154- ] ,
155- cwd = self . path ,
156- stdout = sys . stderr if verbose else subprocess . PIPE ,
157- env = env ,
158- check = False ,
159- text = True ,
160- )
161- if result . returncode and verbose :
162- print ( # noqa: T201
163- f"ERROR: { result . stdout } " ,
164- file = sys . stderr ,
197+ if result .returncode and verbose :
198+ print ( # noqa: T201
199+ f"ERROR: { result . stdout } " ,
200+ file = sys . stderr ,
201+ )
202+ result . check_returncode ()
203+
204+ result = subprocess . run (
205+ [
206+ "cmake" ,
207+ "--install" ,
208+ "." ,
209+ "--prefix" ,
210+ self . install_dir ,
211+ * self . install_options ,
212+ ] ,
213+ cwd = self . path ,
214+ stdout = sys . stderr if verbose else subprocess . PIPE ,
215+ env = env ,
216+ check = False ,
217+ text = True ,
165218 )
166- result .check_returncode ()
219+ if result .returncode and verbose :
220+ print ( # noqa: T201
221+ f"ERROR: { result .stdout } " ,
222+ file = sys .stderr ,
223+ )
224+ result .check_returncode ()
225+ finally :
226+ lock .release ()
167227
168228
169229def install (
0 commit comments