comparison pipcl.py @ 18:dd663470c57c

Make building an sdist work on FreeBSD and with Mercurial as SCM. Because of the inclusion of MuPDF symbolic links in tar-files must be handled also.
author Franz Glasner <fzglas.hg@dom66.de>
date Thu, 18 Sep 2025 22:02:17 +0200
parents 59f1bd90b2a0
children dcabf2733f0f
comparison
equal deleted inserted replaced
17:dd9cdb856310 18:dd663470c57c
685 dist_info_dir = self._dist_info_dir() 685 dist_info_dir = self._dist_info_dir()
686 686
687 # Add the files returned by fn_build(). 687 # Add the files returned by fn_build().
688 # 688 #
689 for item in items: 689 for item in items:
690 from_, (to_abs, to_rel) = self._fromto(item) 690 from_, (to_abs, to_rel, _) = self._fromto(item)
691 add(from_, to_rel) 691 add(from_, to_rel)
692 692
693 # Add <name>-<version>.dist-info/WHEEL. 693 # Add <name>-<version>.dist-info/WHEEL.
694 # 694 #
695 add_str( 695 add_str(
783 783
784 def add_string(text, name): 784 def add_string(text, name):
785 textb = text.encode('utf8') 785 textb = text.encode('utf8')
786 return add(textb, name) 786 return add(textb, name)
787 787
788 def add_symlink(from_, name, linkname=None):
789 check_name(name)
790 assert isinstance(from_, str)
791 assert isinstance(name, str)
792 assert name
793 assert os.path.islink(from_)
794 if linkname is None:
795 linkname = os.readlink(from_)
796 log2(f'Adding symlink: {os.path.relpath(from_)} => {name} --> {linkname}')
797 ti = tar.gettarinfo(from_, name)
798 tar.addfile(ti)
799
788 found_pyproject_toml = False 800 found_pyproject_toml = False
789 for item in items: 801 for item in items:
790 from_, (to_abs, to_rel) = self._fromto(item) 802 from_, (to_abs, to_rel, to_symlink) = self._fromto(item, resolve_symlinks=False)
791 if isinstance(from_, bytes): 803 if to_symlink:
804 add_symlink(from_, to_rel, to_symlink)
805 elif isinstance(from_, bytes):
792 add(from_, to_rel) 806 add(from_, to_rel)
793 else: 807 else:
794 if from_.startswith(f'{os.path.abspath(sdist_directory)}/'): 808 if from_.startswith(f'{os.path.abspath(sdist_directory)}/'):
795 # Source files should not be inside <sdist_directory>. 809 # Source files should not be inside <sdist_directory>.
796 assert 0, f'Path is inside sdist_directory={sdist_directory}: {from_!r}' 810 assert 0, f'Path is inside sdist_directory={sdist_directory}: {from_!r}'
1018 with open( to_abs, 'w') as f: 1032 with open( to_abs, 'w') as f:
1019 f.write( content) 1033 f.write( content)
1020 record.add_content(content, to_rel) 1034 record.add_content(content, to_rel)
1021 1035
1022 for item in items: 1036 for item in items:
1023 from_, (to_abs, to_rel) = self._fromto(item) 1037 from_, (to_abs, to_rel, _) = self._fromto(item)
1024 log0(f'{from_=} {to_abs=} {to_rel=}') 1038 log0(f'{from_=} {to_abs=} {to_rel=}')
1025 to_abs2 = f'{root2}/{to_rel}' 1039 to_abs2 = f'{root2}/{to_rel}'
1026 add_file( from_, to_abs2, to_rel) 1040 add_file( from_, to_abs2, to_rel)
1027 1041
1028 add_str( self._metainfo(), f'{root2}/{dist_info_dir}/METADATA', f'{dist_info_dir}/METADATA') 1042 add_str( self._metainfo(), f'{root2}/{dist_info_dir}/METADATA', f'{dist_info_dir}/METADATA')
1413 ret += '\n' # Empty line separates headers from body. 1427 ret += '\n' # Empty line separates headers from body.
1414 ret += description_text 1428 ret += description_text
1415 ret += '\n' 1429 ret += '\n'
1416 return ret 1430 return ret
1417 1431
1418 def _path_relative_to_root(self, path, assert_within_root=True): 1432 def _path_relative_to_root(self, path, assert_within_root=True, resolve_symlinks=True):
1419 ''' 1433 '''
1420 Returns `(path_abs, path_rel)`, where `path_abs` is absolute path and 1434 Returns `(path_abs, path_rel)`, where `path_abs` is absolute path and
1421 `path_rel` is relative to `self.root`. 1435 `path_rel` is relative to `self.root`.
1422 1436
1423 Interprets `path` as relative to `self.root` if not absolute. 1437 Interprets `path` as relative to `self.root` if not absolute.
1429 ''' 1443 '''
1430 if os.path.isabs(path): 1444 if os.path.isabs(path):
1431 p = path 1445 p = path
1432 else: 1446 else:
1433 p = os.path.join(self.root, path) 1447 p = os.path.join(self.root, path)
1448 path_abs = p
1449 # always resolve symlinks at first
1434 p = os.path.realpath(os.path.abspath(p)) 1450 p = os.path.realpath(os.path.abspath(p))
1435 if assert_within_root: 1451 if assert_within_root:
1436 assert p.startswith(self.root+os.sep) or p == self.root, \ 1452 assert p.startswith(self.root+os.sep) or p == self.root, \
1437 f'Path not within root={self.root+os.sep!r}: {path=} {p=}' 1453 f'Path not within root={self.root+os.sep!r}: {path=} {p=}'
1438 p_rel = os.path.relpath(p, self.root) 1454 p_rel = os.path.relpath(p, self.root)
1439 return p, p_rel 1455 if resolve_symlinks or not os.path.islink(path_abs):
1440 1456 return p, p_rel, None
1441 def _fromto(self, p): 1457 else:
1458 assert os.path.islink(path_abs)
1459 p_rel = os.path.relpath(path_abs, self.root)
1460 return path_abs, p_rel, os.readlink(path_abs)
1461
1462 def _fromto(self, p, resolve_symlinks=True):
1442 ''' 1463 '''
1443 Returns `(from_, (to_abs, to_rel))`. 1464 Returns `(from_, (to_abs, to_rel))`.
1444 1465
1445 If `p` is a string we convert to `(p, p)`. Otherwise we assert that 1466 If `p` is a string we convert to `(p, p)`. Otherwise we assert that
1446 `p` is a tuple `(from_, to_)` where `from_` is str/bytes and `to_` is 1467 `p` is a tuple `(from_, to_)` where `from_` is str/bytes and `to_` is
1474 to_ = f'{self._dist_info_dir()}/{to_[ len(prefix):]}' 1495 to_ = f'{self._dist_info_dir()}/{to_[ len(prefix):]}'
1475 prefix = '$data/' 1496 prefix = '$data/'
1476 if to_.startswith( prefix): 1497 if to_.startswith( prefix):
1477 to_ = f'{self.name}-{self.version}.data/{to_[ len(prefix):]}' 1498 to_ = f'{self.name}-{self.version}.data/{to_[ len(prefix):]}'
1478 if isinstance(from_, str): 1499 if isinstance(from_, str):
1479 from_, _ = self._path_relative_to_root( from_, assert_within_root=False) 1500 from_, _, _ = self._path_relative_to_root( from_, assert_within_root=False, resolve_symlinks=resolve_symlinks)
1480 to_ = self._path_relative_to_root(to_) 1501 to_ = self._path_relative_to_root(to_, resolve_symlinks=resolve_symlinks)
1481 assert isinstance(from_, (str, bytes)) 1502 assert isinstance(from_, (str, bytes))
1482 log2(f'returning {from_=} {to_=}') 1503 log2(f'returning {from_=} {to_=}')
1483 return from_, to_ 1504 return from_, to_
1484 1505
1485 1506
2004 2025
2005 We run a `git ls-files` command internally. 2026 We run a `git ls-files` command internally.
2006 2027
2007 This function can be useful for the `fn_sdist()` callback. 2028 This function can be useful for the `fn_sdist()` callback.
2008 ''' 2029 '''
2009 command = 'cd ' + directory + ' && git ls-files' 2030 if os.path.isdir(os.path.join(directory, ".hg")):
2010 if submodules: 2031 command = 'cd ' + directory + ' && hg files'
2011 command += ' --recurse-submodules' 2032 if submodules:
2033 command += ' --subrepos'
2034 else:
2035 command = 'cd ' + directory + ' && git ls-files'
2036 if submodules:
2037 command += ' --recurse-submodules'
2012 log1(f'Running {command=}') 2038 log1(f'Running {command=}')
2013 text = subprocess.check_output( command, shell=True) 2039 text = subprocess.check_output( command, shell=True)
2014 ret = [] 2040 ret = []
2015 for path in text.decode('utf8').strip().split( '\n'): 2041 for path in text.decode('utf8').strip().split( '\n'):
2016 path2 = os.path.join(directory, path) 2042 path2 = os.path.join(directory, path)