xcode-archive-cache 0.0.10.pre.2 → 0.0.10

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: de08375420b8d97642ead0ec3ebc63a78f9f2c372347038054f3b66927d7cad4
4
- data.tar.gz: 3712950c9e6038a5193a0db108127a5e89cb29edea098929e527346db24d838c
3
+ metadata.gz: ff86c853bc7b50f265964695cd55edd14821c821f5d27a1f916dcc23ccc1c4b4
4
+ data.tar.gz: a11e6dd9c98f59526f6f5329aab00acdcc58b5d7e2b65d83b6a1348d647a2323
5
5
  SHA512:
6
- metadata.gz: 8e6f791d1a84b4835981617e9092e5a24db3beaa6ee3119b351c47ff99cbed1a222c0168659e5b2bc89c3bc999fb57fd47611c0eb6daf601ef50985ef4959469
7
- data.tar.gz: 588243bf92bf878d5020296f69252a83bc99eb58da5150237b62881a9e023539f6365e630e191b1f39bfc6681dbfeda87d321ce915459b1f624280e65c6ce3af
6
+ metadata.gz: a71f194f4af63d93ee4f256977eb98525065599a36f270215c5c0f3a18cb8742957bb8f2e407497a7629f05ba18b9532d093d02b1dec305fcf4f82f815473431
7
+ data.tar.gz: ba25058d81d37e12594422d9a765d07fb8af192b53fe980ca3b4da3e1eac61f5a0bc69454f406be92094742e7809dbe8f4887d33e48b305c869733e5c581a4d8
@@ -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
@@ -83,8 +87,32 @@ module XcodeArchiveCache
83
87
  # this one is generated during Swift compilation
84
88
  # so we need to cache it as well
85
89
  #
86
- swift_objc_interface_header_path = built_node.swift_objc_interface_header_path
87
- paths << swift_objc_interface_header_path if swift_objc_interface_header_path
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
115
+ end
88
116
 
89
117
  paths
90
118
  end
@@ -97,9 +125,52 @@ module XcodeArchiveCache
97
125
  product_name = built_node.native_target.product_reference.name ?
98
126
  built_node.native_target.product_reference.name :
99
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)
100
171
  File.join(derived_data_path,
101
172
  "**",
102
- File.basename(product_name))
173
+ file_name)
103
174
  end
104
175
 
105
176
  # @param [String] framework_path
@@ -67,7 +67,11 @@ module XcodeArchiveCache
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")
@@ -14,12 +14,13 @@ module XcodeArchiveCache
14
14
  #
15
15
  def extract_targets(projects)
16
16
  projects
17
- .map {|project| unnest(project)}
18
- .flatten
19
- .uniq
20
- .map(&:native_targets)
21
- .flatten
22
- .select {|target| !target.test_target_type?}
17
+ .map {|project| unnest(project)}
18
+ .flatten
19
+ .sort_by(&:path)
20
+ .inject([]) {|unique, current| unique.last && unique.last.path == current.path ? unique : unique + [current]}
21
+ .map(&:native_targets)
22
+ .flatten
23
+ .select {|target| !target.test_target_type?}
23
24
  end
24
25
 
25
26
  # @param [String] platform_name
@@ -34,6 +34,10 @@ module XcodeArchiveCache
34
34
  #
35
35
  attr_accessor :build_settings
36
36
 
37
+ # @return [Array<Xcodeproj::Project::Object::PBXNativeTarget>]
38
+ #
39
+ attr_reader :targets_injected_to
40
+
37
41
  # @param [String] name
38
42
  # @param [Xcodeproj::Project::Object::PBXNativeTarget] native_target
39
43
  # @param [Boolean] is_root
@@ -44,6 +48,7 @@ module XcodeArchiveCache
44
48
  @is_root = is_root
45
49
  @dependent = []
46
50
  @dependencies = []
51
+ @targets_injected_to = []
47
52
  @state = :unknown
48
53
  end
49
54
 
@@ -91,8 +96,8 @@ module XcodeArchiveCache
91
96
 
92
97
  # @return [String]
93
98
  #
94
- def modulemap_file_path
95
- modulemap_file = build_settings[XcodeArchiveCache::BuildSettings::MODULEMAP_FILE_KEY]
99
+ def original_modulemap_file_path
100
+ modulemap_file = modulemap_file_name
96
101
  return unless modulemap_file
97
102
 
98
103
  Pathname.new(modulemap_file).absolute? ? modulemap_file : File.join(File.dirname(native_target.project.path), modulemap_file)
@@ -100,11 +105,12 @@ module XcodeArchiveCache
100
105
 
101
106
  # @return [String]
102
107
  #
103
- def swift_objc_interface_header_path
104
- header_file = swift_objc_interface_header_file
105
- return if header_file == nil
106
-
107
- File.join(build_settings[XcodeArchiveCache::BuildSettings::DERIVED_SOURCES_DIR_KEY], header_file)
108
+ def resulting_modulemap_file_name
109
+ if module_name
110
+ module_name + ".modulemap"
111
+ else
112
+ File.basename(modulemap_file_name)
113
+ end
108
114
  end
109
115
 
110
116
  # @return [String]
