Skip to content

Commit

Permalink
Allow --add-path, --include, --exclude, and --no-mangle to be…
Browse files Browse the repository at this point in the history
… specified multiple times

When specified multiple times, values are combined instead of
overwritten.
  • Loading branch information
adang1345 committed Aug 13, 2024
1 parent 573103b commit af276c4
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 8 deletions.
16 changes: 8 additions & 8 deletions delvewheel/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@ def main():
parser_needed = subparsers.add_parser('needed', help=parser_needed_description, description=parser_needed_description)
for subparser in (parser_show, parser_repair):
subparser.add_argument('wheel', nargs='+', help='wheel(s) to show or repair')
subparser.add_argument('--add-path', default='', metavar='PATHS', help=f'additional path(s) to search for DLLs, {os.pathsep!r}-delimited')
subparser.add_argument('--include', '--add-dll', default='', metavar='DLLS', type=_dll_names, help=f'force inclusion of DLL name(s), {os.pathsep!r}-delimited')
subparser.add_argument('--exclude', '--no-dll', default='', metavar='DLLS', type=_dll_names, help=f'force exclusion of DLL name(s), {os.pathsep!r}-delimited')
subparser.add_argument('--add-path', action='append', default=[], metavar='PATHS', help=f'additional path(s) to search for DLLs, {os.pathsep!r}-delimited')
subparser.add_argument('--include', '--add-dll', action='append', default=[], metavar='DLLS', type=_dll_names, help=f'force inclusion of DLL name(s), {os.pathsep!r}-delimited')
subparser.add_argument('--exclude', '--no-dll', action='append', default=[], metavar='DLLS', type=_dll_names, help=f'force exclusion of DLL name(s), {os.pathsep!r}-delimited')
subparser.add_argument('--ignore-existing', '--ignore-in-wheel', action='store_true', help="don't search for or vendor in DLLs that are already in the wheel")
subparser.add_argument('--analyze-existing', action='store_true', help='analyze and vendor in dependencies of DLLs that are already in the wheel')
subparser.add_argument('-v', action='count', default=0, help='verbosity')
subparser.add_argument('--extract-dir', help=argparse.SUPPRESS)
subparser.add_argument('--test', default='', help=argparse.SUPPRESS) # comma-separated testing options, internal use only
parser_repair.add_argument('-w', '--wheel-dir', dest='target', default='wheelhouse', help='directory to write repaired wheel')
parser_repair.add_argument('--no-mangle', default='', metavar='DLLS', type=_dll_names, help=f'DLL names(s) not to mangle, {os.pathsep!r}-delimited')
parser_repair.add_argument('--no-mangle', action='append', default=[], metavar='DLLS', type=_dll_names, help=f'DLL names(s) not to mangle, {os.pathsep!r}-delimited')
parser_repair.add_argument('--no-mangle-all', action='store_true', help="don't mangle any DLL names")
parser_repair.add_argument('--strip', action='store_true', help='strip DLLs that contain trailing data when name-mangling')
parser_repair.add_argument('-L', '--lib-sdir', default='.libs', type=_subdir_suffix, help='directory suffix in package to store vendored DLLs (default .libs)')
Expand All @@ -65,9 +65,9 @@ def main():

# handle command
if args.command in ('show', 'repair'):
add_paths = dict.fromkeys(os.path.abspath(path) for path in args.add_path.split(os.pathsep) if path)
include = set(dll_name.lower() for dll_name in args.include.split(os.pathsep) if dll_name)
exclude = set(dll_name.lower() for dll_name in args.exclude.split(os.pathsep) if dll_name)
add_paths = dict.fromkeys(os.path.abspath(path) for path in os.pathsep.join(args.add_path).split(os.pathsep) if path)
include = set(dll_name.lower() for dll_name in os.pathsep.join(args.include).split(os.pathsep) if dll_name)
exclude = set(dll_name.lower() for dll_name in os.pathsep.join(args.exclude).split(os.pathsep) if dll_name)

intersection = include & exclude
if intersection:
Expand All @@ -81,7 +81,7 @@ def main():
if args.command == 'show':
wr.show()
else: # args.command == 'repair'
no_mangles = set(dll_name.lower() for dll_name in args.no_mangle.split(os.pathsep) if dll_name)
no_mangles = set(dll_name.lower() for dll_name in os.pathsep.join(args.no_mangle).split(os.pathsep) if dll_name)
namespace_pkgs = set(tuple(namespace_pkg.split('.')) for namespace_pkg in args.namespace_pkg.split(os.pathsep) if namespace_pkg)
wr.repair(args.target, no_mangles, args.no_mangle_all, args.strip, args.lib_sdir, not args.no_diagnostic and 'SOURCE_DATE_EPOCH' not in os.environ, namespace_pkgs, args.include_symbols, args.include_imports)
else: # args.command == 'needed'
Expand Down
49 changes: 49 additions & 0 deletions tests/run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,19 @@ def test_no_mangle_2(self):
self.assertTrue(is_mangled(path.name), f'{path.name} is mangled')
self.assertTrue(import_iknowpy_successful())

