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.
@@ -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
 
@@ -86,9 +91,52 @@ module XcodeArchiveCache
86
91
  # @return [String]
87
92
  #
88
93
  def dsym_file_name
89
- return nil unless build_settings
94
+ build_settings ? build_settings[XcodeArchiveCache::BuildSettings::DWARF_DSYM_FILE_NAME_KEY] : nil
95
+ end
96
+
97
+ # @return [String]
98
+ #
99
+ def original_modulemap_file_path
100
+ modulemap_file = modulemap_file_name
101
+ return unless modulemap_file
102
+
103
+ Pathname.new(modulemap_file).absolute? ? modulemap_file : File.join(File.dirname(native_target.project.path), modulemap_file)
104
+ end
105
+
106
+ # @return [String]
107
+ #
108
+ def resulting_modulemap_file_name
109
+ if module_name
110
+ module_name + ".modulemap"
111
+ else
112
+ File.basename(modulemap_file_name)
113
+ end
114
+ end
90
115
 
91
- build_settings[XcodeArchiveCache::BuildSettings::DWARF_DSYM_FILE_NAME_KEY]
116
+ # @return [String]
117
+ #
118
+ def swift_objc_interface_header_file
119
+ header_file = build_settings[XcodeArchiveCache::BuildSettings::SWIFT_OBJC_INTERFACE_HEADER_NAME_KEY]
120
+ if header_file == nil
121
+ our_module_name = module_name
122
+ return if our_module_name == nil
123
+
124
+ header_file = our_module_name + "-Swift.h"
125
+ end
126
+
127
+ header_file
128
+ end
129
+
130
+ # @return [String]
131
+ #
132
+ def module_name
133
+ build_settings[XcodeArchiveCache::BuildSettings::PRODUCT_MODULE_NAME_KEY]
134
+ end
135
+
136
+ # @return [String]
137
+ #
138
+ def modulemap_file_name
139
+ build_settings[XcodeArchiveCache::BuildSettings::MODULEMAP_FILE_KEY]
92
140
  end
93
141
 
94
142
  # @return [Array<Node>]
@@ -8,24 +8,50 @@ module XcodeArchiveCache
8
8
  @cache_storage = cache_storage
9
9
  end
10
10
 
11
+ # @param [XcodeArchiveCache::BuildGraph::Graph] graph
12
+ #
13
+ def evaluate_build_graph(graph)
14
+ return if graph.root_node.state != :unknown
15
+
16
+ # DFS over graph, evaluating dependencies first
17
+ #
18
+ stack = [graph.root_node]
19
+
20
+ while stack.length > 0
21
+ last_node = stack.last
22
+
23
+ if last_node.state == :evaluating_dependencies
24
+ # dependencies were evaluated, we're good to go
25
+ evaluate(last_node)
26
+ stack.delete_at(stack.length - 1)
27
+ elsif last_node.state == :unknown
28
+ last_node.state = :evaluating_dependencies
29
+ stack += last_node.dependencies.select { |dependency| dependency.state == :unknown }
30
+ else
31
+ stack.delete_at(stack.length - 1)
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
11
38
  # @param [XcodeArchiveCache::BuildGraph::Node] node
12
39
  #
13
40
  def evaluate(node)
14
- return if node.state != :unknown
41
+ has_dependencies_waiting_for_rebuild = node.dependencies
42
+ .reduce(false) { |rebuild, dependency| rebuild || dependency.waiting_for_rebuild }
15
43
 
16
44
  # we include dependency shas in every node sha calculation,
17
45
  # so if some dependency changes, that change propagates
18
46
  # all the way to the top level
19
47
  #
20
- if cache_storage.cached_artifact_path(node) == nil
48
+ if has_dependencies_waiting_for_rebuild || cache_storage.cached_artifact_path(node) == nil
21
49
  node.state = :waiting_for_rebuild
22
50
  else
23
51
  node.state = :exists_in_cache
24
52
  end
25
53
  end
26
54
 
27
- private
28
-
29
55
  # @return [XcodeArchiveCache::ArtifactCache::AbstractStorage]
30
56
  #
31
57
  attr_reader :cache_storage
@@ -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,6 +54,9 @@ module XcodeArchiveCache
36
54
  inputs << list_build_phase_inputs(build_phase)
37
55
  end
38
56
 