@@ -127,6 +133,12 @@ module XcodeArchiveCache
127
133
  build_settings[XcodeArchiveCache::BuildSettings::PRODUCT_MODULE_NAME_KEY]
128
134
  end
129
135
 
136
+ # @return [String]
137
+ #
138
+ def modulemap_file_name
139
+ build_settings[XcodeArchiveCache::BuildSettings::MODULEMAP_FILE_KEY]
140
+ end
141
+
130
142
  # @return [Array<Node>]
131
143
  # Direct + transitive dependents
132
144
  #
@@ -2,6 +2,10 @@ module XcodeArchiveCache
2
2
  module BuildGraph
3
3
  class NodeShaCalculator
4
4
 
5
+ def initialize
6
+ @own_sha = calculate_own_sources_sha
7
+ end
8
+
5
9
  # @param [XcodeArchiveCache::BuildGraph::Node] node
6
10
  #
7
11
  def calculate(node)
@@ -24,6 +28,20 @@ module XcodeArchiveCache
24
28
 
25
29
  private
26
30
 
31
+ # @return [String]
32
+ #
33
+ attr_reader :own_sha
34
+
35
+ # @return [String]
36
+ #
37
+ def calculate_own_sources_sha
38
+ root = Pathname.new(File.expand_path('../', File.dirname(__FILE__)))
39
+ source_file_glob = File.join(root.realpath.to_s, "**", "*.rb")
40
+ source_file_paths = Dir.glob(source_file_glob)
41
+
42
+ calculate_sha(source_file_paths)
43
+ end
44
+
27
45
  # @param [XcodeArchiveCache::BuildGraph::Node] node
28
46
  #
29
47
  # @return [Array<String>]
@@ -36,7 +54,7 @@ module XcodeArchiveCache
36
54
  inputs << list_build_phase_inputs(build_phase)
37
55
  end
38
56
 
39
- modulemap_file_path = node.modulemap_file_path
57
+ modulemap_file_path = node.original_modulemap_file_path
40
58
  inputs << modulemap_file_path if modulemap_file_path
41
59
 
42
60
  # file path order should not affect evaluation result
@@ -74,7 +92,7 @@ module XcodeArchiveCache
74
92
  # @param [Array<String>] dependency_shas
75
93
  #
76
94
  def save_auxiliary_data(build_settings, dependency_shas, tempfile)
77
- file_contents = build_settings + dependency_shas.join("\n")
95
+ file_contents = build_settings + dependency_shas.join("\n") + "\nXCODE-ARCHIVE-CACHE: #{own_sha}\n"
78
96
  tempfile << file_contents
79
97
  tempfile.flush
80
98
  end
@@ -7,9 +7,9 @@ module XcodeArchiveCache
7
7
  # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
8
8
  # @param [String] path
9
9
  #
10
- def add_framework_search_path(build_configuration, path)
10
+ def replace_or_add_framework_search_path(build_configuration, target_name, path)
11
11
  debug("using framework search path #{path}")
12
- add_flag_to_configuration(build_configuration, FRAMEWORK_SEARCH_PATHS_KEY, path_to_search_path(path))
12
+ replace_or_add_flag(build_configuration, [FRAMEWORK_SEARCH_PATHS_KEY], nil, [target_name], path_to_search_path(path), true)
13
13
  end
14
14
 
15
15
  # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
@@ -44,8 +44,17 @@ module XcodeArchiveCache
44
44
  add_libtool_flag(build_configuration, flag)
45
45
  end
46
46
  end
47
+
48
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
49
+ # @param [String] path
50
+ #
51
+ def add_swift_include_path(build_configuration, path)
52
+ debug("adding #{path} to SWIFT_INCLUDE_PATHS")
53
+ add_flag_to_configuration(build_configuration, SWIFT_INCLUDE_PATHS_KEY, path_to_search_path(path))
54
+ end
47
55
 
48
56
  # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
57
+ # @param [String] artifact_location
49
58
  # @param [XcodeArchiveCache::BuildGraph::Node] node
50
59
  #
51
60
  def add_framework_headers_iquote(build_configuration, artifact_location, node)
@@ -57,9 +66,9 @@ module XcodeArchiveCache
57
66
  # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
58
67
  # @param [String] path
59
68
  #
60
- def add_library_search_path(build_configuration, path)
69
+ def replace_or_add_library_search_path(build_configuration, target_name, path)
61
70
  debug("using library search path #{path}")
62
- add_flag_to_configuration(build_configuration, LIBRARY_SEARCH_PATHS_KEY, path_to_search_path(path))
71
+ replace_or_add_flag(build_configuration, [LIBRARY_SEARCH_PATHS_KEY], nil, [target_name], path_to_search_path(path), true)
63
72
  end
64
73
 
65
74
  # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
@@ -87,24 +96,14 @@ module XcodeArchiveCache
87
96
  end
88
97
 
89
98
  # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
99
+ # @param [Array<String>] old_modulemap_names
90
100
  # @param [String] path
91
101
  #
92
102
  def fix_module_map_path(build_configuration, old_modulemap_names, path)