def test_no_mangle_3(self):
"""--no-mangle for 2 DLLs, flag specified twice"""
check_call(['delvewheel', 'repair', '--add-path', 'iknowpy', '--no-mangle', 'iKnowEngine.dll', '--no-mangle', 'iKnowBase.dll', 'iknowpy/iknowpy-1.5.3-cp312-cp312-win_amd64.whl'])
with zipfile.ZipFile('wheelhouse/iknowpy-1.5.3-cp312-cp312-win_amd64.whl') as wheel:
for path in zipfile.Path(wheel, 'iknowpy.libs/').iterdir():
if path.name in ('.load-order-iknowpy-1.5.3',):
continue
if path.name.startswith('iKnowEngine') or path.name.startswith('iKnowBase'):
self.assertFalse(is_mangled(path.name), f'{path.name} is not mangled')
else:
self.assertTrue(is_mangled(path.name), f'{path.name} is mangled')
self.assertTrue(import_iknowpy_successful())

def test_no_mangle_all(self):
"""--no-mangle for all DLLs"""
check_call(['delvewheel', 'repair', '--add-path', 'iknowpy', '--no-mangle-all', 'iknowpy/iknowpy-1.5.3-cp312-cp312-win_amd64.whl'])
Expand All @@ -278,6 +291,13 @@ def test_strip_1(self):
check_call(['delvewheel', 'repair', '--add-path', 'iknowpy/trailing_data_1;iknowpy', '--strip', '--test', 'not_enough_padding', 'iknowpy/iknowpy-1.5.3-cp312-cp312-win_amd64.whl'])
self.assertTrue(import_iknowpy_successful())

def test_add_path_2(self):
"""--add-path specified twice"""
with self.assertRaises(subprocess.CalledProcessError):
check_call(['delvewheel', 'repair', '--add-path', 'iknowpy/trailing_data_1', '--add-path', 'iknowpy', '--test', 'not_enough_padding', 'iknowpy/iknowpy-1.5.3-cp312-cp312-win_amd64.whl'])
check_call(['delvewheel', 'repair', '--add-path', 'iknowpy/trailing_data_1;iknowpy', '--strip', '--test', 'not_enough_padding', 'iknowpy/iknowpy-1.5.3-cp312-cp312-win_amd64.whl'])
self.assertTrue(import_iknowpy_successful())

def test_strip_2(self):
"""--strip needed for 2 DLLs"""
with self.assertRaises(subprocess.CalledProcessError):
Expand Down Expand Up @@ -364,6 +384,27 @@ def test_include_2(self):
self.assertTrue(kernelbase_found, 'kernelbase.dll found')
self.assertTrue(import_iknowpy_successful())

def test_include_3(self):
"""--include for 2 DLLs, flag specified twice"""
check_call(['delvewheel', 'repair', '--add-path', 'iknowpy', '--include', 'kernel32.dll', '--include', 'kernelbase.dll', 'iknowpy/iknowpy-1.5.3-cp312-cp312-win_amd64.whl'])
kernel32_found = False
kernelbase_found = False
with zipfile.ZipFile('wheelhouse/iknowpy-1.5.3-cp312-cp312-win_amd64.whl') as wheel:
for path in zipfile.Path(wheel, 'iknowpy.libs/').iterdir():
if path.name in ('.load-order-iknowpy-1.5.3',):
continue
if path.name.lower().startswith('kernel32'):
self.assertFalse(is_mangled(path.name), f'{path.name} is not mangled')
kernel32_found = True
elif path.name.lower().startswith('kernelbase'):
self.assertFalse(is_mangled(path.name), f'{path.name} is not mangled')
kernelbase_found = True
else:
self.assertTrue(is_mangled(path.name), f'{path.name} is mangled')
self.assertTrue(kernel32_found, 'kernel32.dll found')
self.assertTrue(kernelbase_found, 'kernelbase.dll found')
self.assertTrue(import_iknowpy_successful())

def test_include_exclude_overlap(self):
"""overlap between --include and --exclude generates an error"""
with self.assertRaises(subprocess.CalledProcessError):
Expand Down Expand Up @@ -424,6 +465,14 @@ def test_exclude_all(self):
finally:
remove('wheelhouse/iknowpy-1.5.3-cp312-cp312-win_amd64.whl')

def test_exclude_all_2(self):
"""--exclude that removes all DLLs, flag specified twice"""
try:
output = subprocess.check_output(['delvewheel', 'repair', '--add-path', 'iknowpy', '--exclude', 'iKnowEngine.dll', '--exclude', 'msvcp140.dll', '--no-mangle-all', 'iknowpy/iknowpy-1.5.3-cp312-cp312-win_amd64.whl'], text=True)
self.assertIn('no external dependencies are needed', output)
finally:
remove('wheelhouse/iknowpy-1.5.3-cp312-cp312-win_amd64.whl')

def test_ignore_existing_irrelevant(self):
"""--ignore-existing when no DLLs are in the wheel"""
check_call(['delvewheel', 'repair', '--add-path', 'iknowpy', '--ignore-existing', '--no-mangle-all', 'iknowpy/iknowpy-1.5.3-cp312-cp312-win_amd64.whl'])
Expand Down

0 comments on commit af276c4

Please sign in to comment.