xcode-archive-cache 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f444a19221b84c55a31be3d82277eef653239bdec9299d65b1252c1874dae15
4
- data.tar.gz: 9f1927ba1c9bf360d83a3b4febf128b069b8d846eca236a8d8f9bd848de9af24
3
+ metadata.gz: 3c3de4b956fe76ce673329ecf9977a911d3c6bb3055c00e59c3e6903cb16e6b2
4
+ data.tar.gz: 92253d7316a2a716b71a053f182483e7af54aeb1662111175f3e071909d79998
5
5
  SHA512:
6
- metadata.gz: a0b85c53b4127976dfee59597aade6e6e7d0064841b8ea240446f9f7482bf3bd438a482cc3d75bd86caa917c040fe5e9f89f3c35e07291fdb8cd08d3f5b6b9e6
7
- data.tar.gz: c52f972d48a54b9331985d0b7a39f8e131a77a0e46c3cb8c0501422f313deb279b4ed9d0974fb99be7cd83f40a40feb374ae3d6f0feb73b5376089d7d574324a
6
+ metadata.gz: 88b0a7e44eecbdc426755761dd5df10fb0e1cff4a00c44f915df1f65515685ce3f2527d8d9b1173ea439bff032d49f4875f32a859291a702f829d378103c8234
7
+ data.tar.gz: c63650b58e921da5e38bb8648d0e6e3cfeddb55165e72f8984511c769a139b6ab3108350c0e684ed27ed9dbf9e02ef46135cc315a182ebc00586bb33c3fdae1f
@@ -9,7 +9,8 @@ module XcodeArchiveCache
9
9
  #
10
10
  def archive(path, destination)
11
11
  if File.exists?(destination)
12
- raise ArgumentError.new, "Artifact cache path #{destination} is already taken"
12
+ warn "Replacing artifact archive at path #{destination}"
13
+ FileUtils.rm_rf(destination)
13
14
  end
14
15
 
15
16
  if File.file?(path)
@@ -60,7 +60,8 @@ module XcodeArchiveCache
60
60
  state_file_path = archive_path + ".state"
61
61
 
62
62
  if File.exist?(state_file_path)
63
- raise ArgumentError.new, "State file already exists: #{state_file_path}"
63
+ warn "Replacing state file #{state_file_path}"
64
+ FileUtils.rm_f(state_file_path)
64
65
  end
65
66
 
66
67
  dependency_shas = node.dependencies
@@ -35,7 +35,7 @@ module XcodeArchiveCache
35
35
  # @param [XcodeArchiveCache::BuildGraph::Graph] graph
36
36
  #
37
37
  def should_rebuild?(graph)
38
- graph.nodes.reduce(false) {|rebuild, node| rebuild || node.waiting_for_rebuild}
38
+ graph.root_node.state != :unpacked
39
39
  end
40
40
 
41
41
  private
@@ -2,6 +2,8 @@ module XcodeArchiveCache
2
2
  module Build
3
3
  class ProductExtractor
4
4
 
5
+ include XcodeArchiveCache::Logs
6
+
5
7
  # @param [String] configuration
6
8
  # @param [String] derived_data_path
7
9
  #
@@ -17,7 +19,9 @@ module XcodeArchiveCache
17
19
  #
18
20
  def list_product_contents(built_node)
19
21
  file_paths = list_products(built_node)
20
- file_paths.select {|path| File.exist?(path)}.map {|path| File.realpath(path)}
22
+ file_paths
23
+ .select { |path| File.exist?(path) }
24
+ .map { |path| File.realpath(path) }
21
25
  end
22
26
 
23
27
  private
@@ -44,7 +48,7 @@ module XcodeArchiveCache
44
48
  elsif built_node.has_acceptable_product?
45
49
  list_single_product(built_node)
46
50
  else
47
- raise Informative, "#{built_node.name} has unsupported product type: #{built_node.native_target.product_type}"
51
+ raise XcodeArchiveCache::Informative, "#{built_node.name} has unsupported product type: #{built_node.native_target.product_type}"
48
52
  end
49
53
  end
50
54
 
@@ -56,7 +60,7 @@ module XcodeArchiveCache
56
60
  framework_glob = get_main_product_glob(built_node)
57
61
  framework_path = Dir.glob(framework_glob).first
58
62
  unless framework_path
59
- raise Informative, "Framework product not found for #{built_node.name}"
63
+ raise XcodeArchiveCache::Informative, "Framework product not found for #{built_node.name}"
60
64
  end
