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

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: 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