57
+ modulemap_file_path = node.original_modulemap_file_path
58
+ inputs << modulemap_file_path if modulemap_file_path
59
+
39
60
  # file path order should not affect evaluation result
40
61
  inputs.flatten.compact.sort
41
62
  end
@@ -71,7 +92,7 @@ module XcodeArchiveCache
71
92
  # @param [Array<String>] dependency_shas
72
93
  #
73
94
  def save_auxiliary_data(build_settings, dependency_shas, tempfile)
74
- file_contents = build_settings + dependency_shas.join("\n")
95
+ file_contents = build_settings + dependency_shas.join("\n") + "\nXCODE-ARCHIVE-CACHE: #{own_sha}\n"
75
96
  tempfile << file_contents
76
97
  tempfile.flush
77
98
  end
@@ -13,7 +13,7 @@ module XcodeArchiveCache
13
13
  # @return [Hash{String => Container}]
14
14
  # Target build settings keyed by target name
15
15
  #
16
- def extract_per_target(build_settings)
16
+ def extract_per_target(build_settings, fix_simulator)
17
17
  per_target_settings = build_settings.split("Build settings for action")
18
18
  result = Hash.new
19
19
 
@@ -22,6 +22,7 @@ module XcodeArchiveCache
22
22
  target_name = get_target_name(parsed_settings)
23
23
  next unless target_name
24
24
 
25
+ replace_platform_with_simulator(parsed_settings) if fix_simulator
25
26
  filtered_settings = filter.filter(parsed_settings)
26
27
  result[target_name] = Container.new(parsed_settings, filtered_settings)
27
28
  end
@@ -65,6 +66,27 @@ module XcodeArchiveCache
65
66
  result
66
67
  end
67
68
 
69
+
70
+ # @param [Hash{String => String}] settings
71
+ #
72
+ def replace_platform_with_simulator(settings)
73
+ original_platform = settings[EFFECTIVE_PLATFORM_NAME_KEY]
74
+ simulator_platform = settings[CORRESPONDING_SIMULATOR_PLATFORM_NAME_KEY]
75
+ settings[EFFECTIVE_PLATFORM_NAME_KEY] = "-#{simulator_platform}"
76
+ settings[PLATFORM_NAME_KEY] = simulator_platform
77
+
78
+ configuration = settings[CONFIGURATION_KEY]
79
+ path_regexp = Regexp.new("#{configuration}#{original_platform}")
80
+ simulator_path = "#{configuration}-#{simulator_platform}"
81
+ settings.each do |key, value|
82
+ settings[key] = value.gsub(path_regexp, simulator_path)
83
+ end
84
+ end
85
+
86
+ PLATFORM_NAME_KEY = "PLATFORM_NAME".freeze
87
+ EFFECTIVE_PLATFORM_NAME_KEY = "EFFECTIVE_PLATFORM_NAME".freeze
88
+ CORRESPONDING_SIMULATOR_PLATFORM_NAME_KEY = "CORRESPONDING_SIMULATOR_PLATFORM_NAME".freeze
89
+ CONFIGURATION_KEY = "CONFIGURATION".freeze
68
90
  TARGET_NAME_KEY = "TARGETNAME".freeze
69
91
 
70
92
  # @param [Hash{String => String}] parsed_settings
@@ -10,7 +10,7 @@ module XcodeArchiveCache
10
10
  # Machine-dependent settings i.e. paths, user names, group names are rejected
11
11
  #
12
12
  def filter(settings, settings_to_keep = SETTINGS_TO_KEEP)
13
- filtered_settings = settings.select {|name, _| settings_to_keep.include?(name)}
13
+ filtered_settings = settings.select { |name, _| settings_to_keep.include?(name) }
14
14
  SETTINGS_TO_STRIP.each do |name|
15
15
  value = filtered_settings[name]
16
16
  next if value == nil
@@ -44,7 +44,9 @@ module XcodeArchiveCache
44
44
  # splitting will be broken, but probability of
45
45
  # someone using such path is quite low (or it isn't ?)
46
46
  #
47
- value_components = value_without_quotes.split(/\s-/)
47
+ value_components = value_without_quotes
48
+ .split(/^-|\s-/)
49
+ .select { |component| component.length > 0 }
48
50
 
49
51
  index = 0
50
52
  indices_to_remove = []