93
- replace_module_map_flag(build_configuration.build_settings, OTHER_CFLAGS_KEY, old_modulemap_names, path)
94
- replace_module_map_flag(build_configuration.build_settings, OTHER_CPLUSPLUSFLAGS_KEY, old_modulemap_names, path)
95
- replace_module_map_flag(build_configuration.build_settings, OTHER_SWIFT_FLAGS_KEY, old_modulemap_names, path)
103
+ debug("using #{path}")
96
104
 
97
- if build_configuration.base_configuration_reference
98
- xcconfig_path = build_configuration.base_configuration_reference.real_path
99
- return unless File.exist?(xcconfig_path)
100
- xcconfig = Xcodeproj::Config.new(xcconfig_path)
101
-
102
- replace_module_map_flag(xcconfig.attributes, OTHER_CFLAGS_KEY, old_modulemap_names, path)
103
- replace_module_map_flag(xcconfig.attributes, OTHER_CPLUSPLUSFLAGS_KEY, old_modulemap_names, path)
104
- replace_module_map_flag(xcconfig.attributes, OTHER_SWIFT_FLAGS_KEY, old_modulemap_names, path)
105
-
106
- xcconfig.save_as(Pathname.new(xcconfig_path))
107
- end
105
+ settings_with_modulemaps = [OTHER_CFLAGS_KEY, OTHER_CPLUSPLUSFLAGS_KEY, OTHER_SWIFT_FLAGS_KEY]
106
+ replace_or_add_flag(build_configuration, settings_with_modulemaps, MODULE_MAP_FLAG, old_modulemap_names, path_to_search_path(path), false)
108
107
  end
109
108
 
110
109
  private
@@ -117,8 +116,11 @@ module XcodeArchiveCache
117
116
  OTHER_LDFLAGS_KEY = "OTHER_LDFLAGS"
118
117
  OTHER_LIBTOOLFLAGS_KEY = "OTHER_LIBTOOLFLAGS"
119
118
  OTHER_SWIFT_FLAGS_KEY = "OTHER_SWIFT_FLAGS"
119
+ SWIFT_INCLUDE_PATHS_KEY = "SWIFT_INCLUDE_PATHS"
120
120
  INHERITED_SETTINGS_VALUE = "$(inherited)"
121
121
 
122
+ MODULE_MAP_FLAG = "-fmodule-map-file="
123
+
122
124
  # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
123
125
  # @param [String] flag
124
126
  #
@@ -233,50 +235,106 @@ module XcodeArchiveCache
233
235
  node.product_file_name.gsub(/^lib/, "-l").gsub(/\.a$/, "")
234
236
  end
235
237
 
236
- # @param [Hash] build_settings
237
- # @param [String] flags_key
238
- # @param [Array<String>] old_modulemap_names
239
- # @param [String] path
238
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
239
+ # @param [Array<String>] setting_keys
240
+ # @param [String] flag_name
241
+ # @param [Array<String>] possible_old_values
242
+ # @param [String] new_value
240
243
  #
241
- def replace_module_map_flag(build_settings, flags_key, old_modulemap_names, path)
242
- flags = build_settings[flags_key]
243
- if flags
244
- build_settings[flags_key] = replace_module_map_path(flags, old_modulemap_names, path)
244
+ def replace_or_add_flag(build_configuration, setting_keys, flag_name, possible_old_values, new_value, add_if_missing)
245
+ replaced = false
246
+
247
+ setting_keys.each do |setting|
248
+ replaced = replace_flag_value(build_configuration.build_settings, setting, flag_name, possible_old_values, new_value) || replaced
245
249
  end
246
- end
247
250
 
248
- MODULE_MAP_FLAG = "-fmodule-map-file="
251
+ if build_configuration.base_configuration_reference
252
+ xcconfig_path = build_configuration.base_configuration_reference.real_path
253
+ project_dir = File.dirname(build_configuration.project.path)
249
254
 
250
- # @param [String] flags
251
- # @param [Array<String>] old_modulemap_names
252
- # @param [String] path
255
+ replaced = replace_flag_value_in_xcconfig_recursively(xcconfig_path, project_dir, setting_keys, flag_name, possible_old_values, new_value) || replaced
256
+ end
257
+
258
+ if !replaced && add_if_missing
259
+ full_value = get_full_flag_value(flag_name, new_value)
260
+
261
+ setting_keys.each do |setting|
262
+ add_flag_to_configuration(build_configuration, setting, full_value)
263
+ end
264
+ end
265
+ end
266
+
267
+ # @param [String] xcconfig_path
268
+ # @param [String] project_dir
269
+ # @param [Array<String>] setting_keys
270
+ # @param [String] flag_name
271
+ # @param [Array<String>] possible_old_values
272
+ # @param [String] new_value
253
273
  #
