xccache 0.0.1a1 → 0.0.1a2

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/lib/xccache/assets/templates/cachemap.html.template +54 -0
  3. data/lib/xccache/assets/templates/cachemap.js.template +94 -0
  4. data/lib/xccache/assets/templates/cachemap.style.css.template +92 -0
  5. data/lib/xccache/assets/templates/framework.info.plist.template +25 -0
  6. data/lib/xccache/assets/templates/framework.modulemap.template +6 -0
  7. data/lib/xccache/assets/templates/resource_bundle_accessor.m.template +27 -0
  8. data/lib/xccache/assets/templates/resource_bundle_accessor.swift.template +24 -0
  9. data/lib/xccache/assets/templates/umbrella.Package.swift.template +147 -0
  10. data/lib/xccache/cache/cachemap.rb +60 -0
  11. data/lib/xccache/command/build.rb +35 -0
  12. data/lib/xccache/command/pkg/build.rb +34 -0
  13. data/lib/xccache/command/rollback.rb +13 -0
  14. data/lib/xccache/command/use.rb +18 -0
  15. data/lib/xccache/command.rb +11 -1
  16. data/lib/xccache/core/cacheable.rb +28 -0
  17. data/lib/xccache/core/config.rb +82 -0
  18. data/lib/xccache/core/error.rb +7 -0
  19. data/lib/xccache/core/git.rb +19 -0
  20. data/lib/xccache/core/hash.rb +21 -0
  21. data/lib/xccache/core/lockfile.rb +40 -0
  22. data/lib/xccache/core/log.rb +41 -0
  23. data/lib/xccache/core/sh.rb +50 -0
  24. data/lib/xccache/core/syntax/hash.rb +31 -0
  25. data/lib/xccache/core/syntax/json.rb +16 -0
  26. data/lib/xccache/core/syntax/yml.rb +16 -0
  27. data/lib/xccache/core/syntax.rb +1 -0
  28. data/lib/xccache/core/system.rb +54 -0
  29. data/lib/xccache/core.rb +1 -0
  30. data/lib/xccache/framework/slice.rb +183 -0
  31. data/lib/xccache/framework/xcframework.rb +51 -0
  32. data/lib/xccache/installer/build.rb +27 -0
  33. data/lib/xccache/installer/rollback.rb +36 -0
  34. data/lib/xccache/installer/use.rb +27 -0
  35. data/lib/xccache/installer.rb +67 -0
  36. data/lib/xccache/main.rb +3 -1
  37. data/lib/xccache/spm/desc/base.rb +61 -0
  38. data/lib/xccache/spm/desc/dep.rb +40 -0
  39. data/lib/xccache/spm/desc/desc.rb +110 -0
  40. data/lib/xccache/spm/desc/product.rb +36 -0
  41. data/lib/xccache/spm/desc/target.rb +138 -0
  42. data/lib/xccache/spm/desc.rb +1 -0
  43. data/lib/xccache/spm/mixin.rb +9 -0
  44. data/lib/xccache/spm/pkg/base.rb +103 -0
  45. data/lib/xccache/spm/pkg/umbrella/build.rb +30 -0
  46. data/lib/xccache/spm/pkg/umbrella/cachemap.rb +93 -0
  47. data/lib/xccache/spm/pkg/umbrella/descs.rb +46 -0
  48. data/lib/xccache/spm/pkg/umbrella/manifest.rb +83 -0
  49. data/lib/xccache/spm/pkg/umbrella/viz.rb +40 -0
  50. data/lib/xccache/spm/pkg/umbrella.rb +81 -0
  51. data/lib/xccache/spm/pkg.rb +1 -0
  52. data/lib/xccache/spm.rb +1 -0
  53. data/lib/xccache/swift/sdk.rb +30 -0
  54. data/lib/xccache/swift/swiftc.rb +16 -0
  55. data/lib/xccache/utils/template.rb +21 -0
  56. data/lib/xccache/xcodeproj/pkg.rb +69 -0
  57. data/lib/xccache/xcodeproj/pkg_product_dependency.rb +19 -0
  58. data/lib/xccache/xcodeproj/project.rb +63 -0
  59. data/lib/xccache/xcodeproj/target.rb +50 -0
  60. data/lib/xccache/xcodeproj.rb +2 -0
  61. metadata +72 -2