@@ -66,7 +68,8 @@ module XcodeArchiveCache
66
68
  kept_components.push(component) unless indices_to_remove.include?(component_index)
67
69
  end
68
70
 
69
- kept_components.join(" -")
71
+ result = kept_components.join(" -")
72
+ result.length > 0 ? "-#{result}" : ""
70
73
  end
71
74
 
72
75
  # TODO: extend
@@ -283,6 +286,7 @@ module XcodeArchiveCache
283
286
  output-file-map
284
287
  save-optimization-record-path
285
288
  working-directory
289
+ fmodule-map-file
286
290
  )
287
291
  end
288
292
  end
@@ -1,8 +1,15 @@
1
1
  module XcodeArchiveCache
2
2
  module BuildSettings
3
3
 
4
+ PRODUCT_NAME_KEY = "PRODUCT_NAME".freeze
5
+ TARGET_NAME_KEY = "TARGET_NAME".freeze
4
6
  FULL_PRODUCT_NAME_KEY = "FULL_PRODUCT_NAME".freeze
5
7
  DWARF_DSYM_FILE_NAME_KEY = "DWARF_DSYM_FILE_NAME".freeze
8
+ MODULEMAP_FILE_KEY = "MODULEMAP_FILE".freeze
9
+ SWIFT_OBJC_INTERFACE_HEADER_NAME_KEY = "SWIFT_OBJC_INTERFACE_HEADER_NAME".freeze
10
+ SWIFT_MODULE_NAME_KEY = "SWIFT_MODULE_NAME".freeze
11
+ PRODUCT_MODULE_NAME_KEY = "PRODUCT_MODULE_NAME".freeze
12
+ DERIVED_SOURCES_DIR_KEY = "DERIVED_SOURCES_DIR".freeze
6
13
 
7
14
  class Container
8
15
 
@@ -58,9 +65,11 @@ module XcodeArchiveCache
58
65
  end
59
66
  end
60
67
 
68
+ should_fix_settings = executor.set_up_for_simulator?
69
+
61
70
  threads.each do |thread|
62
71
  project_path, all_targets_settings = thread.value
63
- per_target_settings = extractor.extract_per_target(all_targets_settings)
72
+ per_target_settings = extractor.extract_per_target(all_targets_settings, should_fix_settings)
64
73
  set_project_settings(project_path, per_target_settings)
65
74
  end
66
75
  end
@@ -1,11 +1,24 @@
1
1
  module XcodeArchiveCache
2
2
  module BuildSettings
3
+ class SettingEntry
4
+ attr_reader :name
5
+ attr_reader :modifiers
6
+ attr_reader :full_string
7
+
8
+ def initialize(name, modifiers, full_string)
9
+ @name = name
10
+ @modifiers = modifiers
11
+ @full_string = full_string
12
+ end
13
+ end
14
+
3
15
  class Parser
4
16
 
5
17
  def initialize
6
18
  @setting_name_regex = Regexp.new("^(?<#{SETTING_NAME_GROUP}>#{SETTING_NAME_CHARACTERS})\s=")
7
19
  @setting_value_regex = Regexp.new("^#{SETTING_NAME_CHARACTERS}\s=\s(?<#{SETTING_VALUE_GROUP}>.+)$")
8
20
  @setting_entry_regex = create_entry_regex
21
+ @setting_entry_part_regex = Regexp.new("#{SETTING_ENTRY_PART_CHARACTERS}")
9
22
  @setting_entry_name_regex = Regexp.new(SETTING_NAME_CHARACTERS)
10
23
  end
11
24
 
@@ -13,12 +26,18 @@ module XcodeArchiveCache
13
26
  #
14
27
  # @return [Array<String>]
15
28
  #
16
- def find_all_names(string)
29
+ def find_all_entries(string)
30
+ return nil if string == nil
31
+
17
32
  string.scan(setting_entry_regex)
18
- .map {|entry| entry.scan(setting_entry_name_regex).first}
19
- .flatten
33
+ .map {|entry|
34
+ parts = entry.scan(setting_entry_part_regex)
35
+ name = parts.first
36
+ modifiers = parts.drop(1)
37
+
38
+ name != nil ? SettingEntry.new(name, modifiers, entry) : nil
39
+ }
20
40
  .compact
21
- .uniq
22
41
  end
23
42
 
24
43
  # @param [String] string