61
65
 
62
66
  framework_dsym_glob = File.join(File.dirname(framework_glob), built_node.dsym_file_name)
@@ -75,10 +79,42 @@ module XcodeArchiveCache
75
79
  product_glob = get_main_product_glob(built_node)
76
80
  product_path = Dir.glob(product_glob).first
77
81
  unless product_path
78
- raise Informative, "Product of type #{built_node.native_target.product_type} not found for #{built_node.name}"
82
+ raise XcodeArchiveCache::Informative, "Product of type #{built_node.native_target.product_type} not found for #{built_node.name}"
83
+ end
84
+
85
+ paths = [product_path]
86
+
87
+ # this one is generated during Swift compilation
88
+ # so we need to cache it as well
89
+ #
90
+ swift_objc_interface_header_glob = get_swift_objc_interface_header_glob(built_node)
91
+ swift_objc_interface_header_path = Dir.glob(swift_objc_interface_header_glob).first
92
+ if swift_objc_interface_header_path
93
+ debug("using Swift-ObjC interface header #{swift_objc_interface_header_path}")
94
+ paths << swift_objc_interface_header_path
95
+ end
96
+
97
+ swiftmodule_glob = get_swiftmodule_glob(built_node)
98
+ if swiftmodule_glob
99
+ swiftmodule_path = Dir.glob(swiftmodule_glob).first
100
+
101
+ if swiftmodule_path
102
+ debug("using swiftmodule #{swiftmodule_path}")
103
+ paths << swiftmodule_path
104
+ end
105
+ end
106
+
107
+ modulemap_glob = get_modulemap_glob(built_node)
108
+ if modulemap_glob
109
+ modulemap_path = Dir.glob(modulemap_glob).first
110
+
111
+ if modulemap_path
112
+ debug("using modulemap #{modulemap_path}")
113
+ paths << modulemap_path
114
+ end
79
115
  end
80
116
 
81
- [product_path]
117
+ paths
82
118
  end
83
119
 
84
120
  # @param [XcodeArchiveCache::BuildGraph::Node] built_node
@@ -89,9 +125,52 @@ module XcodeArchiveCache
89
125
  product_name = built_node.native_target.product_reference.name ?
90
126
  built_node.native_target.product_reference.name :
91
127
  built_node.native_target.product_reference.path
128
+ get_product_glob(File.basename(product_name))
129
+ end
130
+
131
+ # @param [XcodeArchiveCache::BuildGraph::Node] built_node
132
+ #
133
+ # @return [String]
134
+ #
135
+ def get_swift_objc_interface_header_glob(built_node)
136
+ get_product_glob(File.basename(built_node.swift_objc_interface_header_file))
137
+ end
138
+
139
+ # @param [XcodeArchiveCache::BuildGraph::Node] built_node
140
+ #
141
+ # @return [String]
142
+ #
143
+ def get_swiftmodule_glob(built_node)
144
+ if built_node.module_name
145
+ get_product_glob(built_node.module_name + ".swiftmodule")
146
+ end
147
+ end
148
+
149
+ # @param [XcodeArchiveCache::BuildGraph::Node] built_node
150
+ #
151
+ # @return [String]
152
+ #
153
+ def get_modulemap_glob(built_node)
154
+ resulting_modulemap_file_name = built_node.resulting_modulemap_file_name
155
+ if resulting_modulemap_file_name
156
+ get_product_glob(resulting_modulemap_file_name)
157
+ else
158
+ modulemap_file_path = built_node.original_modulemap_file_path
159
+ if modulemap_file_path && File.exist?(modulemap_file_path)
160
+ modulemap_file_name = File.basename(modulemap_file_path)
161
+ get_product_glob(modulemap_file_name)
162
+ end
163
+ end
164
+ end
165
+
166
+ # @param [String] file_name
167
+ #
168
+ # @return [String]
169
+ #
170
+ def get_product_glob(file_name)
92
171
  File.join(derived_data_path,
93
172
  "**",
94
- File.basename(product_name))
173
+ file_name)
95
174
  end
96
175
 
97
176
  # @param [String] framework_path
@@ -102,11 +181,11 @@ module XcodeArchiveCache
102
181
  executable_name = File.basename(framework_path, File.extname(framework_path))
103
182
  executable_path = File.join(framework_path, executable_name)
104
183
  unless File.exist?(executable_path)
