|  | 
| 4 | 4 | # | 
| 5 | 5 | # Licensed under the Apache License, Version 2.0 (the "License"); | 
| 6 | 6 | # you may not use this file except in compliance with the License. | 
| 7 |  | -# You may obtain a copy of the License at | 
|  | 7 | +# You may | 
|  | 8 | +# obtain a copy of the License at | 
| 8 | 9 | # | 
| 9 | 10 | #      http://www.apache.org/licenses/LICENSE-2.0 | 
| 10 | 11 | # | 
|  | 
| 15 | 16 | # limitations under the License. | 
| 16 | 17 | 
 | 
| 17 | 18 | require 'xcodeproj' | 
|  | 19 | +require 'set' | 
| 18 | 20 | 
 | 
| 19 | 21 | # This script removes all Swift Package Manager dependencies from an Xcode project. | 
| 20 | 22 | # It's designed to be used in CI to prepare a project for framework-based testing. | 
|  | 
| 37 | 39 | 
 | 
| 38 | 40 | puts "Opened project: #{project.path}" | 
| 39 | 41 | 
 | 
| 40 |  | -# Remove package references from the project's root object. | 
| 41 |  | -# This corresponds to the "Package Dependencies" section in Xcode's navigator. | 
| 42 |  | -unless project.root_object.package_references.empty? | 
| 43 |  | -  puts "Removing #{project.root_object.package_references.count} package reference(s)..." | 
| 44 |  | -  project.root_object.package_references.clear | 
| 45 |  | -  puts "All package references removed from the project." | 
| 46 |  | -else | 
| 47 |  | -  puts "No package references found in the project." | 
|  | 42 | +# --- Step 1: Find all SPM product dependencies --- | 
|  | 43 | +package_product_dependencies = project.objects.select do |obj| | 
|  | 44 | +  obj.is_a?(Xcodeproj::Project::Object::XCSwiftPackageProductDependency) | 
| 48 | 45 | end | 
| 49 | 46 | 
 | 
| 50 |  | -# Remove package product dependencies from all targets. | 
| 51 |  | -# This removes the link to the package products in the "Frameworks, Libraries, | 
| 52 |  | -# and Embedded Content" section of each target. | 
| 53 |  | -project.targets.each do |target| | 
| 54 |  | -  dependencies_to_remove = target.dependencies.select do |dependency| | 
| 55 |  | -    dependency.is_a?(Xcodeproj::Project::Object::XCSwiftPackageProductDependency) | 
|  | 47 | +if package_product_dependencies.empty? | 
|  | 48 | +  puts "No SPM product dependencies found to remove." | 
|  | 49 | +else | 
|  | 50 | +  puts "Found #{package_product_dependencies.count} SPM product dependencies. Removing all references..." | 
|  | 51 | +  package_product_dep_uuids = package_product_dependencies.map(&:uuid).to_set | 
|  | 52 | + | 
|  | 53 | +  # --- Step 2: Find all BuildFile objects that reference these SPM products --- | 
|  | 54 | +  build_files_to_remove = project.objects.select do |obj| | 
|  | 55 | +    obj.is_a?(Xcodeproj::Project::Object::PBXBuildFile) && | 
|  | 56 | +    obj.product_ref && | 
|  | 57 | +    package_product_dep_uuids.include?(obj.product_ref.uuid) | 
| 56 | 58 |   end | 
|  | 59 | +  build_file_uuids_to_remove = build_files_to_remove.map(&:uuid).to_set | 
|  | 60 | + | 
|  | 61 | +  # --- Step 3: Remove references from all targets --- | 
|  | 62 | +  project.targets.each do |target| | 
|  | 63 | +    puts "Cleaning target '#{target.name}'..." | 
| 57 | 64 | 
 | 
| 58 |  | -  unless dependencies_to_remove.empty? | 
| 59 |  | -    puts "Found #{dependencies_to_remove.count} SPM product dependencies in target '#{target.name}'. Removing..." | 
| 60 |  | -    dependencies_to_remove.each do |dep| | 
| 61 |  | -        puts "Removing #{dep.product_name}" | 
| 62 |  | -        target.dependencies.delete(dep) | 
|  | 65 | +    # Remove from target dependencies list | 
|  | 66 | +    removed_deps = target.dependencies.reject! do |dep| | 
|  | 67 | +      package_product_dep_uuids.include?(dep.uuid) | 
|  | 68 | +    end | 
|  | 69 | +    if removed_deps | 
|  | 70 | +      puts "  - Removed #{removed_deps.count} SPM target dependencies." | 
|  | 71 | +    end | 
|  | 72 | + | 
|  | 73 | +    # Remove from build phases (e.g., "Link Binary With Libraries") | 
|  | 74 | +    target.build_phases.each do |phase| | 
|  | 75 | +      next unless phase.respond_to?(:files) | 
|  | 76 | +       | 
|  | 77 | +      original_file_count = phase.files.count | 
|  | 78 | +      phase.files.reject! do |build_file| | 
|  | 79 | +        build_file_uuids_to_remove.include?(build_file.uuid) | 
|  | 80 | +      end | 
|  | 81 | +      removed_count = original_file_count - phase.files.count | 
|  | 82 | +      if removed_count > 0 | 
|  | 83 | +        puts "  - Removed #{removed_count} SPM build file references from '#{phase.display_name}'." | 
|  | 84 | +      end | 
| 63 | 85 |     end | 
| 64 |  | -    puts "SPM product dependencies removed from target '#{target.name}'." | 
| 65 |  | -  else | 
| 66 |  | -    puts "No SPM product dependencies found in target '#{target.name}'." | 
| 67 | 86 |   end | 
|  | 87 | + | 
|  | 88 | +  # --- Step 4: Delete the now-orphaned BuildFile and dependency objects --- | 
|  | 89 | +  puts "Deleting #{build_files_to_remove.count} SPM BuildFile object(s)..." | 
|  | 90 | +  build_files_to_remove.each(&:remove_from_project) | 
|  | 91 | +   | 
|  | 92 | +  puts "Deleting #{package_product_dependencies.count} SPM product dependency object(s)..." | 
|  | 93 | +  package_product_dependencies.each(&:remove_from_project) | 
|  | 94 | +end | 
|  | 95 | + | 
|  | 96 | +# --- Step 5: Remove package references from the project root --- | 
|  | 97 | +unless project.root_object.package_references.empty? | 
|  | 98 | +  puts "Removing #{project.root_object.package_references.count} package reference(s)..." | 
|  | 99 | +  project.root_object.package_references.clear | 
|  | 100 | +  puts "All package references removed from the project." | 
|  | 101 | +else | 
|  | 102 | +  puts "No package references found in the project." | 
| 68 | 103 | end | 
| 69 | 104 | 
 | 
| 70 |  | -# Save the modified project. | 
|  | 105 | +# --- Step 6: Save the modified project --- | 
| 71 | 106 | begin | 
| 72 | 107 |   project.save | 
| 73 | 108 |   puts "Project saved successfully." | 
|  | 
0 commit comments