254
- # @return [String]
274
+ def replace_flag_value_in_xcconfig_recursively(xcconfig_path, project_dir, setting_keys, flag_name, possible_old_values, new_value)
275
+ debug("changing #{possible_old_values} to #{new_value} in #{File.basename(xcconfig_path)}")
276
+ return unless File.exist?(xcconfig_path)
277
+
278
+ replaced = false
279
+ xcconfig = Xcodeproj::Config.new(xcconfig_path)
280
+
281
+ setting_keys.each do |key|
282
+ replaced = replace_flag_value(xcconfig.attributes, key, flag_name, possible_old_values, new_value) || replaced
283
+ end
284
+
285
+ xcconfig.save_as(Pathname.new(xcconfig_path))
286
+
287
+ xcconfig.includes.each do |included_xcconfig|
288
+ included_xcconfig_path = File.join(project_dir, included_xcconfig)
289
+ replaced = replace_flag_value_in_xcconfig_recursively(included_xcconfig_path, project_dir, setting_keys, flag_name, possible_old_values, new_value) || replaced
290
+ end
291
+
292
+ replaced
293
+ end
294
+
295
+ # @param [Hash] attributes
296
+ # @param [String] setting_key
297
+ # @param [String] flag_name
298
+ # @param [Array<String>] possible_old_values
299
+ # @param [String] new_value
255
300
  #
256
- def replace_module_map_path(flags, old_modulemap_names, path)
257
- return if flags == nil
301
+ def replace_flag_value(attributes, setting_key, flag_name, possible_old_values, new_value)
302
+ build_settings = attributes[setting_key]
303
+ return unless build_settings
258
304
 
259
- is_flags_string = flags.is_a?(String)
260
- flags = flags.split(" ") if is_flags_string
261
- updated_flags = flags
262
- .map { |flags_line| flags_line.split(" ") }
305
+ replaced = false
306
+ is_string = build_settings.is_a?(String)
307
+ build_settings = build_settings.split(" ") if is_string
308
+ full_value = get_full_flag_value(flag_name, new_value)
309
+ old_value_regexps = possible_old_values.map { |value| Regexp.new("#{value}\"*$") }
310
+
311
+ updated_settings = build_settings
312
+ .map { |line| line.split(" ") }
263
313
  .flatten
264
314
  .map do |line|
315
+ if flag_name
316
+ next line unless line.include?(flag_name)
317
+ end
318
+
265
319
  updated_line = line
266
320
 
267
- if line.include?(MODULE_MAP_FLAG)
268
- old_modulemap_names.each do |name|
269
- if line.include?(name)
270
- updated_line = "#{MODULE_MAP_FLAG}\"#{path}\""
271
- break
272
- end
321
+ old_value_regexps.each do |regexp|
322
+ if regexp.match?(line)
323
+ replaced = true
324
+ updated_line = full_value
325
+ break
273
326
  end
274
327
  end
275
328
 
276
329
  updated_line
277
330
  end
278
331
 
279
- is_flags_string ? updated_flags.join(" ") : updated_flags
332
+ attributes[setting_key] = is_string ? updated_settings.join(" ") : updated_settings
333
+ replaced
334
+ end
335
+
336
+ def get_full_flag_value(flag_name, value)
337
+ "#{flag_name}#{value}"
280
338
  end
281
339
  end
282
340
  end
@@ -9,6 +9,7 @@ module XcodeArchiveCache
9
9
  def initialize(storage)
10
10
  @storage = storage
11
11
  @build_settings_interpolator = XcodeArchiveCache::BuildSettings::StringInterpolator.new
12
+ @modulemap_header_path_extractor = XcodeArchiveCache::Modulemap::HeaderPathExtractor.new
12
13
  end
13
14
 
14
15
  # @param [XcodeArchiveCache::BuildGraph::Node] node
@@ -33,16 +34,22 @@ module XcodeArchiveCache
33
34
  headers_file_paths = node.native_target
34
35
  .headers_build_phase
35
36
  .files
37
+ .select { |file| file.settings && file.settings["ATTRIBUTES"].include?("Public") }
36
38
  .map { |header| get_real_path(header) }
37
39
  .uniq
38
40
  storage.store_default_headers(node, headers_file_paths)
39
41
 
40
42
  header_count += headers_file_paths.length
43
+ end
41
44
 
42
- storage.store_modulemap(node)
45
+ modulemap_file_path = node.original_modulemap_file_path
46
+ if modulemap_file_path && File.exist?(modulemap_file_path)
47
+ header_file_paths = modulemap_header_path_extractor.extract_all_paths(modulemap_file_path)
48
+ storage.store_modulemap_headers(node, header_file_paths)
49
+ header_count += header_file_paths.length
43
50
  end
44
51
 
45
- debug("found #{header_count} headers")
52
+ debug("found #{header_count} header(s)")
46
53
  end
47
54
 
48
55
  private
@@ -55,6 +62,10 @@ module XcodeArchiveCache
55
62
  #
56
63
  attr_reader :build_settings_interpolator
57
64
 
65
+ # @return [XcodeArchiveCache::Modulemap::HeaderPathExtractor]
66
+ #
67
+ attr_reader :modulemap_header_path_extractor
68
+
58
69
  # @param [Xcodeproj::Project::Object::PBXBuildFile] build_file
59
70
  #
60
71
  # @return [String]
@@ -14,14 +14,14 @@ module XcodeArchiveCache
14
14
  @dependency_remover = DependencyRemover.new
15
15
  @build_flags_changer = BuildFlagsChanger.new
16
16
  @pods_fixer = PodsScriptFixer.new
