xcode-archive-cache 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -44,7 +44,7 @@ module XcodeArchiveCache
44
44
  # @return [String]
45
45
  #
46
46
  def find_embed_frameworks_script(target, build_settings)
47
- find_script(target, build_settings, "[CP] Embed Pods Frameworks")
47
+ target.find_script(build_settings_interpolator, build_settings, "[CP] Embed Pods Frameworks")
48
48
  end
49
49
 
50
50
  # @param [Xcodeproj::Project::Object::PBXNativeTarget] target
@@ -53,25 +53,7 @@ module XcodeArchiveCache
53
53
  # @return [String]
54
54
  #
55
55
  def find_copy_resources_script(target, build_settings)
56
- find_script(target, build_settings, "[CP] Copy Pods Resources")
57
- end
58
-
59
- # @param [Xcodeproj::Project::Object::PBXNativeTarget] target
60
- # @param [XcodeArchiveCache::BuildSettings::Container] build_settings
61
- # @param [String] script_name
62
- #
63
- # @return [String]
64
- #
65
- def find_script(target, build_settings, script_name)
66
- target.shell_script_build_phases.each do |phase|
67
- if phase.display_name == script_name
68
- return build_settings_interpolator.interpolate(phase.shell_script, build_settings)
69
- .gsub(/^"|"$/, "")
70
- .strip
71
- end
72
- end
73
-
74
- nil
56
+ target.find_script(build_settings_interpolator, build_settings, "[CP] Copy Pods Resources")
75
57
  end
76
58
 
77
59
  # @param [String] file_path
@@ -0,0 +1,126 @@
1
+ module XcodeArchiveCache
2
+ module Injection
3
+ class PodsXCFrameworkFixer
4
+
5
+ include XcodeArchiveCache::Logs
6
+
7
+ # @param [XcodeArchiveCache::Injection::Storage] storage
8
+ # @param [XcodeArchive::BuildGraph::NativeTargetFinder] native_target_finder
9
+ # @param [String] configuration_name
10
+ #
11
+ def initialize(storage, native_target_finder, configuration_name)
12
+ @storage = storage
13
+ @native_target_finder = native_target_finder
14
+ @configuration_name = configuration_name
15
+ @shell_executor = XcodeArchiveCache::Shell::Executor.new
16
+ @build_settings_interpolator = XcodeArchiveCache::BuildSettings::StringInterpolator.new
17
+ @checked_targets = []
18
+ end
19
+
20
+ # @param [Xcodeproj::Project::Object::PBXAbstractTarget] target
21
+ # @param [XcodeArchiveCache::BuildSettings::Container] build_settings
22
+ #
23
+ def fix(target, build_settings_loader)
24
+ checked_targets.push(target.equatable_identifier)
25
+ build_settings = build_settings_loader.get_settings(target.project.path, target.display_name)
26
+
27
+ debug("fixing #{target.display_name}")
28
+ script_path = find_copy_xcframeworks_script(target, build_settings)
29
+ if script_path != nil
30
+ fix_file(script_path)
31
+
32
+ unless shell_executor.execute_with_env(script_path, build_settings.all)
33
+ raise XcodeArchiveCache::Informative, "Failed to execute Pods XCFramework script #{script_path}"
34
+ end
35
+ end
36
+
37
+ embed_frameworks_script_path = find_embed_frameworks_script(target, build_settings)
38
+ if embed_frameworks_script_path != nil
39
+ fix_file(embed_frameworks_script_path)
40
+ end
41
+
42
+ build_configuration = target.find_build_configuration(configuration_name)
43
+ if build_configuration.has_xcconfig?
44
+ fix_xcconfig_recursively(build_configuration.get_xcconfig_path, build_configuration.get_project_dir)
45
+ end
46
+
47
+ dependencies = native_target_finder.find_all_dependencies(target)
48
+ dependencies.each do |dependency_target|
49
+ if checked_targets.include?(dependency_target.equatable_identifier)
50
+ next
51
+ end
52
+
53
+ fix(dependency_target, build_settings_loader)
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ # @return [XcodeArchive::Injection::InjectionStorage]
60
+ #
61
+ attr_reader :storage
62
+
63
+ # @return [XcodeArchive::BuildGraph::NativeTargetFinder]
64
+ #
65
+ attr_reader :native_target_finder
66
+
67
+ # @return [String]
68
+ #
69
+ attr_reader :configuration_name
70
+
71
+ # @return [XcodeArchiveCache::Shell::Executor]
72
+ #
73
+ attr_reader :shell_executor
74
+
75
+ # @return [XcodeArchiveCache::BuildSettings::StringInterpolator]
76
+ #
77
+ attr_reader :build_settings_interpolator
78
+
79
+ # @return [Array<String>]
80
+ #
81
+ attr_accessor :checked_targets
82
+
83
+ # @param [String] path
84
+ # @param [String] project_dir
85
+ #
86
+ def fix_xcconfig_recursively(path, project_dir)
87
+ fix_file(path)
88
+ xcconfig = Xcodeproj::Config.new(path)
89
+
90
+ xcconfig.includes.each do |included_xcconfig|
91
+ included_xcconfig_path = File.join(project_dir, included_xcconfig)
92
+ fix_xcconfig_recursively(included_xcconfig_path, project_dir)
93
+ end
94
+ end
95
+
96
+ # @param [Xcodeproj::Project::Object::PBXAbstractTarget] target
97
+ # @param [XcodeArchiveCache::BuildSettings::Container] build_settings
98
+ #
99
+ # @return [String]
100
+ #
101
+ def find_copy_xcframeworks_script(target, build_settings)
102
+ target.find_script(build_settings_interpolator, build_settings, "[CP] Copy XCFrameworks")
103
+ end
104
+
105
+ # @param [Xcodeproj::Project::Object::PBXAbstractTarget] target
106
+ # @param [XcodeArchiveCache::BuildSettings::Container] build_settings
107
+ #
108
+ # @return [String]
109
+ #
110
+ def find_embed_frameworks_script(target, build_settings)
111
+ target.find_script(build_settings_interpolator, build_settings, "[CP] Embed Pods Frameworks")
112
+ end
113
+
114
+ # @param [String] file_path
115
+ #
116
+ def fix_file(file_path)
117
+ debug("fixing #{file_path}")
118
+ contents = File
119
+ .read(file_path)
120
+ .gsub("${PODS_XCFRAMEWORKS_BUILD_DIR}", storage.container_dir_path)
121
+
122
+ File.open(file_path, "w") {|file| file.puts(contents)}
123
+ end
124
+ end
125
+ end
126
+ end
@@ -23,18 +23,42 @@ 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)
31
31
  end