105
- raise Informative, "Failed to find executable inside framework: #{framework_path}"
184
+ raise XcodeArchiveCache::Informative, "Failed to find executable inside framework: #{framework_path}"
106
185
  end
107
186
 
108
187
  uuids = list_bc_symbolmap_uuids(executable_path)
109
- uuids.map {|uuid| find_bc_symbolmap(uuid)}.flatten
188
+ uuids.map { |uuid| find_bc_symbolmap(uuid) }.flatten
110
189
  end
111
190
 
112
191
  # @param [String] executable_path
@@ -114,7 +193,11 @@ module XcodeArchiveCache
114
193
  # @return [Array<String>]
115
194
  #
116
195
  def list_bc_symbolmap_uuids(executable_path)
117
- shell_executor.execute_for_output("otool -l #{executable_path} | grep uuid | awk {'print $2'}").split("\n")
196
+ begin
197
+ shell_executor.execute_for_output("otool -l #{executable_path} | grep uuid | awk {'print $2'}").split("\n")
198
+ rescue
199
+ []
200
+ end
118
201
  end
119
202
 
120
203
  # @param [String] uuid
@@ -6,8 +6,8 @@ module XcodeArchiveCache
6
6
 
7
7
  # @param [XcodeArchiveCache::Xcodebuild::Executor] xcodebuild_executor
8
8
  #
9
- def initialize(native_target_finder, xcodebuild_executor)
10
- @build_settings_loader = XcodeArchiveCache::BuildSettings::Loader.new(xcodebuild_executor)
9
+ def initialize(native_target_finder, build_settings_loader)
10
+ @build_settings_loader = build_settings_loader
11
11
  @native_target_finder = native_target_finder
12
12
  @sha_calculator = NodeShaCalculator.new
13
13
  end
@@ -61,13 +61,17 @@ module XcodeArchiveCache
61
61
  display_name = target.display_name
62
62
  if target_stack.include?(display_name)
63
63
  target_stack.push(display_name)
64
- raise Informative, "Circular dependency detected: #{target_stack.join(" -> ")}"
64
+ raise XcodeArchiveCache::Informative, "Circular dependency detected: #{target_stack.join(" -> ")}"
65
65
  end
66
66
 
67
67
  node = ALL_NODES.select {|node| node.native_target.uuid == target.uuid && node.native_target.project == target.project}.first
68
68
  if node
69
69
  debug("already traversed this one")
70
- graph.add_multiple_nodes(node.subgraph) unless graph.nodes.include?(node)
70
+ unless graph.nodes.include?(node)
71
+ nodes_to_add = node.subgraph.select {|subgraph_node| !graph.nodes.include?(subgraph_node)}
72
+ graph.add_multiple_nodes(nodes_to_add)
73
+ end
74
+
71
75
  return node
72
76
  else
73
77
  debug("adding new node")
@@ -79,11 +83,7 @@ module XcodeArchiveCache
79
83
  dependencies = []
80
84
  target_stack.push(display_name)
81
85
 
82
- dependency_targets = target.dependencies.map {|dependency| native_target_finder.find_for_dependency(dependency)} +
83
- target.frameworks_build_phase.files.map {|file| native_target_finder.find_for_file(file)}
84
-
85
- # PBXNativeTarget has no custom equality check
86
- deduplicated_targets = dependency_targets.compact.uniq {|dependency_target| dependency_target.uuid + dependency_target.display_name}
86
+ deduplicated_targets = native_target_finder.find_native_dependencies(target)
87
87
  debug("dependency targets: #{deduplicated_targets.map(&:display_name)}")
88
88
 
89
89
  deduplicated_targets.each do |dependency_target|
@@ -141,7 +141,7 @@ module XcodeArchiveCache
141
141
  info("getting settings for #{target.display_name}")
142
142
  build_settings = build_settings_loader.get_settings(target.project.path, target.display_name)
143
143
  unless build_settings
144
- raise Informative, "No build settings loaded for #{target.display_name}"
144
+ raise XcodeArchiveCache::Informative, "No build settings loaded for #{target.display_name}"
145
145
  end
146
146
 
147
147
  build_settings
@@ -3,9 +3,14 @@ module XcodeArchiveCache
3
3
  class NativeTargetFinder
4
4
 
5
5
  # @param [Array<Xcodeproj::Project>] projects
6
+ # @param [String] build_configuration_name
6
7
  #
