comparison setup.py @ 41:71bcc18e306f

MERGE: New upstream PyMuPDF v1.26.5 including MuPDF v1.26.10 BUGS: Needs some additional changes yet. Not yet tested.
author Franz Glasner <fzglas.hg@dom66.de>
date Sat, 11 Oct 2025 15:24:40 +0200
parents 14b91574d44a a6bc019ac0b2
children 0a8b06e38e19
comparison
equal deleted inserted replaced
38:8934ac156ef5 41:71bcc18e306f
86 If unset or '-', use internal hard-coded default MuPDF location. 86 If unset or '-', use internal hard-coded default MuPDF location.
87 Otherwise overrides location of MuPDF when building PyMuPDF: 87 Otherwise overrides location of MuPDF when building PyMuPDF:
88 Empty string: 88 Empty string:
89 Build PyMuPDF with the system MuPDF. 89 Build PyMuPDF with the system MuPDF.
90 A string starting with 'git:': 90 A string starting with 'git:':
91 Use `git clone` to get a MuPDF checkout. We use the 91 We use `git` commands to clone/update a local MuPDF checkout.
92 string in the git clone command; it must contain the git 92 Should match `git:[--branch <branch>][--tag <tag>][<remote>]`.
93 URL from which to clone, and can also contain other `git 93 If <remote> is omitted we use a default.
94 clone` args, for example: 94 For example:
95 PYMUPDF_SETUP_MUPDF_BUILD="git:--branch master https://github.com/ArtifexSoftware/mupdf.git" 95 PYMUPDF_SETUP_MUPDF_BUILD="git:--branch master"
96 Passed as <text> arg to pipcl.git_get().
96 Otherwise: 97 Otherwise:
97 Location of mupdf directory. 98 Location of mupdf directory.
98 99
99 PYMUPDF_SETUP_MUPDF_BSYMBOLIC 100 PYMUPDF_SETUP_MUPDF_BSYMBOLIC
100 If '0' we do not link libmupdf.so with -Bsymbolic. 101 If '0' we do not link libmupdf.so with -Bsymbolic.
459 run(f'cd {directory} && git diff') 460 run(f'cd {directory} && git diff')
460 461
461 462
462 mupdf_tgz = os.path.abspath( f'{__file__}/../mupdf.tgz') 463 mupdf_tgz = os.path.abspath( f'{__file__}/../mupdf.tgz')
463 464
464 def get_mupdf_internal(out, location=None, sha=None, local_tgz=None): 465 def get_mupdf_internal(out, location=None, local_tgz=None):
465 ''' 466 '''
466 Gets MuPDF as either a .tgz or a local directory. 467 Gets MuPDF as either a .tgz or a local directory.
467 468
468 Args: 469 Args:
469 out: 470 out:
472 location: 473 location:
473 First, if None we set to hard-coded default URL or git location. 474 First, if None we set to hard-coded default URL or git location.
474 If starts with 'git:', should be remote git location. 475 If starts with 'git:', should be remote git location.
475 Otherwise if containing '://' should be URL for .tgz. 476 Otherwise if containing '://' should be URL for .tgz.
476 Otherwise should path of local mupdf checkout. 477 Otherwise should path of local mupdf checkout.
477 sha:
478 If not None and we use git clone, we checkout this sha.
479 local_tgz: 478 local_tgz:
480 If not None, must be local .tgz file. 479 If not None, must be local .tgz file.
481 Returns: 480 Returns:
482 (path, location): 481 (path, location):
483 `path` is absolute path of local directory or .tgz containing 482 `path` is absolute path of local directory or .tgz containing
485 484
486 `location_out` is `location` if not None, else the hard-coded 485 `location_out` is `location` if not None, else the hard-coded
487 default location. 486 default location.
488 487
489 ''' 488 '''
490 log(f'get_mupdf_internal(): {out=} {location=} {sha=}') 489 log(f'get_mupdf_internal(): {out=} {location=}')
491 assert out in ('dir', 'tgz') 490 assert out in ('dir', 'tgz')
492 if location is None: 491 if location is None:
493 location = f'https://mupdf.com/downloads/archive/mupdf-{version_mupdf}-source.tar.gz' 492 location = f'https://mupdf.com/downloads/archive/mupdf-{version_mupdf}-source.tar.gz'
494 #location = 'git:--branch master https://github.com/ArtifexSoftware/mupdf.git' 493 #location = 'git:--branch master https://github.com/ArtifexSoftware/mupdf.git'
495 494
499 498
500 local_dir = None 499 local_dir = None
501 if local_tgz: 500 if local_tgz:
502 assert os.path.isfile(local_tgz) 501 assert os.path.isfile(local_tgz)
503 elif location.startswith( 'git:'): 502 elif location.startswith( 'git:'):
504 location_git = location[4:]
505 local_dir = 'mupdf-git' 503 local_dir = 'mupdf-git'
506 504 pipcl.git_get(local_dir, text=location, remote='https://github.com/ArtifexSoftware/mupdf.git')
507 # Try to update existing checkout. 505
508 e = run(f'cd {local_dir} && git pull && git submodule update --init', check=False)
509 if e:
510 # No existing git checkout, so do a fresh clone.
511 _fs_remove(local_dir)
512 gitargs = location[4:]
513 run(f'git clone --recursive --depth 1 --shallow-submodules {gitargs} {local_dir}')
514
515 # Show sha of checkout. 506 # Show sha of checkout.
516 run( f'cd {local_dir} && git show --pretty=oneline|head -n 1', check=False) 507 run(
517 if sha: 508 f'cd {local_dir} && git show --pretty=oneline|head -n 1',
518 run( f'cd {local_dir} && git checkout {sha}') 509 check = False,
510 prefix = 'mupdf git id: ',
511 )
519 elif '://' in location: 512 elif '://' in location:
520 # Download .tgz. 513 # Download .tgz.
521 local_tgz = os.path.basename( location) 514 local_tgz = os.path.basename( location)
522 suffix = '.tar.gz' 515 suffix = '.tar.gz'
523 assert location.endswith(suffix), f'Unrecognised suffix in remote URL {location=}.' 516 assert location.endswith(suffix), f'Unrecognised suffix in remote URL {location=}.'
608 freebsd = sys.platform.startswith( 'freebsd') 601 freebsd = sys.platform.startswith( 'freebsd')
609 darwin = sys.platform.startswith( 'darwin') 602 darwin = sys.platform.startswith( 'darwin')
610 windows = platform.system() == 'Windows' or platform.system().startswith('CYGWIN') 603 windows = platform.system() == 'Windows' or platform.system().startswith('CYGWIN')
611 msys2 = platform.system().startswith('MSYS_NT-') 604 msys2 = platform.system().startswith('MSYS_NT-')
612 605
613 pyodide_flags = '-fwasm-exceptions'
614
615 if os.environ.get('PYODIDE') == '1': 606 if os.environ.get('PYODIDE') == '1':
616 if os.environ.get('OS') != 'pyodide': 607 if os.environ.get('OS') != 'pyodide':
617 log('PYODIDE=1, setting OS=pyodide.') 608 log('PYODIDE=1, setting OS=pyodide.')
618 os.environ['OS'] = 'pyodide' 609 os.environ['OS'] = 'pyodide'
619 os.environ['XCFLAGS'] = pyodide_flags
620 os.environ['XCXXFLAGS'] = pyodide_flags
621 610
622 pyodide = os.environ.get('OS') == 'pyodide' 611 pyodide = os.environ.get('OS') == 'pyodide'
623 612
624 def build(): 613 def build():
625 ''' 614 '''
738 add('b', f'{mupdf_build_dir}/libmupdfcpp.so', to_dir) 727 add('b', f'{mupdf_build_dir}/libmupdfcpp.so', to_dir)
739 add('b', f'{mupdf_build_dir}/libmupdf.dylib', to_dir) 728 add('b', f'{mupdf_build_dir}/libmupdf.dylib', to_dir)
740 add('d', f'{mupdf_build_dir}/libmupdf-threads.a', f'{to_dir_d}/lib/') 729 add('d', f'{mupdf_build_dir}/libmupdf-threads.a', f'{to_dir_d}/lib/')
741 elif pyodide: 730 elif pyodide:
742 add('p', f'{mupdf_build_dir}/_mupdf.so', to_dir) 731 add('p', f'{mupdf_build_dir}/_mupdf.so', to_dir)
743 add('b', f'{mupdf_build_dir}/libmupdfcpp.so', 'PyMuPDF.libs/') 732 add('b', f'{mupdf_build_dir}/libmupdfcpp.so', to_dir)
744 add('b', f'{mupdf_build_dir}/libmupdf.so', 'PyMuPDF.libs/') 733 add('b', f'{mupdf_build_dir}/libmupdf.so', to_dir)
745 else: 734 else:
746 add('p', f'{mupdf_build_dir}/_mupdf.so', to_dir) 735 add('p', f'{mupdf_build_dir}/_mupdf.so', to_dir)
747 add('b', pipcl.get_soname(f'{mupdf_build_dir}/libmupdfcpp.so'), to_dir) 736 add('b', pipcl.get_soname(f'{mupdf_build_dir}/libmupdfcpp.so'), to_dir)
748 add('b', pipcl.get_soname(f'{mupdf_build_dir}/libmupdf.so'), to_dir) 737 add('b', pipcl.get_soname(f'{mupdf_build_dir}/libmupdf.so'), to_dir)
749 add('d', f'{mupdf_build_dir}/libmupdf-threads.a', f'{to_dir_d}/lib/') 738 add('d', f'{mupdf_build_dir}/libmupdf-threads.a', f'{to_dir_d}/lib/')
782 try: 771 try:
783 return int(text) 772 return int(text)
784 except Exception: 773 except Exception:
785 return 0 774 return 0
786 swig_version_tuple = tuple(int_or_0(i) for i in swig_version.split('.')) 775 swig_version_tuple = tuple(int_or_0(i) for i in swig_version.split('.'))
776 version_p_tuple = tuple(int_or_0(i) for i in version_p.split('.'))
787 log(f'{swig_version=}') 777 log(f'{swig_version=}')
788 text = '' 778 text = ''
789 if os.path.isdir(mupdf_location): 779 if os.path.isdir(mupdf_location):
790 mupdf_location = mupdf_location.rstrip('/') 780 mupdf_location = mupdf_location.rstrip('/')
791 text += 'mupdf_location = ' + repr(os.path.basename(mupdf_location)) + '\n' 781 text += 'mupdf_location = ' + repr(os.path.basename(mupdf_location)) + '\n'
792 else: 782 else:
793 text += f'mupdf_location = {mupdf_location!r}\n' 783 text += f'mupdf_location = {mupdf_location!r}\n'
794 text += f'pymupdf_version = {version_p!r}\n' 784 text += f'pymupdf_version = {version_p!r}\n'
785 text += f'pymupdf_version_tuple = {version_p_tuple!r}\n'
795 text += f'pymupdf_git_sha = {sha!r}\n' 786 text += f'pymupdf_git_sha = {sha!r}\n'
796 text += f'pymupdf_git_diff = {diff!r}\n' 787 text += f'pymupdf_git_diff = {diff!r}\n'
797 text += f'pymupdf_git_branch = {branch!r}\n' 788 text += f'pymupdf_git_branch = {branch!r}\n'
798 text += f'swig_version = {swig_version!r}\n' 789 text += f'swig_version = {swig_version!r}\n'
799 text += f'swig_version_tuple = {swig_version_tuple!r}\n' 790 text += f'swig_version_tuple = {swig_version_tuple!r}\n'
1256 compiler_extra += f' {cflags}' 1247 compiler_extra += f' {cflags}'
1257 cxxflags = os.environ.get('CXXFLAGS') 1248 cxxflags = os.environ.get('CXXFLAGS')
1258 if cxxflags: 1249 if cxxflags:
1259 compiler_extra += f' {cxxflags}' 1250 compiler_extra += f' {cxxflags}'
1260 1251
1261 if pyodide:
1262 compiler_extra += f' {pyodide_flags}'
1263 linker_extra += f' {pyodide_flags}'
1264 1252
1265 if not darwin and (platform.system() != 'Windows'): 1253 if not darwin and (platform.system() != 'Windows'):
1266 # *BSD and Linux 1254 # *BSD and Linux
1267 # Full RELRO 1255 # Full RELRO
1268 linker_extra += ' -Wl,-z,relro,-z,now' 1256 linker_extra += ' -Wl,-z,relro,-z,now'
1333 1321
1334 # We generate different wheels depending on PYMUPDF_SETUP_FLAVOUR. 1322 # We generate different wheels depending on PYMUPDF_SETUP_FLAVOUR.
1335 # 1323 #
1336 1324
1337 # PyMuPDF version. 1325 # PyMuPDF version.
1338 version_p = '1.26.4+2' 1326 version_p = '1.26.5+XXXFIXME0'
1339 1327
1340 version_mupdf = '1.26.7' 1328 version_mupdf = '1.26.10'
1341 1329
1342 # PyMuPDFb version. This is the PyMuPDF version whose PyMuPDFb wheels we will 1330 # PyMuPDFb version. This is the PyMuPDF version whose PyMuPDFb wheels we will
1343 # (re)use if generating separate PyMuPDFb wheels. Though as of PyMuPDF-1.24.11 1331 # (re)use if generating separate PyMuPDFb wheels. Though as of PyMuPDF-1.24.11
1344 # (2024-10-03) we no longer use PyMuPDFb wheels so this is actually unused. 1332 # (2024-10-03) we no longer use PyMuPDFb wheels so this is actually unused.
1345 # 1333 #
1472 if libclang: 1460 if libclang:
1473 print(f'Overriding to use {libclang=}.') 1461 print(f'Overriding to use {libclang=}.')
1474 ret.append(libclang) 1462 ret.append(libclang)
1475 elif openbsd: 1463 elif openbsd:
1476 print(f'OpenBSD: libclang not available via pip; assuming `pkg_add py3-llvm`.') 1464 print(f'OpenBSD: libclang not available via pip; assuming `pkg_add py3-llvm`.')
1477 elif darwin and platform.machine() == 'arm64':
1478 print(f'MacOS/arm64: forcing use of libclang 16.0.6 because 18.1.1 known to fail with `clang.cindex.TranslationUnitLoadError: Error parsing translation unit.`')
1479 ret.append('libclang==16.0.6')
1480 elif darwin and platform_release_tuple() < (18,): 1465 elif darwin and platform_release_tuple() < (18,):
1481 # There are still of problems when building on old macos. 1466 # There are still of problems when building on old macos.
1482 ret.append('libclang==14.0.6') 1467 ret.append('libclang==14.0.6')
1483 else: 1468 else:
1484 ret.append('libclang') 1469 ret.append('libclang')