17
- @modulemap_fixer = ModulemapFixer.new(storage)
17
+ @modulemap_fixer = XcodeArchiveCache::Modulemap::HeaderPathFixer.new(storage)
18
18
  @framework_embedder = FrameworkEmbedder.new
19
19
  end
20
20
 
21
21
  # @param [XcodeArchiveCache::BuildGraph::Graph] graph
22
22
  #
23
23
  def perform_internal_injection(graph)
24
- inject_unpacked(graph.nodes)
24
+ inject_unpacked_and_rebuilt(graph.nodes)
25
25
  add_header_paths(graph.nodes)
26
26
  save_graph_projects(graph)
27
27
  end
@@ -103,13 +103,18 @@ module XcodeArchiveCache
103
103
 
104
104
  # @param [Array<XcodeArchiveCache::BuildGraph::Node>] nodes
105
105
  #
106
- def inject_unpacked(nodes)
106
+ def inject_unpacked_and_rebuilt(nodes)
107
107
  cached_nodes = nodes.select { |node| node.state == :unpacked }
108
108
  cached_nodes.each do |node|
109
109
  headers_mover.prepare_headers_for_injection(node)
110
110
  modulemap_fixer.fix_modulemap(node)
111
111
  add_as_prebuilt_to_dependents(node)
112
112
  end
113
+
114
+ built_nodes = nodes.select { |node| node.state == :rebuilt_and_cached }
115
+ built_nodes.each do |node|
116
+ add_as_prebuilt_to_dependents(node)
117
+ end
113
118
  end
114
119
 
115
120
  # @param [Array<XcodeArchiveCache::BuildGraph::Node>] nodes
@@ -164,6 +169,9 @@ module XcodeArchiveCache
164
169
  # @param [Xcodeproj::Project::Object::PBXNativeTarget] dependent_target
165
170
  #
166
171
  def add_as_prebuilt_dependency(prebuilt_node, dependent_target)
172
+ target_identifier = get_target_identifier(dependent_target)
173
+ return if prebuilt_node.targets_injected_to.include?(target_identifier)
174
+
167
175
  debug("adding #{prebuilt_node.name} as prebuilt to #{dependent_target.display_name}")
168
176
 
169
177
  unless prebuilt_node.has_acceptable_product?
@@ -176,6 +184,8 @@ module XcodeArchiveCache
176
184
  add_as_prebuilt_static_lib(prebuilt_node, dependent_target)
177
185
  end
178
186
 
187
+ prebuilt_node.targets_injected_to.push(target_identifier)
188
+
179
189
  debug("done with #{prebuilt_node.name} for #{dependent_target.display_name}")
180
190
  end
181
191
 
@@ -186,7 +196,7 @@ module XcodeArchiveCache
186
196
  build_configuration = find_build_configuration(dependent_target)
187
197
 
188
198
  artifact_location = storage.get_storage_path(prebuilt_node)
189
- build_flags_changer.add_framework_search_path(build_configuration, artifact_location)
199
+ build_flags_changer.replace_or_add_framework_search_path(build_configuration, prebuilt_node.native_target.name, artifact_location)
190
200
  build_flags_changer.add_framework_headers_iquote(build_configuration, artifact_location, prebuilt_node)
191
201
 
192
202
  if dependency_remover.is_linked(prebuilt_node, dependent_target)
@@ -204,12 +214,16 @@ module XcodeArchiveCache
204
214
 
205
215
  injected_modulemap_file_path = storage.get_modulemap_path(prebuilt_node)
206
216
  if injected_modulemap_file_path
207
- modulemap_file_names = ["#{prebuilt_node.module_name}.modulemap", File.basename(prebuilt_node.modulemap_file_path)]
217
+ modulemap_file_names = [prebuilt_node.resulting_modulemap_file_name]
208
218
  build_flags_changer.fix_module_map_path(build_configuration, modulemap_file_names, injected_modulemap_file_path)
219
+
220
+ original_modulemap_path = prebuilt_node.original_modulemap_file_path
221
+ add_header_paths_to_target(dependent_target, [File.dirname(original_modulemap_path)])
209
222
  end
210
223
 
211
224
  artifact_location = storage.get_storage_path(prebuilt_node)
212
- build_flags_changer.add_library_search_path(build_configuration, artifact_location)
225
+ build_flags_changer.replace_or_add_library_search_path(build_configuration, prebuilt_node.native_target.name, artifact_location)
226
+ build_flags_changer.add_swift_include_path(build_configuration, artifact_location)
213
227
 
214
228
  if dependency_remover.is_linked(prebuilt_node, dependent_target)
215
229
  if dependent_target.product_type == Xcodeproj::Constants::PRODUCT_TYPE_UTI[:static_library]
@@ -250,6 +264,14 @@ module XcodeArchiveCache
250
264
  debug("deleting #{node.name} target")
251
265
  node.native_target.project.targets.delete(node.native_target)
252
266
  end
267
+
268
+ # @param [Xcodeproj::Project::Object::PBXNativeTarget] target
269
+ #
270
+ # @return [String]
271
+ #
272
+ def get_target_identifier(target)
273
+ target.uuid + target.project.path.realpath.to_s
274
+ end
253
275
  end
254
276
  end
255
277
  end