32
32
 
33
33
  file_paths.each do |file_path|
34
- FileUtils.cp(file_path, File.join(storage_path, File.basename(file_path)))
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
+ end
39
+
40
+ # @param [XcodeArchiveCache::BuildGraph::Node] node
41
+ # @param [Array<String>] file_paths
42
+ #
43
+ def store_default_headers(node, file_paths)
44
+ store_headers(node, get_default_headers_storage_path(node), file_paths)
45
+ end
46
+
47
+ def store_modulemap_headers(node, file_paths)
48
+ store_headers(node, get_storage_path(node), file_paths, false)
49
+ end
50
+
51
+ # @param [XcodeArchiveCache::BuildGraph::Node] node
52
+ #
53
+ # @return [String]
54
+ #
55
+ def get_modulemap_path(node)
56
+ modulemap_file_name = node.resulting_modulemap_file_name
57
+ return if modulemap_file_name == nil
58
+
59
+ storage_path = get_storage_path(node)
60
+ stored_modulemap_file_path = get_stored_file_path(storage_path, modulemap_file_name)
61
+ File.exist?(stored_modulemap_file_path) ? stored_modulemap_file_path : nil
38
62
  end
39
63
 
40
64
  # @param [XcodeArchiveCache::BuildGraph::Node] node
@@ -70,7 +94,7 @@ module XcodeArchiveCache
70
94
 
71
95
  # @param [XcodeArchiveCache::BuildGraph::Node] node
72
96
  #
73
- # @return [Array<String>]
97
+ # @return [String]
74
98
  #
75
99
  def get_headers_storage_paths(node)
76
100
  headers_storage_dir_paths[node.name]
@@ -78,11 +102,19 @@ module XcodeArchiveCache
78
102
 
79
103
  def get_all_headers_storage_paths
80
104
  headers_storage_dir_paths
81
- .map {|_, path| path}
105
+ .map { |_, path| path }
82
106
  .flatten
83
107
  .uniq
84
108
  end
85
109
 
110
+ # @param [XcodeArchiveCache::BuildGraph::Node] node
111
+ #
112
+ # @return [String]
113
+ #
114
+ def get_default_headers_storage_path(node)
115
+ "include/#{node.name}"
116
+ end
117
+
86
118
  private
87
119
 
88
120
  def prepare_container_dir
@@ -99,6 +131,15 @@ module XcodeArchiveCache
99
131
  File.absolute_path(path, container_dir_path)
100
132
  end
101
133
 
134
+ # @param [String] storage_path
135
+ # @param [String] file_path
136
+ #
137
+ # @return [String]
138
+ #
139
+ def get_stored_file_path(storage_path, file_path)
140
+ File.join(storage_path, File.basename(file_path))
141
+ end
142
+
102
143
  # @param [String] path