@@ -0,0 +1,93 @@
1
+ module XCCache
2
+ module SPM
3
+ class Package
4
+ module UmbrellaCachemapMixin
5
+ def sync_cachemap
6
+ UI.section("Syncing cachemap")
7
+ nodes, edges, parents = xccache_desc.traverse
8
+ cache_data = gen_cache_data(nodes, parents)
9
+ targets_data, deps_data = {}, {}
10
+ xccache_desc.targets.each do |agg_target|
11
+ targets_data[agg_target.name] = agg_target.direct_dependencies.flat_map do |d|
12
+ # If any associated targets is missed -> use original product form
13
+ # Otherwise, replace with recursive targets' binaries
14
+ deps_data[d.full_name] = d.recursive_targets.map(&:full_name)
15
+ if d.recursive_targets.all? { |t| cache_data[t] == :hit }
16
+ "#{d.full_name}.binary"
17
+ else
18
+ d.full_name
19
+ end
20
+ end.uniq.sort_by(&:downcase)
21
+ end
22
+
23
+ config.cachemap.raw = {
24
+ "manifest" => {
25
+ "targets" => targets_data,
26
+ "deps" => deps_data,
27
+ },
28
+ "cache" => cache_data.transform_keys(&:full_name),
29
+ "depgraph" => {
30
+ "nodes" => nodes.map { |x| target_to_cytoscape_node(x, cache_data) },
31
+ "edges" => edges.map { |x, y| { :source => x.full_name, :target => y.full_name } },
32
+ },
33
+ }
34
+ config.cachemap.save
35
+ config.cachemap.print_stats
36
+ end
37
+
38
+ private
39
+
40
+ def gen_cache_data(nodes, parents)
41
+ result = nodes.to_h do |node|
42
+ res = if config.ignore?(node.full_name) then :ignored
43
+ else
44
+ verify_binary?(node) ? :hit : :missed
45
+ end
46
+ [node, res]
47
+ end
48
+
49
+ # Propagate cache miss
50
+ to_visit = result.select { |_, v| %i[missed ignore].include?(v) }.keys
51
+ visited = Set.new
52
+ until to_visit.empty?
53
+ node = to_visit.pop
54
+ next if visited.include?(node)
55
+ visited << node
56
+ result[node] = :missed if result[node] == :hit
57
+ to_visit += parents[node] if parents.key?(node)
58
+ end
59
+ result
60
+ end
61
+
62
+ def verify_binary?(target)
63
+ return true if target.binary?
64
+
65
+ bpath = binary_path(target.name)
66
+ bpath_with_checksum = binary_path(target.name, checksum: target.root.checksum)
67
+ # If checksum matches, create symlink from `A-abc123.xcframework` -> `A.framework`
68
+ # Otherwise, remove symlink `A.xcframework`
69
+ if bpath_with_checksum.exist?
70
+ bpath_with_checksum.symlink_to(bpath)
71
+ elsif bpath.exist?
72
+ bpath.rmtree
73
+ end
74
+ bpath_with_checksum.exist?
75
+ end
76
+
77
+ def binary_path(name, checksum: nil)
78
+ suffix = checksum.nil? ? "" : "-#{checksum}"
79
+ p = config.spm_binaries_frameworks_dir / File.basename(name, ".binary")
80
+ p / "#{p.basename}#{suffix}.xcframework"
81
+ end
82
+
83
+ def target_to_cytoscape_node(x, cache_data)
84
+ h = { :id => x.full_name, :cache => cache_data[x] }
85
+ h[:type] = "agg" if x.name.end_with?(".xccache")
86
+ h[:checksum] = x.root.checksum
87
+ h[:binary] = binary_path(x.name) if cache_data[x] == :hit
88
+ h
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,46 @@
1
+ module XCCache
2
+ module SPM
3
+ class Package
4
+ module UmbrellaDescsMixin
5
+ def gen_metadata
6
+ UI.section("Generating metadata of packages") do
7
+ dirs = [root_dir] + root_dir.glob(".build/checkouts/*").reject { |p| p.glob("Package*.swift").empty? }
8
+ dirs.each do |dir|
9
+ desc = Description.in_dir(dir, save_to_dir: config.spm_metadata_dir)
10
+ next if desc.nil?
11
+
12
+ desc.retrieve_pkg_desc = proc { |name| @descs_by_name[name] }
13
+ desc.save
14
+ desc.save(to: desc.path.parent / "#{desc.name}.json") if desc.name != dir.basename.to_s
15
+ @descs << desc
16
+ @descs_by_name[desc.name] = desc
17
+ @descs_by_name[dir.basename.to_s] = desc
18
+ end
19
+ end
20
+ end
21
+
22
+ def xccache_desc
23
+ @xccache_desc ||= desc_of("xccache")
24
+ end
25
+
26
+ def targets_of_products(products)
27
+ products = [products] if products.is_a?(String)
28
+ products.flat_map { |x| desc_of(x).targets_of_products(File.basename(x)) }
29
+ end
30
+
31
+ def dependency_targets_of_products(products)
32
+ products = [products] if products.is_a?(String)
33
+ products.flat_map { |p| @dependency_targets_by_products[p] || [p] }.uniq
34
+ end
35
+
36
+ def desc_of(d)
37
+ @descs_by_name[d.split("/").first]
38
+ end
39
+
40
+ def binary_targets
41
+ @descs_by_name.values.flatten.uniq.flat_map(&:binary_targets)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,83 @@
1
+ module XCCache
2
+ module SPM
3
+ class Package
4
+ module UmbrellaManifestMixin
5
+ def write_manifest(no_cache: false)
6
+ UI.info("Writing Package.swift (package: #{root_dir.basename.to_s.dark})")
7
+ Template.new("umbrella.Package.swift").render(
8
+ {
9
+ :timestamp => Time.new.strftime("%F %T"),
10
+ :json => manifest_targets_json(no_cache: no_cache),
11
+ :products_to_targets => manifest_products_to_targets_json(no_cache: no_cache),
12
+ :platforms => manifest_platforms,
13
+ :dependencies => manifest_pkg_dependencies,
14
+ :swift_version => Swift::Swiftc.version_without_patch,
15
+ },
16
+ save_to: root_dir / "Package.swift",
17
+ )
18
+ end
19
+
20
+ def manifest_targets_json(no_cache: false)
21
+ data = no_cache ? config.lockfile.targets_data : config.cachemap.manifest_data["targets"]
22
+ JSON.pretty_generate(data)
23
+ end
24
+
25
+ def manifest_products_to_targets_json(no_cache: false)
26
+ data = no_cache ? {} : config.cachemap.manifest_data["deps"]
27
+ JSON.pretty_generate(data)
28
+ end
29
+
30
+ def manifest_pkg_dependencies
31
+ decl = proc do |hash|
32
+ if (path_from_root = hash["path_from_root"])
33
+ absolute_path = (Pathname(".") / path_from_root).expand_path
34
+ next ".package(path: \"#{absolute_path}\")"
35
+ end
36
+
37
+ requirement = hash["requirement"]
38
+ case requirement["kind"]
39
+ when "upToNextMajorVersion"
40
+ opt = ".upToNextMajor(from: \"#{requirement['minimumVersion']}\")"
41
+ when "upToNextMinorVersion"
42
+ opt = ".upToNextMinor(from: \"#{requirement['minimumVersion']}\")"
43
+ when "exactVersion"
44
+ opt = "exact: \"#{requirement['version']}\""
45
+ when "branch"
46
+ opt = "branch: \"#{requirement['branch']}\""
47
+ when "revision"
48
+ opt = "revision: \"#{requirement['revision']}\""
49
+ when "versionRange"
50
+ opt = "\"#{requirement['minimumVersion']}\"..<\"#{requirement['maximumVersion']}\""
51
+ end
52
+ ".package(url: \"#{hash['repositoryURL']}\", #{opt})"
53
+ end
54
+
55
+ config.lockfile.pkgs.map { |h| " #{decl.call(h)}," }.join("\n")
56
+ end
57
+
58
+ def manifest_platforms
59
+ @manifest_platforms ||= begin
60
+ to_spm_platform = {
61
+ :ios => "iOS",
62
+ :macos => "macOS",
63
+ :osx => "macOS",
64
+ :tvos => "tvOS",
65
+ :watchos => "watchOS",
66
+ :visionos => "visionOS",
67
+ }
68
+ hash = {}
69
+ config.project_targets.each do |t|
70
+ platform = to_spm_platform[t.platform_name]
71
+ hash[platform] ||= []
72
+ hash[platform] << t.deployment_target.split(".")[0]
73
+ end
74
+ hash
75
+ .transform_values(&:min)
76
+ .map { |platform, version| " .#{platform}(.v#{version})," }
77
+ .join("\n")
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,40 @@
1
+ module XCCache
2
+ module SPM
3
+ class Package
4
+ module UmbrellaVizMixin
5
+ def gen_cachemap_viz
6
+ stats = config.cachemap.stats
7
+ html_path = config.sandbox / "cachemap.html"
8
+ js_path = Dir.prepare(config.sandbox / "assets") / "cachemap.js"
9
+ css_path = config.sandbox / "assets" / "style.css"
10
+
11
+ root_dir = Pathname(".").expand_path
12
+ to_relative = proc do |p|
13
+ p.to_s.start_with?(root_dir.to_s) ? p.relative_path_from(root_dir).to_s : p.to_s
14
+ end
15
+
16
+ UI.message("Cachemap visualization: #{html_path}")
17
+ Template.new("cachemap.html").render(
18
+ {
19
+ :root_dir => root_dir.to_s,
20
+ :root_dir_short => root_dir.basename.to_s,
21
+ :lockfile_path => config.lockfile.path.to_s,
22
+ :lockfile_path_short => to_relative.call(config.lockfile.path),
23
+ :binaries_dir => config.spm_binaries_frameworks_dir.to_s,
24
+ :binaries_dir_short => to_relative.call(config.spm_binaries_frameworks_dir),
25
+ :desc_hit => stats[:hit],
26
+ :desc_missed => stats[:missed],
27
+ :desc_ignored => stats[:ignored],
28
+ },
29
+ save_to: html_path
30
+ )
31
+ Template.new("cachemap.js").render(
32
+ { :json => JSON.pretty_generate(config.cachemap.depgraph_data) },
33
+ save_to: js_path
34
+ )
35
+ Template.new("cachemap.style.css").render(save_to: css_path)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,81 @@
1
+ require "xccache/swift/swiftc"
2
+ require "xccache/utils/template"
3
+ require "xccache/cache/cachemap"
4
+ require "xccache/spm/pkg/base"
5
+
6
+ Dir["#{__dir__}/#{File.basename(__FILE__, '.rb')}/*.rb"].sort.each { |f| require f }
7
+
8
+ module XCCache
9
+ module SPM
10
+ class Package
11
+ class Umbrella < Package
12
+ include Config::Mixin
13
+ include UmbrellaCachemapMixin
14
+ include UmbrellaDescsMixin
15
+ include UmbrellaBuilldMixin
16
+ include UmbrellaManifestMixin
17
+ include UmbrellaVizMixin
18
+
19
+ def initialize(options = {})
20
+ super
21
+ @descs = []
22
+ @descs_by_name = {}
23
+ @dependency_targets_by_products = {}
24
+ end
25
+
26
+ def prepare(options = {})
27
+ create
28
+ resolve unless options[:skip_resolve]
29
+ create_symlinks
30
+ gen_metadata
31
+ resolve_recursive_dependencies
32
+ create_symlinks_to_artifacts
33
+ sync_cachemap
34
+ end
35
+
36
+ def resolve_recursive_dependencies
37
+ UI.section("Resolving recursive dependencies")
38
+ @descs.each do |desc|
39
+ @dependency_targets_by_products.merge!(desc.resolve_recursive_dependencies.transform_keys(&:full_name))
40
+ end
41
+ end
42
+
43
+ def create
44
+ UI.info("Creating umbrella package")
45
+ # Initially, write json with the original data in lockfile (without cache)
46
+ write_manifest(no_cache: true)
47
+ # Create dummy sources dirs prefixed with `.` so that they do not show up in Xcode
48
+ config.project_targets.each do |target|
49
+ dir = Dir.prepare(root_dir / ".Sources" / "#{target.name}.xccache")
50
+ (dir / "dummy.swift").write("")
51
+ end
52
+ end
53
+
54
+ def create_symlinks
55
+ # Symlinks for convenience
56
+ (root_dir / "binaries").symlink_to(root_dir.parent / "binaries")
57
+ (root_dir / ".build").symlink_to(root_dir.parent / ".build")
58
+ (root_dir / ".build/checkouts").symlink_to(root_dir.parent / "checkouts")
59
+ end
60
+
61
+ def create_symlinks_to_artifacts
62
+ # Clean up broken symlinks
63
+ config.spm_binaries_frameworks_dir.glob("*/*.xcframework").each do |p|
64
+ p.rmtree if p.symlink? && !p.readlink.exist?
65
+ end
66
+
67
+ UI.message("Creating symlinks to binary artifacts of targets: #{binary_targets.map(&:full_name).to_s.dark}")
68
+ binary_targets.each do |target|
69
+ dst_path = config.spm_binaries_frameworks_dir / target.name / "#{target.name}.xcframework"
70
+ # For local xcframework, just symlink to the path
71
+ # Zip frameworks (either of local or remote pkgs) are unzipped in the build artifacts
72
+ target.local_binary_path.symlink_to(dst_path) if target.local_binary_path&.extname == ".xcframework"
73
+ config.spm_artifacts_dir.glob("#{target.full_name}/*.xcframework").each do |p|
74
+ p.symlink_to(dst_path)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1 @@
1
+ Dir["#{__dir__}/#{File.basename(__FILE__, '.rb')}/*.rb"].sort.each { |f| require f }
@@ -0,0 +1 @@
1
+ Dir["#{__dir__}/#{File.basename(__FILE__, '.rb')}/*.rb"].sort.each { |f| require f }
@@ -0,0 +1,30 @@
1
+ require "xccache/core/sh"
2
+
3
+ module XCCache
4
+ module Swift
5
+ class Sdk
6
+ attr_reader :name
7
+
8
+ NAME_TO_TRIPLE = {
9
+ :iphonesimulator => "arm64-apple-ios-simulator",
10
+ :iphoneos => "arm64-apple-ios",
11
+ }.freeze
12
+
13
+ def initialize(name)
14
+ @name = name
15
+ end
16
+
17
+ def to_s
18
+ name
19
+ end
20
+
21
+ def triple
22
+ NAME_TO_TRIPLE[name.to_sym]
23
+ end
24
+
25
+ def sdk_path
26
+ @sdk_path ||= Pathname(Sh.capture_output("xcrun --sdk #{name} --show-sdk-path")).realpath
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,16 @@
1
+ module XCCache
2
+ module Swift
3
+ module Swiftc
4
+ def self.version
5
+ @version ||= begin
6
+ m = /Apple Swift version ([\d\.]+)/.match(Sh.capture_output("xcrun swift -version"))
7
+ m.nil? ? "6.0" : m[1]
8
+ end
9
+ end
10
+
11
+ def self.version_without_patch
12
+ version.split(".")[...2].join(".")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ require "erb"
2
+ require "xccache/core/error"
3
+
4
+ module XCCache
5
+ class Template
6
+ attr_reader :name, :path
7
+
8
+ def initialize(name)
9
+ @name = name
10
+ @path = Gem.find_files("xccache/assets/templates/#{name}.template").first
11
+ end
12
+
13
+ def render(hash = {}, save_to: nil)
14
+ raise GeneralError, "Template not found: #{name}" if path.nil?
15
+
16
+ rendered = ERB.new(File.read(@path)).result_with_hash(hash)
17
+ Pathname(save_to).write(rendered) unless save_to.nil?
18
+ rendered
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,69 @@
1
+ require "xcodeproj"
2
+
3
+ module Xcodeproj
4
+ class Project
5
+ module Object
6
+ module PkgRefMixin
7
+ def id
8
+ local? ? (relative_path || path) : repositoryURL
9
+ end
10
+
11
+ def slug
12
+ File.basename(id, File.extname(id))
13
+ end
14
+
15
+ def local?
16
+ is_a?(XCLocalSwiftPackageReference)
17
+ end
18
+
19
+ def xccache_pkg?
20
+ local? && id == "xccache/packages/umbrella"
21
+ end
22
+
23
+ def create_pkg_product_dependency_ref(product)
24
+ ref = project.new(XCSwiftPackageProductDependency)
25
+ ref.package = self
26
+ ref.product_name = product
27
+ ref
28
+ end
29
+
30
+ def create_target_dependency_ref(product)
31
+ ref = project.new(PBXTargetDependency)
32
+ ref.name = product
33
+ ref.product_ref = create_pkg_product_dependency_ref(product)
34
+ ref
35
+ end
36
+ end
37
+
38
+ class XCLocalSwiftPackageReference
39
+ include PkgRefMixin
40
+
41
+ def ascii_plist_annotation
42
+ # Workaround: Xcode is using display_name while Xcodeproj is using File.basename(display_name)
43
+ # Here, the plugin forces to use display_name so that updates either by Xcode or Xcodeproj are consistent
44
+ " #{isa} \"#{display_name}\" "
45
+ end
46
+
47
+ def to_h
48
+ {
49
+ "relative_path" => relative_path,
50
+ "path" => path,
51
+ "path_from_root" => absolute_path.relative_path_from(Pathname(".").expand_path).to_s,
52
+ }
53
+ end
54
+
55
+ def absolute_path
56
+ path.nil? ? project.path.parent / relative_path : path
57
+ end
58
+ end
59
+
60
+ class XCRemoteSwiftPackageReference
61
+ include PkgRefMixin
62
+
63
+ def to_h
64
+ { "repositoryURL" => repositoryURL, "requirement" => requirement }
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,19 @@
1
+ require "xcodeproj"
2
+
3
+ module Xcodeproj
4
+ class Project
5
+ module Object
6
+ class XCSwiftPackageProductDependency
7
+ def full_name
8
+ "#{pkg.slug}/#{product_name}"
9
+ end
10
+
11
+ def pkg
12
+ return package unless package.nil?
13
+
14
+ Log.warn("Missing pkg for #{inspect}")
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,63 @@
1
+ require "xcodeproj"
2
+
3
+ module Xcodeproj
4
+ class Project
5
+ Log = XCCache::UI
6
+
7
+ def display_name
8
+ relative_path.to_s
9
+ end
10
+
11
+ def relative_path
12
+ @relative_path ||= path.relative_path_from(Pathname(".").expand_path)
13
+ end
14
+
15
+ def pkgs
16
+ root_object.package_references
17
+ end
18
+
19
+ def non_xccache_pkgs
20
+ pkgs.reject(&:xccache_pkg?)
21
+ end
22
+
23
+ def has_pkg?(hash)
24
+ id = hash[pkg_key_in_hash(hash)]
25
+ pkgs.any? { |p| p.id == id }
26
+ end
27
+
28
+ def has_xccache_pkg?
29
+ pkgs.any?(&:xccache_pkg?)
30
+ end
31
+
32
+ def add_pkg(hash)
33
+ key = pkg_key_in_hash(hash)
34
+ is_local = ["relative_path", "path"].include?(key)
35
+
36
+ Log.message("Add package #{hash[key].bold} to project #{display_name.bold}")
37
+ cls = is_local ? XCLocalSwiftPackageReference : XCRemoteSwiftPackageReference
38
+ ref = new(cls)
39
+ hash.each { |k, v| ref.send("#{k}=", v) }
40
+ root_object.package_references << ref
41
+ ref
42
+ end
43
+
44
+ def add_xccache_pkg
45
+ sandbox_path = XCCache::Config.instance.spm_umbrella_sandbox
46
+ add_pkg("relative_path" => sandbox_path.relative_path_from(path.parent).to_s)
47
+ end
48
+
49
+ def get_target(name)
50
+ targets.find { |t| t.name == name }
51
+ end
52
+
53
+ def get_pkg(name)
54
+ pkgs.find { |p| p.slug == name }
55
+ end
56
+
57
+ private
58
+
59
+ def pkg_key_in_hash(hash)
60
+ ["repositoryURL", "relative_path", "path"].find { |k| hash.key?(k) }
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,50 @@
1
+ require "xcodeproj"
2
+
3
+ module Xcodeproj
4
+ class Project
5
+ module Object
6
+ class PBXNativeTarget
7
+ alias pkg_product_dependencies package_product_dependencies
8
+
9
+ def non_xccache_pkg_product_dependencies
10
+ pkg_product_dependencies.reject { |d| d.pkg.xccache_pkg? }
11
+ end
12
+
13
+ def has_xccache_product_dependency?
14
+ pkg_product_dependencies.any? { |d| d.pkg.xccache_pkg? }
15
+ end
16
+
17
+ def has_pkg_product_dependency?(name)
18
+ pkg_product_dependencies.any? { |d| "#{d.pkg.slug}/#{d.name}" == name }
19
+ end
20
+
21
+ def add_pkg_product_dependency(name)
22
+ Log.message("(+) Add dependency #{name.blue} to target #{display_name.bold}")
23
+ pkg_name, product_name = name.split("/")
24
+ pkg = project.get_pkg(pkg_name)
25
+ pkg_product_dependencies << pkg.create_target_dependency_ref(product_name).product_ref
26
+ end
27
+
28
+ def add_xccache_product_dependency
29
+ add_pkg_product_dependency("umbrella/#{name}.xccache")
30
+ end
31
+
32
+ def remove_xccache_product_dependencies
33
+ remove_pkg_product_dependencies { |d| d.pkg.xccache_pkg? }
34
+ end
35
+
36
+ def remove_pkg_product_dependencies(&block)
37
+ package_product_dependencies.select(&block).each do |d|
38
+ XCCache::UI.info(
39
+ "(-) Remove #{d.product_name.red} from product dependencies of target #{display_name.bold}"
40
+ )
41
+ build_phases.each do |phase|
42
+ phase.files.select { |f| f.remove_from_project if f.product_ref == d }
43
+ end
44
+ d.remove_from_project
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,2 @@
1
+ require "xccache/core"
2
+ Dir["#{__dir__}/#{File.basename(__FILE__, '.rb')}/*.rb"].sort.each { |f| require f }