xcode-archive-cache 0.0.9 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
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