103
144
  # @param [XcodeArchiveCache::BuildGraph::Node] node
104
145
  #
@@ -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
data/lib/runner/runner.rb CHANGED
@@ -9,7 +9,7 @@ module XcodeArchiveCache
9
9
  @config = config
10
10
 
11
11
  projects = list_projects
12
- @native_target_finder = XcodeArchiveCache::BuildGraph::NativeTargetFinder.new(projects)
12
+ @native_target_finder = XcodeArchiveCache::BuildGraph::NativeTargetFinder.new(projects, config.active_configuration.build_configuration)
13
13
 
14
14
  storage_path = File.absolute_path(config.storage.path)
15
15
  @cache_storage = XcodeArchiveCache::ArtifactCache::LocalStorage.new(storage_path)
@@ -37,7 +37,7 @@ module XcodeArchiveCache
37
37
  return workspace.file_references.map {|file_reference| Xcodeproj::Project.open(file_reference.absolute_path(workspace_dir))}
38
38
  end
39
39
 
40
- raise Informative, "Configuration misses entry point -- must have either a project or a workspace"
40
+ raise XcodeArchiveCache::Informative, "Configuration misses entry point -- must have either a project or a workspace"
41
41
  end
42
42
 
43
43
  def run
@@ -65,45 +65,56 @@ module XcodeArchiveCache
65
65
  def handle_target(target_config)
66
66
  target = @native_target_finder.find_for_product_name(target_config.name)
67
67
  unless target
68
- raise Informative, "Target not found for #{target_config.name}"
68
+ raise XcodeArchiveCache::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
+ build_settings_loader = XcodeArchiveCache::BuildSettings::Loader.new(xcodebuild_executor)
77
+ graph_builder = XcodeArchiveCache::BuildGraph::Builder.new(@native_target_finder, build_settings_loader)
78
+
79
+ dependency_targets = Hash.new
80
+ build_graphs = Hash.new
81
+
71
82
  target_config.dependencies.each do |dependency_name|
72
- handle_dependency(target, dependency_name)
83
+ info("creating build graph for #{dependency_name}")
84
+
85
+ dependency_target = find_dependency_target(target, dependency_name)
86
+ dependency_targets[dependency_name] = dependency_target
87
+ build_graphs[dependency_name] = graph_builder.build_graph(target, dependency_target)
88
+ end
89
+
90
+ pods_xcframeworks_fixer = XcodeArchiveCache::Injection::PodsXCFrameworkFixer.new(@injection_storage, @native_target_finder, config.active_configuration.build_configuration)
91
+ pods_xcframeworks_fixer.fix(target, build_settings_loader)
92
+
93
+ target_config.dependencies.each do |dependency_name|
94
+ info("processing #{dependency_name}")
95
+
96
+ dependency_target = dependency_targets[dependency_name]
97
+ build_graph = build_graphs[dependency_name]
98
+
99
+ @rebuild_evaluator.evaluate_build_graph(build_graph)
100
+ unpack_cached_artifacts(build_graph)
101
+ rebuild_if_needed(xcodebuild_executor, dependency_target, build_graph)
102
+ @injector.perform_outgoing_injection(build_graph, target)
73
103
  end
74
104
  end
75
105
 
76
106
  # @param [Xcodeproj::Project::Object::PBXNativeTarget] target
77
107
  # @param [String] dependency_name
78
108
  #
79
- def handle_dependency(target, dependency_name)
80
- info("checking #{dependency_name}")
81
-
109
+ # @return [Xcodeproj::Project::Object::PBXNativeTarget]
110
+ #
111
+ def find_dependency_target(target, dependency_name)
82
112
  dependency_target = @native_target_finder.find_for_product_name(dependency_name)
83
113
  unless dependency_target
84
- raise Informative, "Target not found for #{dependency_name} of #{target.display_name}"
114
+ raise XcodeArchiveCache::Informative, "Target not found for #{dependency_name} of #{target.display_name}"
85
115
  end
86
116
 
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
- evaluate_for_rebuild(graph)
96
- unpack_cached_artifacts(graph)
97
- rebuild_if_needed(xcodebuild_executor, dependency_target, graph)
98
- @injector.perform_outgoing_injection(graph, target)
99
- end
100
-
101
- # @param [XcodeArchiveCache::BuildGraph::Graph] graph
102
- #
103
- def evaluate_for_rebuild(graph)
104
- graph.nodes.each do |node|
105
- @rebuild_evaluator.evaluate(node)
106
- end
117
+ dependency_target
107
118
  end
108
119
 
109
120
  # @param [XcodeArchiveCache::BuildGraph::Graph] graph