Skip to content

Commit 8fd0a23

Browse files
pipcl.py: fix github macos x64+arm64 builds by improving command rerunning.
We now also rerun commands if the command itself has changed. In Github MacOS builds, x64 and arm64 builds are done in same PyMuPDF checkout. But extension library names such as _fitz.cpython-311-darwin.so do not contain the cpu, so the second build does nothing because the output file looks up to date. So have changed running of commands to also detect whether the command itself has changed. This works by writing the command into <out>.cmd
1 parent 842729b commit 8fd0a23

File tree

1 file changed

+145
-68
lines changed

1 file changed

+145
-68
lines changed

pipcl.py

Lines changed: 145 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,22 +1332,25 @@ def build_extension(
13321332
# Run SWIG.
13331333
deps_path = f'{path_cpp}.d'
13341334
prerequisites_swig2 = _get_prerequisites( deps_path)
1335-
if _doit( path_cpp, path_i, prerequisites_swig, prerequisites_swig2):
1336-
run( f'''
1337-
{swig}
1338-
-Wall
1339-
{"-c++" if cpp else ""}
1340-
-python
1341-
-module {name}
1342-
-outdir {outdir}
1343-
-o {path_cpp}
1344-
-MD -MF {deps_path}
1345-
{includes_text}
1346-
{path_i}
1347-
'''
1348-
)
1349-
else:
1350-
_log(f'Not running swig because up to date: {path_cpp}')
1335+
run_if(
1336+
f'''
1337+
{swig}
1338+
-Wall
1339+
{"-c++" if cpp else ""}
1340+
-python
1341+
-module {name}
1342+
-outdir {outdir}
1343+
-o {path_cpp}
1344+
-MD -MF {deps_path}
1345+
{includes_text}
1346+
{path_i}
1347+
'''
1348+
,
1349+
path_cpp,
1350+
path_i,
1351+
prerequisites_swig,
1352+
prerequisites_swig2,
1353+
)
13511354

13521355
path_so_leaf = f'_{name}{_so_suffix()}'
13531356
path_so = f'{outdir}/{path_so_leaf}'
@@ -1404,10 +1407,7 @@ def build_extension(
14041407
{defines_text}
14051408
{compiler_extra}
14061409
'''
1407-
if _doit( path_obj, path_cpp, prerequisites_compile):
1408-
run(command)
1409-
else:
1410-
_log(f'Not compiling because up to date: {path_obj}')
1410+
run_if( command, path_obj, path_cpp, prerequisites_compile)
14111411

14121412
command, pythonflags = base_linker(cpp=cpp)
14131413
debug2 = '/DEBUG' if debug else ''
@@ -1426,10 +1426,7 @@ def build_extension(
14261426
{path_obj}
14271427
{linker_extra}
14281428
'''
1429-
if _doit( path_so, path_obj, prerequisites_link):
1430-
run(command)
1431-
else:
1432-
_log(f'Not linking because up to date: {path_so}')
1429+
run_if( command, path_so, path_obj, prerequisites_link)
14331430

14341431
else:
14351432

@@ -1525,10 +1522,14 @@ def build_extension(
15251522
{linker_extra}
15261523
{pythonflags.ldflags}
15271524
'''
1528-
if _doit( path_so, path_cpp, prerequisites_compile, prerequisites_link, prerequisites):
1529-
run(command)
1530-
else:
1531-
_log(f'Not compiling+linking because up to date: {path_so}')
1525+
run_if(
1526+
command,
1527+
path_so,
1528+
path_cpp,
1529+
prerequisites_compile,
1530+
prerequisites_link,
1531+
prerequisites,
1532+
)
15321533

15331534
if darwin():
15341535
# We need to patch up references to shared libraries in `libs`.
@@ -1548,7 +1549,8 @@ def build_extension(
15481549
_log(f'Warning: can not find path of lib={lib!r} in libpaths={libpaths}')
15491550
macos_patch( path_so, *sublibraries)
15501551

1551-
run(f'file {path_so}, check=0')
1552+
run(f'ls -l {path_so}', check=0)
1553+
run(f'file {path_so}', check=0)
15521554

15531555
return path_so_leaf
15541556

@@ -1879,52 +1881,127 @@ def _cpu_name():
18791881
return f'x{32 if sys.maxsize == 2**31 - 1 else 64}'
18801882

18811883

1882-
def _doit( out, in_, *prerequisites):
1884+
def run_if( command, out, *prerequisites, verbose=True):
18831885
'''
1884-
Returns true/false for whether to run a command.
1886+
Runs a command only if the output file is not up to date.
18851887
1886-
out:
1887-
Output path.
1888-
prerequisites:
1889-
List of input paths or true/false/None. If an item is None it is
1890-
ignored, otherwise if an item is not a string we immediately return it
1891-
cast to a bool.
1892-
'''
1893-
_log( f'out={out!r}')
1894-
_log( f'in={in_!r}')
1895-
def _make_prerequisites(p):
1896-
if isinstance( p, (list, tuple)):
1897-
return list(p)
1898-
else:
1899-
return [p]
1900-
prerequisites_all = list()
1901-
prerequisites_all.append( in_)
1902-
for p in prerequisites:
1903-
prerequisites_all += _make_prerequisites( p)
1904-
if 0:
1905-
_log( 'prerequisites_all:')
1906-
for i in prerequisites_all:
1907-
_log( f' {i!r}')
1908-
pre_mtime = 0
1909-
pre_path = None
1910-
for prerequisite in prerequisites_all:
1911-
if isinstance( prerequisite, str):
1912-
mtime = _fs_mtime_newest( prerequisite)
1913-
if mtime >= pre_mtime:
1914-
pre_mtime = mtime
1915-
pre_path = prerequisite
1916-
elif prerequisite is None:
1888+
Args:
1889+
command:
1890+
The command to run. We write this into a file <out>.cmd so that we
1891+
know to run a command if the command itself has changed.
1892+
out:
1893+
Path of the output file.
1894+
1895+
prerequisites:
1896+
List of prerequisite paths or true/false/None items. If an item
1897+
is None it is ignored, otherwise if an item is not a string we
1898+
immediately return it cast to a bool.
1899+
1900+
Returns:
1901+
True if we ran the command, otherwise None.
1902+
1903+
1904+
If the output file does not exist, the command is run:
1905+
1906+
>>> out = 'run_if_test_out'
1907+
>>> if os.path.exists( out):
1908+
... os.remove( out)
1909+
>>> run_if( f'touch {out}', out, verbose=0)
1910+
True
1911+
1912+
If we repeat, the output file will be up to date so the command is not run:
1913+
1914+
>>> run_if( f'touch {out}', out, verbose=0)
1915+
1916+
If we change the command, the command is run:
1917+
1918+
>>> run_if( f'touch {out}', out, verbose=0)
1919+
True
1920+
1921+
If we add a prerequisite that is newer than the output, the command is run:
1922+
1923+
>>> prerequisite = 'run_if_test_prerequisite'
1924+
>>> run( f'touch {prerequisite}', verbose=0)
1925+
>>> run_if( f'touch {out}', out, prerequisite, verbose=0)
1926+
True
1927+
1928+
If we repeat, the output will be newer than the prerequisite, so the
1929+
command is not run:
1930+
1931+
>>> run_if( f'touch {out}', out, prerequisite, verbose=1)
1932+
pipcl.py: run_if(): Not running command because up to date: 'run_if_test_out'
1933+
'''
1934+
doit = False
1935+
1936+
if not doit:
1937+
out_mtime = _fs_mtime( out)
1938+
if out_mtime == 0:
1939+
doit = 'File does not exist: {out!e}'
1940+
1941+
cmd_path = f'{out}.cmd'
1942+
if os.path.isfile( cmd_path):
1943+
with open( cmd_path) as f:
1944+
cmd = f.read()
1945+
else:
1946+
cmd = None
1947+
if command != cmd:
1948+
doit = 'Command has changed'
1949+
1950+
if not doit:
1951+
def _make_prerequisites(p):
1952+
if isinstance( p, (list, tuple)):
1953+
return list(p)
1954+
else:
1955+
return [p]
1956+
prerequisites_all = list()
1957+
for p in prerequisites:
1958+
prerequisites_all += _make_prerequisites( p)
1959+
if 0:
1960+
_log( 'prerequisites_all:')
1961+
for i in prerequisites_all:
1962+
_log( f' {i!r}')
1963+
pre_mtime = 0
1964+
pre_path = None
1965+
for prerequisite in prerequisites_all:
1966+
if isinstance( prerequisite, str):
1967+
mtime = _fs_mtime_newest( prerequisite)
1968+
if mtime >= pre_mtime:
1969+
pre_mtime = mtime
1970+
pre_path = prerequisite
1971+
elif prerequisite is None:
1972+
pass
1973+
elif prerequisite:
1974+
doit = str(prerequisite)
1975+
break
1976+
if not doit:
1977+
if pre_mtime > out_mtime:
1978+
doit = f'Prerequisite is new: {pre_path!r}'
1979+
1980+
if doit:
1981+
# Remove `cmd_path` before we run the command, so any failure
1982+
# will force rerun next time.
1983+
#
1984+
try:
1985+
os.remove( cmd_path)
1986+
except Exception:
19171987
pass
1918-
else:
1919-
_log( f'Returning prerequisite={prerequisite!r}')
1920-
return bool(prerequisite)
1921-
out_mtime = _fs_mtime( out)
1922-
ret = pre_mtime >= out_mtime
1988+
if verbose:
1989+
_log( f'Running command because: {doit}')
1990+
1991+
run( command, verbose=verbose)
1992+
1993+
# Write the command we ran, into `cmd_path`.
1994+
with open( cmd_path, 'w') as f:
1995+
f.write( command)
1996+
return True
1997+
else:
1998+
if verbose:
1999+
_log( f'Not running command because up to date: {out!r}')
2000+
19232001
if 0:
19242002
_log( f'out_mtime={time.ctime(out_mtime)} pre_mtime={time.ctime(pre_mtime)}.'
19252003
f' pre_path={pre_path!r}: returning {ret!r}.'
19262004
)
1927-
return ret
19282005

19292006

19302007
def _get_prerequisites(path):

0 commit comments

Comments
 (0)