7
- def initialize(projects)
8
+ def initialize(projects, build_configuration_name)
8
9
  @all_targets = extract_targets(projects)
10
+ @build_configuration_name = build_configuration_name
11
+ @interpolator = XcodeArchiveCache::BuildSettings::StringInterpolator.new
12
+
13
+ setup_product_name_to_target_mapping
9
14
  end
10
15
 
11
16
  # @param [Array<Xcodeproj::Project>] projects
@@ -14,30 +19,67 @@ module XcodeArchiveCache
14
19
  #
15
20
  def extract_targets(projects)
16
21
  projects
17
- .map {|project| unnest(project)}
18
- .flatten
19
- .uniq
20
- .map(&:native_targets)
21
- .flatten
22
- .select {|target| !target.test_target_type?}
22
+ .map {|project| unnest(project)}
23
+ .flatten
24
+ .sort_by(&:path)
25
+ .inject([]) {|unique, current| unique.last && unique.last.path == current.path ? unique : unique + [current]}
26
+ .map(&:native_targets)
27
+ .flatten
28
+ .select {|target| !target.test_target_type?}
23
29
  end
24
30
 
25
31
  # @param [String] platform_name
26
32
  #
33
+ # @return [Array<Xcodeproj::Project::Object::PBXNativeTarget>]
34
+ #
27
35
  def set_platform_name_filter(platform_name)
28
36
  @platform_name = platform_name
29
37
  end
30
38
 
39
+ # @param [Xcodeproj::Project::Object::PBXNativeTarget] target
40
+ #
41
+ # @return [Array<Xcodeproj::Project::Object::PBXNativeTarget>]
42
+ #
43
+ def find_native_dependencies(target)
44
+ direct_dependencies = target
45
+ .dependencies
46
+ .map {|dependency| find_for_dependency(dependency)}
47
+ linked_dependencies = find_linked_dependencies(target)
48
+ join(direct_dependencies, linked_dependencies)
49
+ end
50
+
51
+ # @param [Xcodeproj::Project::Object::PBXAbstractTarget] target
52
+ #
53
+ # @return [Array<Xcodeproj::Project::Object::PBXAbstractTarget>]
54
+ #
55
+ def find_all_dependencies(target)
56
+ direct_dependencies = target
57
+ .dependencies
58
+ .map {|dependency| find_any_for_dependency(dependency)}
59
+ linked_dependencies = []
60
+
61
+ if target.is_a?(Xcodeproj::Project::Object::PBXNativeTarget)
62
+ linked_dependencies = find_linked_dependencies(target)
63
+ end
64
+
65
+ join(direct_dependencies, linked_dependencies)
66
+ end
67
+
31
68
  # @param [Xcodeproj::Project::Object::PBXTargetDependency] dependency
32
69
  #
33
70
  # @return [Xcodeproj::Project::Object::PBXNativeTarget]
34
71
  #
35
72
  def find_for_dependency(dependency)
36
73
  # targets from embedded projects are proxied
37
- target = dependency.target ? dependency.target : dependency.target_proxy.proxied_object
74
+ target = find_any_for_dependency(dependency)
38
75
  target.is_a?(Xcodeproj::Project::Object::PBXNativeTarget) ? target : nil
39
76
  end
40
77
 
78
+ def find_any_for_dependency(dependency)
79
+ target = dependency.target ? dependency.target : dependency.target_proxy.proxied_object
80
+ target && target.platform_name == platform_name ? target : nil
81
+ end
82
+
41
83
  # @param [Xcodeproj::Project::Object::PBXBuildFile] file
42
84
  #
43
85
  # @return [Xcodeproj::Project::Object::PBXNativeTarget]
@@ -60,7 +102,7 @@ module XcodeArchiveCache
60
102
  end
61
103
 
62
104
  if target == nil
63
- raise Informative, "Target for #{file.file_ref.path} not found"
105
+ raise XcodeArchiveCache::Informative, "Target for #{file.file_ref.path} not found"
64
106
  end
65
107
 
66
108
  target
@@ -68,7 +110,7 @@ module XcodeArchiveCache
68
110
  # products of sibling project targets are added as PBXFileReferences
69
111
  targets = find_with_product_path(file.file_ref.path)
70
112
  if targets.length > 1
71
- raise Informative, "Found more than one target with product #{File.basename(file.file_ref.path)} in:\n#{targets.map(&:project)}"
113
+ raise XcodeArchiveCache::Informative, "Found more than one target with product #{File.basename(file.file_ref.path)} in:\n#{targets.map(&:project)}"
72
114
  end
