cocoapods-tt 0.0.1

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.
Files changed (124) hide show
  1. checksums.yaml +7 -0
  2. data/lib/cocoapods-tt/command/native/install.rb +56 -0
  3. data/lib/cocoapods-tt/command/native/update.rb +157 -0
  4. data/lib/cocoapods-tt/command/tt/make.rb +92 -0
  5. data/lib/cocoapods-tt/command/tt.rb +115 -0
  6. data/lib/cocoapods-tt/command.rb +1 -0
  7. data/lib/cocoapods-tt/gem_version.rb +3 -0
  8. data/lib/cocoapods-tt/native/command.rb +185 -0
  9. data/lib/cocoapods-tt/native/config.rb +366 -0
  10. data/lib/cocoapods-tt/native/core_overrides.rb +1 -0
  11. data/lib/cocoapods-tt/native/downloader/cache.rb +322 -0
  12. data/lib/cocoapods-tt/native/downloader/request.rb +86 -0
  13. data/lib/cocoapods-tt/native/downloader/response.rb +16 -0
  14. data/lib/cocoapods-tt/native/downloader.rb +192 -0
  15. data/lib/cocoapods-tt/native/executable.rb +247 -0
  16. data/lib/cocoapods-tt/native/external_sources/abstract_external_source.rb +205 -0
  17. data/lib/cocoapods-tt/native/external_sources/downloader_source.rb +30 -0
  18. data/lib/cocoapods-tt/native/external_sources/path_source.rb +55 -0
  19. data/lib/cocoapods-tt/native/external_sources/podspec_source.rb +54 -0
  20. data/lib/cocoapods-tt/native/external_sources.rb +57 -0
  21. data/lib/cocoapods-tt/native/gem_version.rb +5 -0
  22. data/lib/cocoapods-tt/native/generator/acknowledgements/markdown.rb +44 -0
  23. data/lib/cocoapods-tt/native/generator/acknowledgements/plist.rb +94 -0
  24. data/lib/cocoapods-tt/native/generator/acknowledgements.rb +107 -0
  25. data/lib/cocoapods-tt/native/generator/app_target_helper.rb +363 -0
  26. data/lib/cocoapods-tt/native/generator/bridge_support.rb +22 -0
  27. data/lib/cocoapods-tt/native/generator/constant.rb +19 -0
  28. data/lib/cocoapods-tt/native/generator/copy_dsyms_script.rb +56 -0
  29. data/lib/cocoapods-tt/native/generator/copy_resources_script.rb +223 -0
  30. data/lib/cocoapods-tt/native/generator/copy_xcframework_script.rb +227 -0
  31. data/lib/cocoapods-tt/native/generator/dummy_source.rb +31 -0
  32. data/lib/cocoapods-tt/native/generator/embed_frameworks_script.rb +196 -0
  33. data/lib/cocoapods-tt/native/generator/file_list.rb +39 -0
  34. data/lib/cocoapods-tt/native/generator/header.rb +103 -0
  35. data/lib/cocoapods-tt/native/generator/info_plist_file.rb +128 -0
  36. data/lib/cocoapods-tt/native/generator/module_map.rb +99 -0
  37. data/lib/cocoapods-tt/native/generator/prefix_header.rb +60 -0
  38. data/lib/cocoapods-tt/native/generator/script_phase_constants.rb +100 -0
  39. data/lib/cocoapods-tt/native/generator/umbrella_header.rb +46 -0
  40. data/lib/cocoapods-tt/native/hooks_manager.rb +132 -0
  41. data/lib/cocoapods-tt/native/installer/analyzer/analysis_result.rb +87 -0
  42. data/lib/cocoapods-tt/native/installer/analyzer/locking_dependency_analyzer.rb +103 -0
  43. data/lib/cocoapods-tt/native/installer/analyzer/pod_variant.rb +87 -0
  44. data/lib/cocoapods-tt/native/installer/analyzer/pod_variant_set.rb +175 -0
  45. data/lib/cocoapods-tt/native/installer/analyzer/podfile_dependency_cache.rb +55 -0
  46. data/lib/cocoapods-tt/native/installer/analyzer/sandbox_analyzer.rb +268 -0
  47. data/lib/cocoapods-tt/native/installer/analyzer/specs_state.rb +108 -0
  48. data/lib/cocoapods-tt/native/installer/analyzer/target_inspection_result.rb +58 -0
  49. data/lib/cocoapods-tt/native/installer/analyzer/target_inspector.rb +258 -0
  50. data/lib/cocoapods-tt/native/installer/analyzer.rb +1204 -0
  51. data/lib/cocoapods-tt/native/installer/base_install_hooks_context.rb +135 -0
  52. data/lib/cocoapods-tt/native/installer/installation_options.rb +195 -0
  53. data/lib/cocoapods-tt/native/installer/pod_source_installer.rb +224 -0
  54. data/lib/cocoapods-tt/native/installer/pod_source_preparer.rb +77 -0
  55. data/lib/cocoapods-tt/native/installer/podfile_validator.rb +168 -0
  56. data/lib/cocoapods-tt/native/installer/post_install_hooks_context.rb +9 -0
  57. data/lib/cocoapods-tt/native/installer/post_integrate_hooks_context.rb +9 -0
  58. data/lib/cocoapods-tt/native/installer/pre_install_hooks_context.rb +51 -0
  59. data/lib/cocoapods-tt/native/installer/pre_integrate_hooks_context.rb +9 -0
  60. data/lib/cocoapods-tt/native/installer/project_cache/project_cache.rb +11 -0
  61. data/lib/cocoapods-tt/native/installer/project_cache/project_cache_analysis_result.rb +53 -0
  62. data/lib/cocoapods-tt/native/installer/project_cache/project_cache_analyzer.rb +200 -0
  63. data/lib/cocoapods-tt/native/installer/project_cache/project_cache_version.rb +43 -0
  64. data/lib/cocoapods-tt/native/installer/project_cache/project_installation_cache.rb +103 -0
  65. data/lib/cocoapods-tt/native/installer/project_cache/project_metadata_cache.rb +73 -0
  66. data/lib/cocoapods-tt/native/installer/project_cache/target_cache_key.rb +176 -0
  67. data/lib/cocoapods-tt/native/installer/project_cache/target_metadata.rb +74 -0
  68. data/lib/cocoapods-tt/native/installer/sandbox_dir_cleaner.rb +105 -0
  69. data/lib/cocoapods-tt/native/installer/sandbox_header_paths_installer.rb +45 -0
  70. data/lib/cocoapods-tt/native/installer/source_provider_hooks_context.rb +34 -0
  71. data/lib/cocoapods-tt/native/installer/target_uuid_generator.rb +34 -0
  72. data/lib/cocoapods-tt/native/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +179 -0
  73. data/lib/cocoapods-tt/native/installer/user_project_integrator/target_integrator.rb +815 -0
  74. data/lib/cocoapods-tt/native/installer/user_project_integrator.rb +280 -0
  75. data/lib/cocoapods-tt/native/installer/xcode/multi_pods_project_generator.rb +82 -0
  76. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/aggregate_target_dependency_installer.rb +66 -0
  77. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/aggregate_target_installer.rb +192 -0
  78. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/app_host_installer.rb +154 -0
  79. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/file_references_installer.rb +329 -0
  80. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pod_target_dependency_installer.rb +195 -0
  81. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pod_target_installer.rb +1239 -0
  82. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pod_target_integrator.rb +312 -0
  83. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pods_project_writer.rb +90 -0
  84. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/project_generator.rb +120 -0
  85. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/target_installation_result.rb +140 -0
  86. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/target_installer.rb +257 -0
  87. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/target_installer_helper.rb +110 -0
  88. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator.rb +291 -0
  89. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator_result.rb +54 -0
  90. data/lib/cocoapods-tt/native/installer/xcode/single_pods_project_generator.rb +38 -0
  91. data/lib/cocoapods-tt/native/installer/xcode/target_validator.rb +170 -0
  92. data/lib/cocoapods-tt/native/installer/xcode.rb +11 -0
  93. data/lib/cocoapods-tt/native/installer.rb +1044 -0
  94. data/lib/cocoapods-tt/native/native_target_extension.rb +60 -0
  95. data/lib/cocoapods-tt/native/open-uri.rb +33 -0
  96. data/lib/cocoapods-tt/native/podfile.rb +13 -0
  97. data/lib/cocoapods-tt/native/project.rb +544 -0
  98. data/lib/cocoapods-tt/native/resolver/lazy_specification.rb +88 -0
  99. data/lib/cocoapods-tt/native/resolver/resolver_specification.rb +41 -0
  100. data/lib/cocoapods-tt/native/resolver.rb +600 -0
  101. data/lib/cocoapods-tt/native/sandbox/file_accessor.rb +532 -0
  102. data/lib/cocoapods-tt/native/sandbox/headers_store.rb +163 -0
  103. data/lib/cocoapods-tt/native/sandbox/path_list.rb +242 -0
  104. data/lib/cocoapods-tt/native/sandbox/pod_dir_cleaner.rb +71 -0
  105. data/lib/cocoapods-tt/native/sandbox/podspec_finder.rb +23 -0
  106. data/lib/cocoapods-tt/native/sandbox.rb +470 -0
  107. data/lib/cocoapods-tt/native/sources_manager.rb +221 -0
  108. data/lib/cocoapods-tt/native/target/aggregate_target.rb +558 -0
  109. data/lib/cocoapods-tt/native/target/build_settings.rb +1385 -0
  110. data/lib/cocoapods-tt/native/target/pod_target.rb +1168 -0
  111. data/lib/cocoapods-tt/native/target.rb +378 -0
  112. data/lib/cocoapods-tt/native/user_interface/error_report.rb +204 -0
  113. data/lib/cocoapods-tt/native/user_interface/inspector_reporter.rb +102 -0
  114. data/lib/cocoapods-tt/native/user_interface.rb +463 -0
  115. data/lib/cocoapods-tt/native/validator.rb +1170 -0
  116. data/lib/cocoapods-tt/native/version_metadata.rb +26 -0
  117. data/lib/cocoapods-tt/native/xcode/framework_paths.rb +54 -0
  118. data/lib/cocoapods-tt/native/xcode/linkage_analyzer.rb +22 -0
  119. data/lib/cocoapods-tt/native/xcode/xcframework/xcframework_slice.rb +138 -0
  120. data/lib/cocoapods-tt/native/xcode/xcframework.rb +99 -0
  121. data/lib/cocoapods-tt/native/xcode.rb +7 -0
  122. data/lib/cocoapods-tt.rb +1 -0
  123. data/lib/cocoapods_plugin.rb +17 -0
  124. metadata +193 -0