@@ -23,8 +23,8 @@ module XcodeArchiveCache
23
23
  # @param [String] path
24
24
  # @param [Array<String>] file_paths
25
25
  #
26
- def store_headers(node, path, file_paths)
27
- storage_path = get_full_header_storage_path(path)
26
+ def store_headers(node, path, file_paths, save_path = true)
27
+ storage_path = Pathname.new(path).absolute? ? path : get_full_header_storage_path(path)
28
28
 
29
29
  unless File.exist?(storage_path)
30
30
  FileUtils.mkdir_p(storage_path)
@@ -34,7 +34,7 @@ module XcodeArchiveCache
34
34
  FileUtils.cp(file_path, get_stored_file_path(storage_path, file_path))
35
35
  end
36
36
 
37
- save_header_storage_path(storage_path, node)
37
+ save_header_storage_path(storage_path, node) if save_path
38
38
  end
39
39
 
40
40
  # @param [XcodeArchiveCache::BuildGraph::Node] node
@@ -44,13 +44,8 @@ module XcodeArchiveCache
44
44
  store_headers(node, get_default_headers_storage_path(node), file_paths)
45
45
  end
46
46
 
47
- # @param [XcodeArchiveCache::BuildGraph::Node] node
48
- #
49
- def store_modulemap(node)
50
- modulemap_file_path = node.modulemap_file_path
51
- if modulemap_file_path && File.exist?(modulemap_file_path)
52
- store_headers(node, get_default_headers_storage_path(node), [modulemap_file_path])
53
- end
47
+ def store_modulemap_headers(node, file_paths)
48
+ store_headers(node, get_storage_path(node), file_paths, false)
54
49
  end
55
50
 
56
51
  # @param [XcodeArchiveCache::BuildGraph::Node] node
@@ -58,11 +53,11 @@ module XcodeArchiveCache
58
53
  # @return [String]
59
54
  #
60
55
  def get_modulemap_path(node)
61
- modulemap_file_path = node.modulemap_file_path
62
- return if modulemap_file_path == nil
56
+ modulemap_file_name = node.resulting_modulemap_file_name
57
+ return if modulemap_file_name == nil
63
58
 
64
- storage_path = get_full_header_storage_path(get_default_headers_storage_path(node))
65
- stored_modulemap_file_path = get_stored_file_path(storage_path, modulemap_file_path)
59
+ storage_path = get_storage_path(node)
60
+ stored_modulemap_file_path = get_stored_file_path(storage_path, modulemap_file_name)
66
61
  File.exist?(stored_modulemap_file_path) ? stored_modulemap_file_path : nil
67
62
  end
68
63
 