73
115
 
74
116
  targets.first
@@ -77,9 +119,16 @@ module XcodeArchiveCache
77
119
 
78
120
  # @param [String] product_name
79
121
  #
122
+ # @return [Xcodeproj::Project::Object::PBXNativeTarget]
123
+ #
80
124
  def find_for_product_name(product_name)
81
- all_targets.select {|native_target| native_target.name == product_name || native_target.product_reference.display_name == product_name}
82
- .first
125
+ canonical = all_targets
126
+ .select {|native_target| native_target.name == product_name || native_target.product_reference.display_name == product_name}
127
+ .first
128
+
129
+ parsed = @product_name_to_target[product_name]
130
+
131
+ canonical ? canonical : parsed
83
132
  end
84
133
 
85
134
  private
@@ -92,6 +141,36 @@ module XcodeArchiveCache
92
141
  #
93
142
  attr_accessor :platform_name
94
143
 
144
+ # @return [String]
145
+ #
146
+ attr_reader :build_configuration_name
147
+
148
+ def setup_product_name_to_target_mapping
149
+ @product_name_to_target = Hash.new
150
+
151
+ @all_targets.each do |target|
152
+ build_settings = target.find_build_configuration(build_configuration_name, raise_if_not_found: false)&.build_settings
153
+ next unless build_settings
154
+
155
+ full_settings = build_settings
156
+ full_settings[XcodeArchiveCache::BuildSettings::TARGET_NAME_KEY] = target.name
157
+ product_name = @interpolator.interpolate(build_settings[XcodeArchiveCache::BuildSettings::PRODUCT_NAME_KEY], full_settings)
158
+
159
+ next if product_name == nil
160
+
161
+ product_name_extension = ""
162
+ case target.product_type
163
+ when Xcodeproj::Constants::PRODUCT_TYPE_UTI[:framework]
164
+ product_name_extension = ".framework"
165
+ when Xcodeproj::Constants::PRODUCT_TYPE_UTI[:static_library]
166
+ product_name_extension = ".a"
167
+ end
168
+
169
+ full_product_name = "#{product_name}#{product_name_extension}"
170
+ @product_name_to_target[full_product_name] = target
171
+ end
172
+ end
173
+
95
174
  # @param [Xcodeproj::Project] project
96
175
  #
97
176
  # @return [Array<Xcodeproj::Project>]
@@ -106,6 +185,28 @@ module XcodeArchiveCache
106
185
  [project] + nested_projects + subnested_projects
107
186
  end
108
187
 
188
+ # @param [Xcodeproj::Project::Object::PBXNativeTarget] target
189
+ #
190
+ # @return [Array<Xcodeproj::Project::Object::PBXNativeTarget>]
191
+ #
192
+ def find_linked_dependencies(target)
193
+ target
194
+ .frameworks_build_phase
195
+ .files
196
+ .map {|file| find_for_file(file)}
197
+ end
198
+
199
+ # @param [Array<Xcodeproj::Project::Object::PBXAbstractTarget>] direct_dependencies
200
+ # @params [Array<Xcodeproj::Project::Object::PBXNativeTarget>] linked_dependencies
201
+ #
202
+ # @return [Array<Xcodeproj::Project::Object::PBXAbstractTarget>]
203
+ #
204
+ def join(direct_dependencies, linked_dependencies)
205
+ (direct_dependencies + linked_dependencies)
206
+ .compact
207
+ .uniq(&:equatable_identifier)
208
+ end
209
+
109
210
  # @param [String] uuid
110
211
  #
111
212
  # @return [Xcodeproj::Project::Object::PBXNativeTarget]
@@ -128,7 +229,16 @@ module XcodeArchiveCache
128
229
  # @return [Array<Xcodeproj::Project::Object::PBXNativeTarget>]
129
230
  #
130
231
  def find_with_product_path(path)
131
- all_targets.select {|target| target.platform_name == platform_name && target.product_reference.path == path}
232
+ canonical = all_targets.select {|target| target.platform_name == platform_name && target.product_reference.path == path }
233
+ parsed = @product_name_to_target[File.basename(path)]
234
+
235
+ if canonical.length > 0
236
+ canonical
237
+ elsif parsed
238
+ [parsed]
239
+ else
240
+ []
241
+ end
132
242
  end
133
243
  end
134
244
  end