|
30 | 30 | import textwrap
|
31 | 31 | import time
|
32 | 32 | import threading
|
| 33 | +import sqlite3 |
| 34 | +import json |
33 | 35 |
|
34 | 36 | from ipykernel.kernelbase import Kernel
|
35 | 37 | from jupyter_client.jsonutil import squash_dates
|
@@ -494,6 +496,12 @@ def _install_packages(self, packages, swiftpm_flags):
|
494 | 496 | 'Install Error: Cannot install packages because '
|
495 | 497 | 'SWIFT_BUILD_PATH is not specified.')
|
496 | 498 |
|
| 499 | + swift_package_path = os.environ.get('SWIFT_PACKAGE_PATH') |
| 500 | + if swift_package_path is None: |
| 501 | + raise PackageInstallException( |
| 502 | + 'Install Error: Cannot install packages because ' |
| 503 | + 'SWIFT_PACKAGE_PATH is not specified.') |
| 504 | + |
497 | 505 | swift_import_search_path = os.environ.get('SWIFT_IMPORT_SEARCH_PATH')
|
498 | 506 | if swift_import_search_path is None:
|
499 | 507 | raise PackageInstallException(
|
@@ -591,27 +599,74 @@ def _install_packages(self, packages, swiftpm_flags):
|
591 | 599 |
|
592 | 600 | # == Copy .swiftmodule and modulemap files to SWIFT_IMPORT_SEARCH_PATH ==
|
593 | 601 |
|
594 |
| - for filename in glob.glob(os.path.join(bin_dir, '*.swiftmodule')): |
| 602 | + build_db_file = os.path.join(package_base_path, '.build', 'build.db') |
| 603 | + if not os.path.exists(build_db_file): |
| 604 | + raise PackageInstallException('build.db is missing') |
| 605 | + |
| 606 | + # Execute swift-package show-dependencies to get all dependencies' paths |
| 607 | + dependencies_result = subprocess.run( |
| 608 | + [swift_package_path, 'show-dependencies', '--format', 'json'], |
| 609 | + stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| 610 | + cwd=package_base_path) |
| 611 | + dependencies_json = dependencies_result.stdout.decode('utf8') |
| 612 | + dependencies_obj = json.loads(dependencies_json) |
| 613 | + |
| 614 | + def flatten_deps_paths(dep): |
| 615 | + paths = [] |
| 616 | + paths.append(dep["path"]) |
| 617 | + if dep["dependencies"]: |
| 618 | + for d in dep["dependencies"]: |
| 619 | + paths.extend(flatten_deps_paths(d)) |
| 620 | + return paths |
| 621 | + |
| 622 | + # Make list of paths where we expect .swiftmodule and .modulemap files of dependencies |
| 623 | + dependencies_paths = [package_base_path] |
| 624 | + dependencies_paths = flatten_deps_paths(dependencies_obj) |
| 625 | + dependencies_paths = list(set(dependencies_paths)) |
| 626 | + |
| 627 | + def is_valid_dependency(path): |
| 628 | + for p in dependencies_paths: |
| 629 | + if path.startswith(p): return True |
| 630 | + return False |
| 631 | + |
| 632 | + # Query to get build files list from build.db |
| 633 | + # SUBSTR because string starts with "N" (why?) |
| 634 | + SQL_FILES_SELECT = "SELECT SUBSTR(key, 2) FROM 'key_names' WHERE key LIKE ?" |
| 635 | + |
| 636 | + # Connect to build.db |
| 637 | + db_connection = sqlite3.connect(build_db_file) |
| 638 | + cursor = db_connection.cursor() |
| 639 | + |
| 640 | + # Process *.swiftmodules files |
| 641 | + cursor.execute(SQL_FILES_SELECT, ['%.swiftmodule']) |
| 642 | + swift_modules = [row[0] for row in cursor.fetchall() if is_valid_dependency(row[0])] |
| 643 | + for filename in swift_modules: |
595 | 644 | shutil.copy(filename, swift_import_search_path)
|
596 | 645 |
|
597 |
| - # The modulemap files appear in a few different places. Add all of |
598 |
| - # them. |
599 |
| - # TODO: Figure out if there is a principled way to figure out where |
600 |
| - # all the modulemap files are. |
601 |
| - modulemap_files = glob.glob( |
602 |
| - os.path.join(bin_dir, '**/module.modulemap'), recursive=True) |
603 |
| - modulemap_files += glob.glob( |
604 |
| - os.path.join(package_base_path, |
605 |
| - '.build/checkouts/**/module.modulemap'), |
606 |
| - recursive=True) |
| 646 | + # Process modulemap files |
| 647 | + cursor.execute(SQL_FILES_SELECT, ['%/module.modulemap']) |
| 648 | + modulemap_files = [row[0] for row in cursor.fetchall() if is_valid_dependency(row[0])] |
607 | 649 | for index, filename in enumerate(modulemap_files):
|
608 | 650 | # Create a separate directory for each modulemap file because the
|
609 | 651 | # ClangImporter requires that they are all named
|
610 | 652 | # "module.modulemap".
|
611 | 653 | modulemap_dest = os.path.join(swift_import_search_path,
|
612 | 654 | 'modulemap-%d' % index)
|
613 | 655 | os.makedirs(modulemap_dest, exist_ok=True)
|
614 |
| - shutil.copy(filename, modulemap_dest) |
| 656 | + src_folder, src_filename = os.path.split(filename) |
| 657 | + dst_path = os.path.join(modulemap_dest, src_filename) |
| 658 | + # Make all relative header paths in module.modulemap absolute |
| 659 | + # because we copy file to different location |
| 660 | + with open(filename, encoding='utf8') as file: |
| 661 | + modulemap_contents = file.read() |
| 662 | + modulemap_contents = re.sub( |
| 663 | + r'header\s+"(.*?)"', |
| 664 | + lambda m: 'header "%s"' % |
| 665 | + (m.group(1) if os.path.isabs(m.group(1)) else os.path.abspath(os.path.join(src_folder, m.group(1)))), |
| 666 | + modulemap_contents |
| 667 | + ) |
| 668 | + with open(dst_path, 'w', encoding='utf8') as outfile: |
| 669 | + outfile.write(modulemap_contents) |
615 | 670 |
|
616 | 671 | # == dlopen the shared lib ==
|
617 | 672 |
|
|
0 commit comments