@@ -0,0 +1,20 @@
1
+ module XcodeArchiveCache
2
+ module Modulemap
3
+ class FileHandler
4
+ # @param [String] modulemap_path
5
+ #
6
+ # @return [Array<String>]
7
+ #
8
+ def read_modulemap_lines(modulemap_path)
9
+ File.read(modulemap_path).split("\n")
10
+ end
11
+
12
+ # @param [Array<String>] lines
13
+ # @param [String] modulemap_path
14
+ #
15
+ def write_modulemap_lines(lines, modulemap_path)
16
+ File.open(modulemap_path, "w") { |file| file.puts lines.join("\n") }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,67 @@
1
+ module XcodeArchiveCache
2
+ module Modulemap
3
+ class HeaderPathDeclaration
4
+ # @return [String]
5
+ #
6
+ attr_reader :type
7
+
8
+ # @return [String]
9
+ #
10
+ attr_reader :path
11
+
12
+ # @param [String] type
13
+ # @param [String] path
14
+ #
15
+ def initialize(type, path)
16
+ @type = type
17
+ @path = path
18
+ end
19
+ end
20
+
21
+ class HeaderPathExtractor
22
+
23
+ include XcodeArchiveCache::Logs
24
+
25
+ # @param [String] modulemap_path
26
+ #
27
+ # @return [Array<String>]
28
+ #
29
+ def extract_all_paths(modulemap_path)
30
+ modulemap_dir = File.dirname(modulemap_path)
31
+ modulemap_lines = FileHandler.new.read_modulemap_lines(modulemap_path)
32
+ header_paths = []
33
+
34
+ modulemap_lines.each do |line|
35
+ header_declaration = extract_header_declaration(line)
36
+ if header_declaration
37
+ header_paths << get_full_header_path(modulemap_dir, header_declaration.path)
38
+ end
39
+ end
40
+
41
+ debug("modulemap header paths: #{header_paths}")
42
+
43
+ header_paths
44
+ end
45
+
46
+ # @param [String] modulemap_dir
47
+ # @param [String] path
48
+ #
49
+ # @return [String]
50
+ #
51
+ def get_full_header_path(modulemap_dir, path)
52
+ Pathname.new(path).absolute? ? path : File.join(modulemap_dir, path)
53
+ end
54
+
55
+ # @param [String] line
56
+ #
57
+ # @return [XcodeArchiveCache::Modulemap::HeaderPathDeclaration]
58
+ #
59
+ def extract_header_declaration(line)
60
+ if line.include?("header") && !line.include?("exclude")
61
+ components = line.split("\"")
62
+ HeaderPathDeclaration.new(components[0], components[1])
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,65 @@
1
+ module XcodeArchiveCache
2
+ module Modulemap
3
+ class HeaderPathFixer
4
+
5
+ include XcodeArchiveCache::Logs
6
+
7
+ # @param [Storage] storage
8
+ #
9
+ def initialize(storage)
10
+ @storage = storage
11
+ @header_path_extractor = HeaderPathExtractor.new
12
+ end
13
+
14
+ # @param [XcodeArchiveCache::BuildGraph::Node] node
15
+ #
16
+ def fix_modulemap(node)
17
+ return unless node.has_static_library_product?
18
+
19
+ injected_modulemap_file_path = storage.get_modulemap_path(node)
20
+ return if injected_modulemap_file_path == nil
21
+
22
+ file_handler = FileHandler.new
23
+
24
+ modulemap_dir = File.dirname(injected_modulemap_file_path)
25
+ modulemap_lines = file_handler.read_modulemap_lines(injected_modulemap_file_path)
26
+
27
+ updated_lines = modulemap_lines.map do |line|
28
+ declaration = header_path_extractor.extract_header_declaration(line)
29
+ next line unless declaration
30
+
31
+ # absolute paths depend on machine and derived data dir
32
+ #
33
+ full_header_path = header_path_extractor.get_full_header_path(modulemap_dir, declaration.path)
34
+ should_replace = Pathname.new(declaration.path).absolute? || !File.exist?(full_header_path)
35
+ next line unless should_replace
36
+
37
+ header_file_name = File.basename(declaration.path)
38
+ injected_header_path = File.join(modulemap_dir, header_file_name)
39
+ next line if injected_header_path == declaration.path
40
+
41
+ if File.exist?(injected_header_path)
42
+ debug("substituting #{declaration.path} with #{injected_header_path} in #{File.basename(injected_modulemap_file_path)}")
43
+ "#{declaration.type}\"#{injected_header_path}\""
44
+ else
45
+ error("failed to substitute missing header #{declaration.path} with another missing header in #{File.basename(injected_modulemap_file_path)}")
46
+ error("leaving the path as it is")
47
+ line
48
+ end
49
+ end
50
+
51
+ file_handler.write_modulemap_lines(updated_lines, injected_modulemap_file_path)
52
+ end
53
+
54
+ private
55
+
56
+ # @return [Storage]
57
+ #
58
+ attr_accessor :storage
59
+
60
+ # @return [XcodeArchiveCache::Modulemap::HeaderPathExtractor]
61
+ #
62
+ attr_accessor :header_path_extractor
63
+ end
64
+ end
65
+ end
@@ -68,34 +68,49 @@ module XcodeArchiveCache
68
68
  raise Informative, "Target not found for #{target_config.name}"
69
69
  end
70
70
 
71
+ xcodebuild_executor = XcodeArchiveCache::Xcodebuild::Executor.new(config.active_configuration.build_configuration,
72
+ target.platform_name,
73
+ config.settings.destination,
74
+ config.active_configuration.action,
75
+ config.active_configuration.xcodebuild_args)
76
+ graph_builder = XcodeArchiveCache::BuildGraph::Builder.new(@native_target_finder, xcodebuild_executor)
77
+
78
+ dependency_targets = Hash.new
79
+ build_graphs = Hash.new
80
+
71
81
  target_config.dependencies.each do |dependency_name|
72
- handle_dependency(target, dependency_name)
82
+ info("creating build graph for #{dependency_name}")
83
+
84
+ dependency_target = find_dependency_target(target, dependency_name)
85
+ dependency_targets[dependency_name] = dependency_target
86
+ build_graphs[dependency_name] = graph_builder.build_graph(target, dependency_target)
87
+ end
88
+
89
+ target_config.dependencies.each do |dependency_name|
90
+ info("processing #{dependency_name}")
91
+
92
+ dependency_target = dependency_targets[dependency_name]
93
+ build_graph = build_graphs[dependency_name]
94
+
95
+ @rebuild_evaluator.evaluate_build_graph(build_graph)
96
+ unpack_cached_artifacts(build_graph)
97
+ rebuild_if_needed(xcodebuild_executor, dependency_target, build_graph)
98
+ @injector.perform_outgoing_injection(build_graph, target)
73
99
  end
74
100
  end
75
101
 
76
102
  # @param [Xcodeproj::Project::Object::PBXNativeTarget] target
77
103
  # @param [String] dependency_name
78
104
  #
79
- def handle_dependency(target, dependency_name)
80
- info("checking #{dependency_name}")
81
-
105
+ # @return [Xcodeproj::Project::Object::PBXNativeTarget]
106
+ #
107
+ def find_dependency_target(target, dependency_name)
82
108
  dependency_target = @native_target_finder.find_for_product_name(dependency_name)
83
109
  unless dependency_target
84
110
  raise Informative, "Target not found for #{dependency_name} of #{target.display_name}"
85
111
  end
86
112
 