@@ -0,0 +1,168 @@
1
+ module Pod
2
+ class Installer
3
+ # Validate the podfile before installing to catch errors and
4
+ # problems
5
+ #
6
+ class PodfileValidator
7
+ # @return [Podfile] The podfile being validated
8
+ #
9
+ attr_reader :podfile
10
+
11
+ # @return [Array<String>] any errors that have occurred during the validation
12
+ #
13
+ attr_reader :errors
14
+
15
+ # @return [Array<String>] any warnings that have occurred during the validation
16
+ #
17
+ attr_reader :warnings
18
+
19
+ # Initialize a new instance
20
+ #
21
+ # @param [Podfile] podfile
22
+ # The podfile to validate
23
+ #
24
+ # @param [Analyzer::PodfileDependencyCache] podfile_dependency_cache
25
+ # An (optional) cache of all the dependencies in the podfile
26
+ #
27
+ def initialize(podfile, podfile_dependency_cache = Analyzer::PodfileDependencyCache.from_podfile(podfile))
28
+ @podfile = podfile
29
+ @podfile_dependency_cache = podfile_dependency_cache
30
+ @errors = []
31
+ @warnings = []
32
+ @validated = false
33
+ end
34
+
35
+ # Validate the podfile
36
+ # Errors are added to the errors array
37
+ #
38
+ def validate
39
+ validate_installation_options
40
+ validate_pod_directives
41
+ validate_no_abstract_only_pods!
42
+ validate_dependencies_are_present!
43
+ validate_no_duplicate_targets!
44
+
45
+ @validated = true
46
+ end
47
+
48
+ # Wether the podfile is valid is not
49
+ # NOTE: Will execute `validate` if the podfile
50
+ # has not yet been validated
51
+ #
52
+ def valid?
53
+ validate unless @validated
54
+
55
+ @validated && errors.empty?
56
+ end
57
+
58
+ # A message describing any errors in the
59
+ # validation
60
+ #
61
+ def message
62
+ errors.join("\n")
63
+ end
64
+
65
+ private
66
+
67
+ def add_error(error)
68
+ errors << error
69
+ end
70
+
71
+ def add_warning(warning)
72
+ warnings << warning
73
+ end
74
+
75
+ def validate_installation_options
76
+ installation_options = podfile.installation_options
77
+
78
+ # Validate `incremental_installation` depends on `generate_multiple_pod_projects`
79
+ invalid = installation_options.incremental_installation? && installation_options.incremental_installation != installation_options.generate_multiple_pod_projects
80
+ add_error 'The installation option `incremental_installation` requires the option `generate_multiple_pod_projects` to also be enabled.' if invalid
81
+ end
82
+
83
+ def validate_pod_directives
84
+ @podfile_dependency_cache.podfile_dependencies.each do |dependency|
85
+ validate_conflicting_external_sources!(dependency)
86
+ end
87
+ end
88
+
89
+ def validate_conflicting_external_sources!(dependency)
90
+ external_source = dependency.external_source
91
+ return false if external_source.nil?
92
+
93
+ available_downloaders = Downloader.downloader_class_by_key.keys
94
+ specified_downloaders = external_source.select { |key| available_downloaders.include?(key) }
95
+ if specified_downloaders.size > 1
96
+ add_error "The dependency `#{dependency.name}` specifies more than one download strategy(#{specified_downloaders.keys.join(',')})." \
97
+ 'Only one is allowed'
98
+ end
99
+
100
+ pod_spec_or_path = external_source[:podspec].present? || external_source[:path].present?
101
+ if pod_spec_or_path && specified_downloaders.size > 0
102
+ add_error "The dependency `#{dependency.name}` specifies `podspec` or `path` in combination with other" \
103
+ ' download strategies. This is not allowed'
104
+ end
105
+ end
106
+
107
+ # Warns the user if the podfile is empty.
108
+ #
109
+ # @note The workspace is created in any case and all the user projects
110
+ # are added to it, however the projects are not integrated as
111
+ # there is no way to discern between target definitions which are
112
+ # empty and target definitions which just serve the purpose to
113
+ # wrap other ones. This is not an issue because empty target
114
+ # definitions generate empty libraries.
115
+ #
116
+ # @return [void]
117
+ #
118
+ def validate_dependencies_are_present!
119
+ if @podfile_dependency_cache.target_definition_list.all?(&:empty?)
120
+ add_warning 'The Podfile does not contain any dependencies.'
121
+ end
122
+ end
123
+
124
+ # Verifies that no dependencies in the Podfile will end up not being built
125
+ # at all. In other words, all dependencies should belong to a non-abstract
126
+ # target, or be inherited by a target where `inheritance == complete`.
127
+ #
128
+ def validate_no_abstract_only_pods!
129
+ @podfile_dependency_cache.target_definition_list.each do |target_definition|
130
+ dependencies = @podfile_dependency_cache.target_definition_dependencies(target_definition)
131
+ next if dependencies.empty?
132
+ next unless target_definition.abstract?
133
+
134
+ children = target_definition.recursive_children
135
+ next if children.any? { |child_target_definition| target_definition_inherits?(:parent => target_definition, :child => child_target_definition) }
136
+
137
+ add_warning "The abstract target #{target_definition.name} is not inherited by a concrete target, " \
138
+ "so the following dependencies won't make it into any targets in your project:" \
139
+ "\n - #{dependencies.map(&:to_s).sort.join("\n - ")}"
140
+
141
+ next if target_definition.platform
142
+
143
+ add_error "The abstract target #{target_definition.name} must specify a platform since its dependencies are not inherited by a concrete target."
144
+ end
145
+ end
146
+
147
+ def target_definition_inherits?(parent: nil, child: nil)
148
+ if parent == child
149
+ true
150
+ elsif child.exclusive?
151
+ false
152
+ else
153
+ target_definition_inherits?(:parent => parent, :child => child.parent)
154
+ end
155
+ end
156
+
157
+ def validate_no_duplicate_targets!
158
+ @podfile_dependency_cache.target_definition_list.group_by { |td| [td.name, td.user_project_path] }.
159
+ each do |(name, project), definitions|
160
+ next unless definitions.size > 1
161
+ error = "The target `#{name}` is declared multiple times"
162
+ error << " for the project `#{project}`" if project
163
+ add_error(error << '.')
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,9 @@
1
+ module Pod
2
+ class Installer
3
+ # Context object designed to be used with the HooksManager which describes
4
+ # the context of the installer.
5
+ #
6
+ class PostInstallHooksContext < BaseInstallHooksContext
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Pod
2
+ class Installer
3
+ # Context object designed to be used with the HooksManager which describes
4
+ # the context of the installer.
5
+ #
6
+ class PostIntegrateHooksContext < BaseInstallHooksContext
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,51 @@
1
+ module Pod
2
+ class Installer
3
+ # Context object designed to be used with the HooksManager which describes
4
+ # the context of the installer before analysis has been completed.
5
+ #
6
+ class PreInstallHooksContext
7
+ # @return [Podfile] The Podfile for the project.
8
+ #
9
+ attr_reader :podfile
10
+
11
+ # @return [Sandbox] The Sandbox for the project.
12
+ #
13
+ attr_reader :sandbox
14
+
15
+ # @return [String] The path to the sandbox root (`Pods` directory).
16
+ #
17
+ attr_reader :sandbox_root
18
+
19
+ # @return [Lockfile] The Lockfile for the project.
20
+ #
21
+ attr_reader :lockfile
22
+
23
+ # Initialize a new instance
24
+ #
25
+ # @param [Sandbox] sandbox see #sandbox
26
+ # @param [String] sandbox_root see #sandbox_root
27
+ # @param [Podfile] podfile see #podfile
28
+ # @param [Lockfile] lockfile see #lockfile
29
+ #
30
+ def initialize(podfile, sandbox, sandbox_root, lockfile)
31
+ @podfile = podfile
32
+ @sandbox = sandbox
33
+ @sandbox_root = sandbox_root
34
+ @lockfile = lockfile
35
+ end
36
+
37
+ # @param [Sandbox] sandbox see {#sandbox}
38
+ #
39
+ # @param [Podfile] podfile see {#podfile}
40
+ #
41
+ # @param [Lockfile] lockfile see {#lockfile}
42
+ #
43
+ # @return [PreInstallHooksContext] Convenience class method to generate the
44
+ # static context.
45
+ #
46
+ def self.generate(sandbox, podfile, lockfile)
47
+ new(podfile, sandbox, sandbox.root.to_s, lockfile)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,9 @@
1
+ module Pod
2
+ class Installer
3
+ # Context object designed to be used with the HooksManager which describes
4
+ # the context of the installer.
5
+ #
6
+ class PreIntegrateHooksContext < BaseInstallHooksContext
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module Pod
2
+ class Installer
3
+ module ProjectCache
4
+ autoload :ProjectCacheAnalyzer, 'cocoapods/installer/project_cache/project_cache_analyzer'
5
+ autoload :ProjectInstallationCache, 'cocoapods/installer/project_cache/project_installation_cache'
6
+ autoload :ProjectMetadataCache, 'cocoapods/installer/project_cache/project_metadata_cache'
7
+ autoload :ProjectCacheAnalysisResult, 'cocoapods/installer/project_cache/project_cache_analysis_result'
8
+ autoload :ProjectCacheVersion, 'cocoapods/installer/project_cache/project_cache_version'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,53 @@
1
+ module Pod
2
+ class Installer
3
+ module ProjectCache
4
+ # The result object from analyzing the project cache.
5
+ #
6
+ class ProjectCacheAnalysisResult
7
+ # @return [Array<PodTarget>]
8
+ # The list of pod targets that need to be regenerated.
9
+ #
10
+ attr_reader :pod_targets_to_generate
11
+
12
+ # @return [Array<AggregateTarget>]
13
+ # The list of aggregate targets that need to be regenerated. This can be nil if we don't want to
14
+ # generate ANY aggregate targets since we still want to be able to generate an empty list of aggregate
15
+ # targets.
16
+ #
17
+ attr_reader :aggregate_targets_to_generate
18
+
19
+ # @return [Hash{String => TargetCacheKey}]
20
+ # Updated hash of target cache key by target label for all targets.
21
+ #
22
+ attr_reader :cache_key_by_target_label
23
+
24
+ # @return [Hash{String => Symbol}]
25
+ # The build configurations to install with each target.
26
+ #
27
+ attr_reader :build_configurations
28
+
29
+ # @return [Integer]
30
+ # The project object version to install with each target.
31
+ #
32
+ attr_reader :project_object_version
33
+
34
+ # Initialize a new instance.
35
+ #
36
+ # @param [Array<PodTarget>] pod_targets_to_generate @see #pod_targets_to_generate
37
+ # @param [Array<AggregateTarget] aggregate_targets_to_generate @see #aggregate_targets_to_generate
38
+ # @param [Hash{String => TargetCacheKey}] cache_key_by_target_label @see #cache_key_by_target_label
39
+ # @param [Hash{String => Symbol}] build_configurations @see #build_configurations
40
+ # @param [Integer] project_object_version @see #project_object_version
41
+ #
42
+ def initialize(pod_targets_to_generate, aggregate_targets_to_generate, cache_key_by_target_label,
43
+ build_configurations, project_object_version)
44
+ @pod_targets_to_generate = pod_targets_to_generate
45
+ @aggregate_targets_to_generate = aggregate_targets_to_generate
46
+ @cache_key_by_target_label = cache_key_by_target_label
47
+ @build_configurations = build_configurations
48
+ @project_object_version = project_object_version
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,200 @@
1
+ module Pod
2
+ class Installer
3
+ module ProjectCache
4
+ # Analyzes the project cache and computes which pod targets need to be generated.
5
+ #
6
+ class ProjectCacheAnalyzer
7
+ require 'cocoapods/installer/project_cache/project_cache_analysis_result'
8
+
9
+ # @return [Sandbox] Project sandbox.
10
+ #
11
+ attr_reader :sandbox
12
+
13
+ # @return [ProjectInstallationCache] The cache of targets that were previously installed.
14
+ #
15
+ attr_reader :cache
16
+
17
+ # @return [Hash{String => Symbol}] The hash of user build configurations.
18
+ #
19
+ attr_reader :build_configurations
20
+
21
+ # @return [Integer] The object version from the user project.
22
+ #
23
+ attr_reader :project_object_version
24
+
25
+ # @return [Hash<String, Hash>] The podfile plugins to be run for the installation.
26
+ #
27
+ attr_reader :podfile_plugins
28
+
29
+ # @return [Array<PodTarget>] The list of pod targets.
30
+ #
31
+ attr_reader :pod_targets
32
+
33
+ # @return [Array<AggregateTarget>] The list of aggregate targets.
34
+ #
35
+ attr_reader :aggregate_targets
36
+
37
+ # @return [Hash<Symbol, Object>] Hash of installation options.
38
+ #
39
+ attr_reader :installation_options
40
+
41
+ # @return [Bool] Flag indicating if we want to ignore the cache and force a clean installation.
42
+ #
43
+ attr_reader :clean_install
44
+
45
+ # Initialize a new instance.
46
+ #
47
+ # @param [Sandbox] sandbox @see #sandbox
48
+ # @param [ProjectInstallationCache] cache @see #cache
49
+ # @param [Hash{String => Symbol}] build_configurations @see #build_configurations
50
+ # @param [Integer] project_object_version @see #project_object_version
51
+ # @param [Hash<String, Hash>] podfile_plugins @see #podfile_plugins
52
+ # @param [Array<PodTarget>] pod_targets @see #pod_targets
53
+ # @param [Array<AggregateTarget>] aggregate_targets @see #aggregate_targets
54
+ # @param [Hash<Symbol, Object>] installation_options @see #installation_options
55
+ # @param [Bool] clean_install @see #clean_install
56
+ #
57
+ def initialize(sandbox, cache, build_configurations, project_object_version, podfile_plugins, pod_targets, aggregate_targets, installation_options,
58
+ clean_install: false)
59
+ @sandbox = sandbox
60
+ @cache = cache
61
+ @build_configurations = build_configurations
62
+ @podfile_plugins = podfile_plugins
63
+ @pod_targets = pod_targets
64
+ @aggregate_targets = aggregate_targets
65
+ @project_object_version = project_object_version
66
+ @installation_options = installation_options
67
+ @clean_install = clean_install
68
+ end
69
+
70
+ # @return [ProjectCacheAnalysisResult]
71
+ # Compares all targets stored against the cache and computes which targets need to be regenerated.
72
+ #
73
+ def analyze
74
+ target_by_label = Hash[(pod_targets + aggregate_targets).map { |target| [target.label, target] }]
75
+ cache_key_by_target_label = create_cache_key_mappings(target_by_label)
76
+
77
+ full_install_results = ProjectCacheAnalysisResult.new(pod_targets, aggregate_targets, cache_key_by_target_label,
78
+ build_configurations, project_object_version)
79
+ if clean_install
80
+ UI.message 'Ignoring project cache from the provided `--clean-install` flag.'
81
+ return full_install_results
82
+ end
83
+
84
+ # Bail out early since these properties affect all targets and their associate projects.
85
+ if cache.build_configurations != build_configurations ||
86
+ cache.project_object_version != project_object_version ||
87
+ YAMLHelper.convert(cache.podfile_plugins) != YAMLHelper.convert(podfile_plugins) ||
88
+ YAMLHelper.convert(cache.installation_options) != YAMLHelper.convert(installation_options)
89
+ UI.message 'Ignoring project cache due to project configuration changes.'
90
+ return full_install_results
91
+ end
92
+
93
+ if project_names_changed?(pod_targets, cache)
94
+ UI.message 'Ignoring project cache due to project name changes.'
95
+ return full_install_results
96
+ end
97
+
98
+ pod_targets_to_generate = Set[]
99
+ aggregate_targets_to_generate = Set[]
100
+ added_targets = compute_added_targets(target_by_label, cache_key_by_target_label.keys, cache.cache_key_by_target_label.keys)
101
+ added_pod_targets, added_aggregate_targets = added_targets.partition { |target| target.is_a?(PodTarget) }
102
+ pod_targets_to_generate.merge(added_pod_targets)
103
+ aggregate_targets_to_generate.merge(added_aggregate_targets)
104
+
105
+ removed_aggregate_target_labels = compute_removed_targets(cache_key_by_target_label.keys, cache.cache_key_by_target_label.keys)
106
+
107
+ changed_targets = compute_changed_targets_from_cache(cache_key_by_target_label, target_by_label, cache)
108
+ changed_pod_targets, changed_aggregate_targets = changed_targets.partition { |target| target.is_a?(PodTarget) }
109
+ pod_targets_to_generate.merge(changed_pod_targets)
110
+ aggregate_targets_to_generate.merge(changed_aggregate_targets)
111
+
112
+ dirty_targets = compute_dirty_targets(pod_targets + aggregate_targets)
113
+ dirty_pod_targets, dirty_aggregate_targets = dirty_targets.partition { |target| target.is_a?(PodTarget) }
114
+ pod_targets_to_generate.merge(dirty_pod_targets)
115
+ aggregate_targets_to_generate.merge(dirty_aggregate_targets)
116
+
117
+ # Since multi xcodeproj will group targets by PodTarget#project_name into individual projects, we need to
118
+ # append these "sibling" targets to the list of targets we need to generate before finalizing the total list,
119
+ # otherwise we will end up with missing targets.
120
+ #
121
+ sibling_pod_targets = compute_sibling_pod_targets(pod_targets, pod_targets_to_generate)
122
+ pod_targets_to_generate.merge(sibling_pod_targets)
123
+
124
+ # We either return the full list of aggregate targets or none since the aggregate targets go into the
125
+ # Pods.xcodeproj and so we need to regenerate all aggregate targets when regenerating Pods.xcodeproj.
126
+ total_aggregate_targets_to_generate = unless aggregate_targets_to_generate.empty? && removed_aggregate_target_labels.empty?
127
+ aggregate_targets
128
+ end
129
+
130
+ ProjectCacheAnalysisResult.new(pod_targets_to_generate.to_a, total_aggregate_targets_to_generate,
131
+ cache_key_by_target_label, build_configurations, project_object_version)
132
+ end
133
+
134
+ private
135
+
136
+ def create_cache_key_mappings(target_by_label)
137
+ Hash[target_by_label.map do |label, target|
138
+ case target
139
+ when PodTarget
140
+ local = sandbox.local?(target.pod_name)
141
+ checkout_options = sandbox.checkout_sources[target.pod_name]
142
+ [label, TargetCacheKey.from_pod_target(sandbox, target, :is_local_pod => local,
143
+ :checkout_options => checkout_options)]
144
+ when AggregateTarget
145
+ [label, TargetCacheKey.from_aggregate_target(sandbox, target)]
146
+ else
147
+ raise "[BUG] Unknown target type #{target}"
148
+ end
149
+ end]
150
+ end
151
+
152
+ def compute_added_targets(target_by_label, target_labels, cached_target_labels)
153
+ (target_labels - cached_target_labels).map do |label|
154
+ target_by_label[label]
155
+ end
156
+ end
157
+
158
+ def compute_removed_targets(target_labels, cached_target_labels)
159
+ cached_target_labels - target_labels
160
+ end
161
+
162
+ def compute_changed_targets_from_cache(cache_key_by_target_label, target_by_label, cache)
163
+ cache_key_by_target_label.each_with_object([]) do |(label, cache_key), changed_targets|
164
+ next unless cache.cache_key_by_target_label[label]
165
+ if cache_key.key_difference(cache.cache_key_by_target_label[label]) == :project
166
+ changed_targets << target_by_label[label]
167
+ end
168
+ end
169
+ end
170
+
171
+ def compute_dirty_targets(targets)
172
+ targets.reject do |target|
173
+ support_files_dir_exists = File.exist? target.support_files_dir
174
+ xcodeproj_exists = case target
175
+ when PodTarget
176
+ File.exist? sandbox.pod_target_project_path(target.project_name)
177
+ when AggregateTarget
178
+ File.exist? sandbox.project_path
179
+ else
180
+ raise "[BUG] Unknown target type #{target}"
181
+ end
182
+ support_files_dir_exists && xcodeproj_exists
183
+ end
184
+ end
185
+
186
+ def compute_sibling_pod_targets(pod_targets, pod_targets_to_generate)
187
+ pod_targets_by_project_name = pod_targets.group_by(&:project_name)
188
+ pod_targets_to_generate.flat_map { |t| pod_targets_by_project_name[t.project_name] }
189
+ end
190
+
191
+ def project_names_changed?(pod_targets, cache)
192
+ pod_targets.any? do |pod_target|
193
+ next unless (target_cache_key = cache.cache_key_by_target_label[pod_target.label])
194
+ target_cache_key.project_name != pod_target.project_name
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,43 @@
1
+ module Pod
2
+ class Installer
3
+ module ProjectCache
4
+ # Object that stores, loads, and holds the version of the project cache.
5
+ #
6
+ class ProjectCacheVersion
7
+ # @return [Version] The version of the project cache.
8
+ #
9
+ attr_reader :version
10
+
11
+ # Initialize a new instance.
12
+ #
13
+ # @param [Version] version @see #version
14
+ #
15
+ def initialize(version = Version.create('0'))
16
+ @version = version
17
+ end
18
+
19
+ # Constructs a ProjectCacheVersion from a file.
20
+ #
21
+ # @param [String] path
22
+ # The path of the project cache
23
+ #
24
+ # @return [ProjectCacheVersion]
25
+ #
26
+ def self.from_file(path)
27
+ return ProjectCacheVersion.new unless File.exist?(path)
28
+ cached_version = Version.create(File.read(path))
29
+ ProjectCacheVersion.new(cached_version)
30
+ end
31
+
32
+ # @return [void]
33
+ #
34
+ # @param [String] path
35
+ # The path of the project cache to save.
36
+ #
37
+ def save_as(path)
38
+ Sandbox.update_changed_file(path, version.to_s)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,103 @@
1
+ module Pod
2
+ class Installer
3
+ module ProjectCache
4
+ # Represents the cache stored at Pods/.project/installation_cache
5
+ #
6
+ class ProjectInstallationCache
7
+ require 'cocoapods/installer/project_cache/target_cache_key'
8
+
9
+ # @return [Hash{String => TargetCacheKey}]
10
+ # Stored hash of target cache key objects for every pod target.
11
+ #
12
+ attr_reader :cache_key_by_target_label
13
+
14
+ # @return [Hash{String => Symbol}]
15
+ # Build configurations stored in the cache.
16
+ #
17
+ attr_reader :build_configurations
18
+
19
+ # @return [Integer]
20
+ # Project object stored in the cache.
21
+ #
22
+ attr_reader :project_object_version
23
+
24
+ # @return [Hash<String, Hash>]
25
+ # Podfile plugins used with a particular install.
26
+ #
27
+ attr_reader :podfile_plugins
28
+
29
+ # @return [Hash<Symbol, Object>]
30
+ # Configured installation options
31
+ #
32
+ attr_reader :installation_options
33
+
34
+ # Initializes a new instance.
35
+ #
36
+ # @param [Hash{String => TargetCacheKey}] cache_key_by_target_label @see #cache_key_by_target_label
37
+ # @param [Hash{String => Symbol}] build_configurations @see #build_configurations
38
+ # @param [Integer] project_object_version @see #project_object_version
39
+ # @param [Hash<String, Hash>] podfile_plugins @see #podfile_plugins
40
+ # @param [Hash<Symbol, Object>] installation_options @see #installation_options
41
+ #
42
+ def initialize(cache_key_by_target_label = {}, build_configurations = nil, project_object_version = nil, podfile_plugins = {}, installation_options = {})
43
+ @cache_key_by_target_label = cache_key_by_target_label
44
+ @build_configurations = build_configurations
45
+ @project_object_version = project_object_version
46
+ @podfile_plugins = podfile_plugins
47
+ @installation_options = installation_options
48
+ end
49
+
50
+ def update_cache_key_by_target_label!(cache_key_by_target_label)
51
+ @cache_key_by_target_label = cache_key_by_target_label
52
+ end
53
+
54
+ def update_build_configurations!(build_configurations)
55
+ @build_configurations = build_configurations
56
+ end
57
+
58
+ def update_project_object_version!(project_object_version)
59
+ @project_object_version = project_object_version
60
+ end
61
+
62
+ def update_podfile_plugins!(podfile_plugins)
63
+ @podfile_plugins = podfile_plugins
64
+ end
65
+
66
+ def update_installation_options!(installation_options)
67
+ @installation_options = installation_options
68
+ end
69
+
70
+ def save_as(path)
71
+ Pathname(path).dirname.mkpath
72
+ Sandbox.update_changed_file(path, YAMLHelper.convert(to_hash))
73
+ end
74
+
75
+ def self.from_file(sandbox, path)
76
+ return ProjectInstallationCache.new unless File.exist?(path)
77
+ contents = YAMLHelper.load_file(path)
78
+ cache_keys = contents.fetch('CACHE_KEYS', {})
79
+ cache_key_by_target_label = Hash[cache_keys.map do |name, key_hash|
80
+ [name, TargetCacheKey.from_cache_hash(sandbox, key_hash)]
81
+ end]
82
+ project_object_version = contents['OBJECT_VERSION']
83
+ build_configurations = contents['BUILD_CONFIGURATIONS']
84
+ podfile_plugins = contents['PLUGINS']
85
+ installation_options = contents['INSTALLATION_OPTIONS']
86
+ ProjectInstallationCache.new(cache_key_by_target_label, build_configurations, project_object_version, podfile_plugins, installation_options)
87
+ end
88
+
89
+ def to_hash
90
+ cache_key_contents = Hash[cache_key_by_target_label.map do |label, key|
91
+ [label, key.to_h]
92
+ end]
93
+ contents = { 'CACHE_KEYS' => cache_key_contents }
94
+ contents['BUILD_CONFIGURATIONS'] = build_configurations if build_configurations
95
+ contents['OBJECT_VERSION'] = project_object_version if project_object_version
96
+ contents['PLUGINS'] = podfile_plugins if podfile_plugins
97
+ contents['INSTALLATION_OPTIONS'] = installation_options if installation_options
98
+ contents
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end