@@ -45,9 +64,9 @@ module XcodeArchiveCache
45
64
 
46
65
  # @param [String] characters
47
66
  #
48
- # @return [String]
67
+ # @return [Regexp]
49
68
  #
50
- def create_entry_regex(characters = SETTING_NAME_CHARACTERS)
69
+ def create_entry_regex(characters = SETTING_ALL_CHARACTERS)
51
70
  Regexp.new("\\$[({]#{characters}[)}]")
52
71
  end
53
72
 
@@ -65,10 +84,14 @@ module XcodeArchiveCache
65
84
  #
66
85
  attr_reader :setting_entry_regex
67
86
 
87
+ attr_reader :setting_entry_part_regex
88
+
68
89
  # @return [Regexp]
69
90
  #
70
91
  attr_reader :setting_entry_name_regex
71
92
 
93
+ SETTING_ALL_CHARACTERS = "[A-Za-z0-9_:]+".freeze
94
+ SETTING_ENTRY_PART_CHARACTERS = "[A-Za-z0-9_]+".freeze
72
95
  SETTING_NAME_CHARACTERS = "[A-Z0-9_]+".freeze
73
96
  SETTING_NAME_GROUP = "name".freeze
74
97
  SETTING_VALUE_GROUP = "value".freeze
@@ -12,15 +12,17 @@ module XcodeArchiveCache
12
12
  # @return [String]
13
13
  #
14
14
  def interpolate(string, build_settings)
15
- names = parser.find_all_names(string)
15
+ return nil if string == nil
16
+
17
+ entries = parser.find_all_entries(string)
16
18
  result = string
17
19
 
18
- names.each do |name|
19
- value = build_settings[name]
20
+ entries.each do |entry|
21
+ value = build_settings[entry.name]
20
22
  next unless value
21
23
 
22
- replacement_regex = parser.create_entry_regex(name)
23
- result = result.gsub(replacement_regex, value)
24
+ modified_value = modify_setting_value(value, entry.modifiers)
25
+ result = result.gsub(entry.full_string, modified_value)
24
26
  end
25
27
 
26
28
  result
@@ -31,6 +33,20 @@ module XcodeArchiveCache
31
33
  # @return [Parser]
32
34
  #
33
35
  attr_accessor :parser
36
+
37
+ def modify_setting_value(value, modifiers)
38
+ modified_value = value
39
+
40
+ modifiers.each do |modifier|
41
+ case modifier
42
+ when "c99extidentifier"
43
+ modified_value = modified_value.gsub(/[-\s]/, "_")
44
+ else
45
+ end
46
+ end
47
+
48
+ modified_value
49
+ end
34
50
  end
35
51
  end
36
52
  end
data/lib/config/config.rb CHANGED
@@ -52,7 +52,7 @@ module XcodeArchiveCache
52
52
  def active_configuration
53
53
  configuration = configurations.select{|config| config.name == active_configuration_name }.first
54
54
  if configuration == nil
55
- raise Informative, "Found no configuration with name \"#{active_configuration_name}\""
55
+ raise XcodeArchiveCache::Informative, "Found no configuration with name \"#{active_configuration_name}\""
56
56
  end
57
57
 
58
58
  configuration
@@ -172,7 +172,7 @@ module XcodeArchiveCache
172
172
  begin
173
173
  eval(contents, nil, path)
174
174
  rescue Exception => e
175
- raise Informative, "Invalid #{File.basename(path)} file: #{e.message}"
175
+ raise XcodeArchiveCache::Informative, "Invalid #{File.basename(path)} file: #{e.message}"
176
176
  end
177
177
  end
178
178
 
@@ -0,0 +1,26 @@
1
+ module XCConfigExtensions
2
+ # @return [Bool]
3
+ #
4
+ def has_xcconfig?
5
+ base_configuration_reference != nil
6
+ end
7
+
8
+ # @return [String]
9
+ #
10
+ def get_xcconfig_path
11
+ base_configuration_reference.real_path
12
+ end
13
+ end
14
+
15
+ module ProjectDir
16
+ # @return [String]
17
+ #
18
+ def get_project_dir
19
+ File.dirname(project.path)
20
+ end
21
+ end
22
+
23
+ class Xcodeproj::Project::Object::XCBuildConfiguration
24
+ include XCConfigExtensions
25
+ include ProjectDir
26
+ end