87
- xcodebuild_executor = XcodeArchiveCache::Xcodebuild::Executor.new(config.active_configuration.build_configuration,
88
- dependency_target.platform_name,
89
- config.settings.destination,
90
- config.active_configuration.action,
91
- config.active_configuration.xcodebuild_args)
92
- graph_builder = XcodeArchiveCache::BuildGraph::Builder.new(@native_target_finder, xcodebuild_executor)
93
- graph = graph_builder.build_graph(target, dependency_target)
94
-
95
- @rebuild_evaluator.evaluate_build_graph(graph)
96
- unpack_cached_artifacts(graph)
97
- rebuild_if_needed(xcodebuild_executor, dependency_target, graph)
98
- @injector.perform_outgoing_injection(graph, target)
113
+ dependency_target
99
114
  end
100
115
 
101
116
  # @param [XcodeArchiveCache::BuildGraph::Graph] graph
@@ -40,13 +40,16 @@ require 'build_settings/parser'
40
40
 
41
41
  require 'injection/injector'
42
42
  require 'injection/pods_script_fixer'
43
- require 'injection/modulemap_fixer'
44
43
  require 'injection/build_flags_changer'
45
44
  require 'injection/dependency_remover'
46
45
  require 'injection/headers_mover'
47
46
  require 'injection/storage'
48
47
  require 'injection/framework_embedder'
49
48
 
49
+ require 'modulemap/file_handler'
50
+ require 'modulemap/header_path_extractor'
51
+ require 'modulemap/header_path_fixer'
52
+
50
53
  require 'runner/runner'
51
54
 
52
55
  require 'shell/executor'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xcode-archive-cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.10.pre.2
4
+ version: 0.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ilya Dyakonov
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-21 00:00:00.000000000 Z
11
+ date: 2020-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: xcodeproj
@@ -78,8 +78,8 @@ dependencies:
78
78
  - - "~>"
79
79
  - !ruby/object:Gem::Version
80
80
  version: '1.0'
81
- description:
82
- email:
81
+ description:
82
+ email:
83
83
  executables:
84
84
  - xcode-archive-cache
85
85
  extensions: []
@@ -112,10 +112,12 @@ files:
112
112
  - lib/injection/framework_embedder.rb
113
113
  - lib/injection/headers_mover.rb
114
114
  - lib/injection/injector.rb
115
- - lib/injection/modulemap_fixer.rb
116
115
  - lib/injection/pods_script_fixer.rb
117
116
  - lib/injection/storage.rb
118
117
  - lib/logs/logs.rb
118
+ - lib/modulemap/file_handler.rb
119
+ - lib/modulemap/header_path_extractor.rb
120
+ - lib/modulemap/header_path_fixer.rb
119
121
  - lib/runner/runner.rb
120
122
  - lib/shell/executor.rb
121
123
  - lib/xcode-archive-cache.rb
@@ -124,7 +126,7 @@ homepage: https://github.com/sweatco/xcode-archive-cache
124
126
  licenses:
125
127
  - MIT
126
128
  metadata: {}
127
- post_install_message:
129
+ post_install_message:
128
130
  rdoc_options: []
129
131
  require_paths:
130
132
  - lib
@@ -135,12 +137,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
135
137
  version: 2.0.0
136
138
  required_rubygems_version: !ruby/object:Gem::Requirement
137
139
  requirements:
138
- - - ">"
140
+ - - ">="
139
141
  - !ruby/object:Gem::Version
140
- version: 1.3.1
142
+ version: '0'
141
143
  requirements: []
142
144
  rubygems_version: 3.0.4
143
- signing_key:
145
+ signing_key:
144
146
  specification_version: 4
145
147
  summary: Native targets cache for Xcode archive builds.
146
148
  test_files: []
@@ -1,47 +0,0 @@
1
- module XcodeArchiveCache
2
- module Injection
3
-
4
- class ModulemapFixer
5
-
6
- # @param [Storage] storage
7
- #
8
- def initialize(storage)
9
- @storage = storage
10
- end
11
-
12
- # @param [XcodeArchiveCache::BuildGraph::Node] node
13
- #
14
- def fix_modulemap(node)
15
- return unless node.has_static_library_product?
16
-
17
- modulemap_file_path = node.modulemap_file_path
18
- return if modulemap_file_path == nil
19
-
20
- injected_modulemap_file_path = storage.get_modulemap_path(node)
21
- return if injected_modulemap_file_path == nil
22
-
23
- swift_objc_interface_header_file_name = node.swift_objc_interface_header_file
24
- return if swift_objc_interface_header_file_name == nil
25
-
26
- # add generated header to modulemap to make Swift stuff available module users
27
- #
28
- storage_path = storage.get_storage_path(node)
29
- header_path = File.join(storage_path, swift_objc_interface_header_file_name)
30
- return unless File.exist?(header_path)
31
-
32
- File.open(injected_modulemap_file_path, "a") do |modulemap_file|
33
- modulemap_file.puts "\nmodule #{node.module_name}.Swift {\n"
34
- modulemap_file.puts " header \"#{header_path}\"\n"
35
- modulemap_file.puts " requires objc\n"
36
- modulemap_file.puts "}\n"
37
- end
38
- end
39
-
40
- private
41
-
42
- # @return [Storage]
43
- #
44
- attr_accessor :storage
45
- end
46
- end
47
- end