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.
@@ -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