xccache 0.0.1a1 → 0.0.1a3

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/lib/xccache/assets/templates/cachemap.html.template +55 -0
  3. data/lib/xccache/assets/templates/cachemap.js.template +95 -0
  4. data/lib/xccache/assets/templates/cachemap.style.css.template +94 -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 +183 -0
  10. data/lib/xccache/cache/cachemap.rb +56 -0
  11. data/lib/xccache/command/base.rb +29 -0
  12. data/lib/xccache/command/build.rb +41 -0
  13. data/lib/xccache/command/pkg/build.rb +30 -0
  14. data/lib/xccache/command/pkg.rb +1 -0
  15. data/lib/xccache/command/remote/pull.rb +15 -0
  16. data/lib/xccache/command/remote/push.rb +15 -0
  17. data/lib/xccache/command/remote.rb +39 -0
  18. data/lib/xccache/command/rollback.rb +14 -0
  19. data/lib/xccache/command/use.rb +19 -0
  20. data/lib/xccache/command.rb +27 -1
  21. data/lib/xccache/core/cacheable.rb +28 -0
  22. data/lib/xccache/core/config.rb +110 -0
  23. data/lib/xccache/core/error.rb +7 -0
  24. data/lib/xccache/core/git.rb +27 -0
  25. data/lib/xccache/core/hash.rb +21 -0
  26. data/lib/xccache/core/lockfile.rb +40 -0
  27. data/lib/xccache/core/log.rb +51 -0
  28. data/lib/xccache/core/parallel.rb +10 -0
  29. data/lib/xccache/core/sh.rb +51 -0
  30. data/lib/xccache/core/syntax/hash.rb +31 -0
  31. data/lib/xccache/core/syntax/json.rb +16 -0
  32. data/lib/xccache/core/syntax/plist.rb +17 -0
  33. data/lib/xccache/core/syntax/yml.rb +16 -0
  34. data/lib/xccache/core/syntax.rb +1 -0
  35. data/lib/xccache/core/system.rb +62 -0
  36. data/lib/xccache/core.rb +1 -0
  37. data/lib/xccache/installer/build.rb +23 -0
  38. data/lib/xccache/installer/rollback.rb +37 -0
  39. data/lib/xccache/installer/use.rb +28 -0
  40. data/lib/xccache/installer.rb +113 -0
  41. data/lib/xccache/main.rb +3 -1
  42. data/lib/xccache/spm/build.rb +53 -0
  43. data/lib/xccache/spm/desc/base.rb +68 -0
  44. data/lib/xccache/spm/desc/dep.rb +40 -0
  45. data/lib/xccache/spm/desc/desc.rb +126 -0
  46. data/lib/xccache/spm/desc/product.rb +36 -0
  47. data/lib/xccache/spm/desc/target/binary.rb +8 -0
  48. data/lib/xccache/spm/desc/target/macro.rb +8 -0
  49. data/lib/xccache/spm/desc/target.rb +164 -0
  50. data/lib/xccache/spm/desc.rb +1 -0
  51. data/lib/xccache/spm/macro.rb +44 -0
  52. data/lib/xccache/spm/mixin.rb +12 -0
  53. data/lib/xccache/spm/pkg/base.rb +107 -0
  54. data/lib/xccache/spm/pkg/umbrella/build.rb +30 -0
  55. data/lib/xccache/spm/pkg/umbrella/cachemap.rb +110 -0
  56. data/lib/xccache/spm/pkg/umbrella/descs.rb +35 -0
  57. data/lib/xccache/spm/pkg/umbrella/manifest.rb +83 -0
  58. data/lib/xccache/spm/pkg/umbrella/viz.rb +40 -0
  59. data/lib/xccache/spm/pkg/umbrella/xcconfigs.rb +31 -0
  60. data/lib/xccache/spm/pkg/umbrella.rb +91 -0
  61. data/lib/xccache/spm/pkg.rb +1 -0
  62. data/lib/xccache/spm/xcframework/metadata.rb +41 -0
  63. data/lib/xccache/spm/xcframework/slice.rb +180 -0
  64. data/lib/xccache/spm/xcframework/xcframework.rb +56 -0
  65. data/lib/xccache/spm/xcframework.rb +2 -0
  66. data/lib/xccache/spm.rb +1 -0
  67. data/lib/xccache/storage/base.rb +26 -0
  68. data/lib/xccache/storage/git.rb +46 -0
  69. data/lib/xccache/storage/s3.rb +53 -0
  70. data/lib/xccache/storage.rb +1 -0
  71. data/lib/xccache/swift/sdk.rb +49 -0
  72. data/lib/xccache/swift/swiftc.rb +16 -0
  73. data/lib/xccache/utils/template.rb +21 -0
  74. data/lib/xccache/xcodeproj/build_configuration.rb +20 -0
  75. data/lib/xccache/xcodeproj/config.rb +9 -0
  76. data/lib/xccache/xcodeproj/file_system_synchronized_root_group.rb +17 -0
  77. data/lib/xccache/xcodeproj/group.rb +26 -0
  78. data/lib/xccache/xcodeproj/pkg.rb +73 -0
  79. data/lib/xccache/xcodeproj/pkg_product_dependency.rb +19 -0
  80. data/lib/xccache/xcodeproj/project.rb +83 -0
  81. data/lib/xccache/xcodeproj/target.rb +52 -0
  82. data/lib/xccache/xcodeproj.rb +2 -0
  83. metadata +107 -2
@@ -0,0 +1,62 @@
1
+ require "digest"
2
+ require "mkmf"
3
+
4
+ class String
5
+ def c99extidentifier
6
+ gsub(/[^a-zA-Z0-9.]/, "_")
7
+ end
8
+ end
9
+
10
+ class File
11
+ def self.which(bin)
12
+ find_executable0(bin)
13
+ end
14
+ end
15
+
16
+ class Dir
17
+ def self.prepare(dir)
18
+ dir = Pathname(dir)
19
+ dir.mkpath
20
+ dir
21
+ end
22
+
23
+ def self.create_tmpdir
24
+ dir = Pathname(Dir.mktmpdir("xccache"))
25
+ res = block_given? ? (yield dir) : dir
26
+ dir.rmtree if block_given?
27
+ res
28
+ end
29
+
30
+ def self.git?(dir)
31
+ XCCache::Sh.capture_output("git -C #{dir} rev-parse --git-dir") == ".git"
32
+ end
33
+ end
34
+
35
+ class Pathname
36
+ def symlink_to(dst)
37
+ dst = Pathname(dst)
38
+ dst.rmtree if dst.symlink?
39
+ dst.parent.mkpath
40
+ File.symlink(expand_path, dst)
41
+ end
42
+
43
+ def copy(to: nil, to_dir: nil)
44
+ dst = to || (Pathname(to_dir) / basename)
45
+ dst.rmtree if dst.exist? || dst.symlink?
46
+ dst.parent.mkpath
47
+ FileUtils.copy_entry(self, dst)
48
+ dst
49
+ end
50
+
51
+ def checksum
52
+ hasher = Digest::SHA256.new
53
+ glob("**/*").reject { |p| p.directory? || p.symlink? }.sort.each do |p|
54
+ p.open("rb") do |f|
55
+ while (chunk = f.read(65_536)) # Read 64KB chunks
56
+ hasher.update(chunk)
57
+ end
58
+ end
59
+ end
60
+ hasher.hexdigest[...8]
61
+ end
62
+ end
@@ -0,0 +1 @@
1
+ Dir["#{__dir__}/#{File.basename(__FILE__, '.rb')}/*.rb"].sort.each { |f| require f }
@@ -0,0 +1,23 @@
1
+ require "xccache/spm"
2
+
3
+ module XCCache
4
+ class Installer
5
+ class Build < Installer
6
+ def initialize(options = {})
7
+ super
8
+ @targets = options[:targets]
9
+ end
10
+
11
+ def install!
12
+ perform_install do
13
+ umbrella_pkg.build(
14
+ targets: @targets,
15
+ out_dir: config.spm_repo_dir,
16
+ checksum: true,
17
+ **@build_options,
18
+ )
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ module XCCache
2
+ class Installer
3
+ class Rollback < Installer
4
+ def install!
5
+ update_projects do |project|
6
+ UI.section("Rolling back cache for project #{project.display_name}".bold.green) do
7
+ rollback_for_project(project)
8
+ end
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def rollback_for_project(project)
15
+ hash = lockfile.hash_for_project(project)
16
+ pkgs, deps_by_targets = hash["packages"], hash["dependencies"]
17
+
18
+ # Add packages back to the project
19
+ pkgs.reject { |h| project.has_pkg?(h) }.each do |h|
20
+ project.add_pkg(h)
21
+ end
22
+
23
+ # Add products back to `Link Binary with Libraries` of targets
24
+ deps_by_targets.each do |name, deps|
25
+ target = project.get_target(name)
26
+ deps.reject { |d| target.has_pkg_product_dependency?(d) }.each do |d|
27
+ target.add_pkg_product_dependency(d)
28
+ end
29
+ end
30
+
31
+ # Remove .binary product from the project
32
+ project.targets.each(&:remove_xccache_product_dependencies)
33
+ project.xccache_pkg&.remove_from_project
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,28 @@
1
+ require "xccache/spm"
2
+
3
+ module XCCache
4
+ class Installer
5
+ class Use < Installer
6
+ def install!
7
+ update_projects do |project|
8
+ perform_install do
9
+ UI.section("Using cache for project #{project.display_name}".bold.green) do
10
+ replace_binaries_for_project(project)
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def replace_binaries_for_project(project)
19
+ project.add_xccache_pkg unless project.has_xccache_pkg?
20
+ project.targets.each do |target|
21
+ target.add_xccache_product_dependency unless target.has_xccache_product_dependency?
22
+ target.remove_pkg_product_dependencies { |d| !d.pkg.xccache_pkg? }
23
+ end
24
+ project.remove_pkgs(&:non_xccache_pkg?) unless config.keep_pkgs_in_project?
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,113 @@
1
+ require "xccache/spm"
2
+ Dir["#{__dir__}/#{File.basename(__FILE__, '.rb')}/*.rb"].sort.each { |f| require f }
3
+
4
+ module XCCache
5
+ class Installer
6
+ include PkgMixin
7
+
8
+ def initialize(options = {})
9
+ ctx = options[:ctx]
10
+ raise GeneralError, "Missing context (Command) for #{self.class}" if ctx.nil?
11
+ @umbrella_pkg = options[:umbrella_pkg]
12
+ @install_options = ctx.install_options
13
+ @build_options = ctx.build_options
14
+ end
15
+
16
+ def perform_install
17
+ config.in_installation = true
18
+ verify_projects!
19
+ if @umbrella_pkg.nil?
20
+ sync_lockfile
21
+ umbrella_pkg.prepare(**@install_options)
22
+ end
23
+
24
+ yield
25
+ umbrella_pkg.write_manifest
26
+ umbrella_pkg.gen_xcconfigs
27
+ projects.each do |project|
28
+ add_xccache_refs_to_project(project)
29
+ inject_xcconfig_to_project(project)
30
+ end
31
+ umbrella_pkg.gen_cachemap_viz
32
+ end
33
+
34
+ def sync_lockfile
35
+ UI.info("Syncing lockfile")
36
+ update_projects do |project|
37
+ lockfile.deep_merge!(project.display_name => lockfile_hash_for_project(project))
38
+ end
39
+ lockfile.save
40
+ end
41
+
42
+ def lockfile
43
+ config.lockfile
44
+ end
45
+
46
+ def projects
47
+ config.projects
48
+ end
49
+
50
+ def save_projects
51
+ yield if block_given?
52
+ projects.each(&:save)
53
+ end
54
+
55
+ def update_projects
56
+ projects.each do |project|
57
+ yield project if block_given?
58
+ project.save
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def lockfile_hash_for_project(project)
65
+ deps_by_targets = project.targets.to_h do |target|
66
+ deps = target.non_xccache_pkg_product_dependencies.select(&:pkg).map { |d| "#{d.pkg.slug}/#{d.product_name}" }
67
+ [target.name, deps]
68
+ end
69
+ {
70
+ "packages" => project.non_xccache_pkgs.map(&:to_h),
71
+ "dependencies" => deps_by_targets,
72
+ }
73
+ end
74
+
75
+ def verify_projects!
76
+ raise "No projects detected. Are you running on the correct project directory?" if projects.empty?
77
+ end
78
+
79
+ def add_xccache_refs_to_project(project)
80
+ group = project.xccache_config_group
81
+ add_file = proc { |p| group[p.basename.to_s] || group.new_file(p) }
82
+ add_file.call(config.spm_umbrella_sandbox / "Package.swift")
83
+ add_file.call(config.lockfile.path)
84
+ add_file.call(config.path) if config.path.exist?
85
+ group.ensure_synced_group(name: "local-packages", path: config.spm_local_pkgs_dir)
86
+ end
87
+
88
+ def inject_xcconfig_to_project(project)
89
+ group = project.xccache_config_group.ensure_synced_group(name: "xcconfigs", path: config.spm_xcconfig_dir)
90
+ project.targets.each do |target|
91
+ xcconfig_path = config.spm_xcconfig_dir / "#{target.name}.xcconfig"
92
+ target.build_configurations.each do |build_config|
93
+ if (existing = build_config.base_configuration_xcconfig)
94
+ next if existing.path == xcconfig_path
95
+
96
+ relative_path = xcconfig_path.relative_path_from(existing.path.parent)
97
+ next if existing.includes.include?(relative_path.to_s)
98
+
99
+ UI.info("Injecting base configuration for #{target} (#{build_config}) (at: #{existing.path})")
100
+ existing.path.write <<~DESC
101
+ #include "#{relative_path}" // Injected by xccache, for prebuilt macros support
102
+ #{existing.path.read.strip}
103
+ DESC
104
+ else
105
+ UI.info("Setting base configuration #{target} (#{build_config}) as #{xcconfig_path}")
106
+ build_config.base_configuration_reference_anchor = group
107
+ build_config.base_configuration_reference_relative_path = xcconfig_path.basename.to_s
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
data/lib/xccache/main.rb CHANGED
@@ -1 +1,3 @@
1
- require "xccache/command"
1
+ require "pry" if ENV["XCCACHE_IMPORT_PRY"] == "true"
2
+ require "pathname"
3
+ Dir["#{__dir__}/*.rb"].sort.each { |f| require f unless f == __FILE__ }
@@ -0,0 +1,53 @@
1
+ module XCCache
2
+ module SPM
3
+ class Buildable
4
+ attr_reader :name, :module_name, :pkg_dir, :pkg_desc, :sdk, :sdks, :config, :path, :tmpdir, :library_evolution
5
+ alias library_evolution? library_evolution
6
+
7
+ def initialize(options = {})
8
+ @name = options[:name]
9
+ @module_name = @name.c99extidentifier
10
+ @pkg_dir = Pathname(options[:pkg_dir] || ".").expand_path
11
+ @pkg_desc = options[:pkg_desc]
12
+ @sdks = options[:sdks] || []
13
+ @sdk = options[:sdk] || @sdks&.first
14
+ @config = options[:config] || "debug"
15
+ @path = options[:path]
16
+ @tmpdir = options[:tmpdir]
17
+ @library_evolution = options[:library_evolution]
18
+ end
19
+
20
+ def build(options = {})
21
+ raise NotImplementedError
22
+ end
23
+
24
+ def swift_build(target: nil)
25
+ cmd = ["swift", "build"] + swift_build_args
26
+ cmd << "--package-path" << pkg_dir
27
+ cmd << "--target" << (target || name)
28
+ cmd << "--sdk" << sdk.sdk_path
29
+ sdk.swiftc_args.each { |arg| cmd << "-Xswiftc" << arg }
30
+ if library_evolution?
31
+ # Workaround for swiftinterface emission
32
+ # https://github.com/swiftlang/swift/issues/64669#issuecomment-1535335601
33
+ cmd << "-Xswiftc" << "-enable-library-evolution"
34
+ cmd << "-Xswiftc" << "-alias-module-names-in-module-interface"
35
+ cmd << "-Xswiftc" << "-emit-module-interface"
36
+ cmd << "-Xswiftc" << "-no-verify-emitted-module-interface"
37
+ end
38
+ Sh.run(cmd, suppress_err: /(dependency '.*' is not used by any target|unable to create symbolic link)/)
39
+ end
40
+
41
+ def swift_build_args
42
+ [
43
+ "--configuration", config,
44
+ "--triple", sdk.triple,
45
+ ]
46
+ end
47
+
48
+ def pkg_target
49
+ @pkg_target ||= pkg_desc.get_target(name)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,68 @@
1
+ require "xccache/core"
2
+
3
+ module XCCache
4
+ module SPM
5
+ class Package
6
+ class BaseObject < JSONRepresentable
7
+ include Config::Mixin
8
+
9
+ ATTRS = %i[root retrieve_pkg_desc].freeze
10
+ attr_accessor(*ATTRS)
11
+
12
+ def name
13
+ raw["name"]
14
+ end
15
+
16
+ def full_name
17
+ is_a?(Description) ? name : "#{pkg_slug}/#{name}"
18
+ end
19
+
20
+ def inspect
21
+ to_s
22
+ end
23
+
24
+ def display_name
25
+ name
26
+ end
27
+
28
+ def to_s
29
+ "<#{self.class} name=#{display_name}>"
30
+ end
31
+
32
+ def cast_to(cls)
33
+ o = cls.new(path, raw: raw)
34
+ ATTRS.each { |sym| o.send("#{sym}=", send(sym.to_s)) }
35
+ o
36
+ end
37
+
38
+ def pkg_name
39
+ @pkg_name ||= root.name
40
+ end
41
+
42
+ def pkg_slug
43
+ @pkg_slug ||= src_dir.basename.to_s
44
+ end
45
+
46
+ def fetch(key, dtype)
47
+ raw[key].map do |h|
48
+ o = dtype.new(nil, raw: h)
49
+ o.root = root
50
+ o.retrieve_pkg_desc = retrieve_pkg_desc
51
+ o
52
+ end
53
+ end
54
+
55
+ def pkg_desc_of(name)
56
+ retrieve_pkg_desc.call(name)
57
+ end
58
+
59
+ def src_dir
60
+ @src_dir ||= begin
61
+ path = root.raw.fetch("packageKind", {}).fetch("root", [])[0]
62
+ Pathname.new(path) unless path.nil?
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,40 @@
1
+ require "xccache/spm/desc/base"
2
+
3
+ module XCCache
4
+ module SPM
5
+ class Package
6
+ class Dependency < BaseObject
7
+ def display_name
8
+ slug
9
+ end
10
+
11
+ def local?
12
+ raw.key?("fileSystem")
13
+ end
14
+
15
+ def hash
16
+ @hash ||= local? ? raw["fileSystem"].first : raw["sourceControl"].first
17
+ end
18
+
19
+ def slug
20
+ @slug ||=
21
+ if hash.key?("path")
22
+ File.basename(hash["path"])
23
+ elsif (location = hash["location"]) && location.key?("remote")
24
+ File.basename(location["remote"].flat_map(&:values)[0], ".*")
25
+ else
26
+ hash["identity"]
27
+ end
28
+ end
29
+
30
+ def path
31
+ @path ||= Pathname(hash["path"]).expand_path if local?
32
+ end
33
+
34
+ def pkg_desc
35
+ @pkg_desc ||= pkg_desc_of(slug)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,126 @@
1
+ require "xccache/spm/desc/base"
2
+
3
+ module XCCache
4
+ module SPM
5
+ class Package
6
+ class Description < BaseObject
7
+ include Cacheable
8
+ cacheable :resolve_recursive_dependencies
9
+
10
+ def self.descs_in_metadata_dir
11
+ descs = Config.instance.spm_metadata_dir.glob("*.json").map { |p| Description.new(p) }
12
+ [descs, combine_descs(descs)]
13
+ end
14
+
15
+ def self.in_dir(dir, save_to_dir: nil)
16
+ path = save_to_dir / "#{dir.basename}.json" unless save_to_dir.nil?
17
+ begin
18
+ raw = JSON.parse(Sh.capture_output("swift package dump-package --package-path #{dir}"))
19
+ Description.new(path, raw: raw)
20
+ rescue StandardError => e
21
+ UI.error("Failed to dump package in #{dir}. Error: #{e}")
22
+ end
23
+ end
24
+
25
+ def self.descs_in_dir(root_dir, save_to_dir: nil)
26
+ dirs = [root_dir] + root_dir.glob(".build/checkouts/*").reject { |p| p.glob("Package*.swift").empty? }
27
+ descs = dirs.parallel_map do |dir|
28
+ desc = Description.in_dir(dir, save_to_dir: save_to_dir)
29
+ unless save_to_dir.nil?
30
+ desc.save
31
+ desc.save(to: desc.path.parent / "#{desc.name}.json") if desc.name != dir.basename.to_s
32
+ end
33
+ desc
34
+ end
35
+ [descs, combine_descs(descs)]
36
+ end
37
+
38
+ def root
39
+ self
40
+ end
41
+
42
+ def metadata
43
+ raw["_metadata"] ||= {}
44
+ end
45
+
46
+ def dependencies
47
+ @dependencies ||= fetch("dependencies", Dependency)
48
+ end
49
+
50
+ def uniform_dependencies
51
+ dependencies.filter_map(&:pkg_desc)
52
+ end
53
+
54
+ def products
55
+ @products ||= fetch("products", Product)
56
+ end
57
+
58
+ def targets
59
+ @targets ||= fetch("targets", Target).map(&:downcast)
60
+ end
61
+
62
+ def binary_targets
63
+ @binary_targets ||= targets.select(&:binary?)
64
+ end
65
+
66
+ def has_target?(name)
67
+ targets.any? { |t| t.name == name }
68
+ end
69
+
70
+ def get_target(name)
71
+ targets.find { |t| t.name == name }
72
+ end
73
+
74
+ def targets_of_products(name)
75
+ matched_products = products.select { |p| p.name == name }
76
+ matched_products
77
+ .flat_map { |p| targets.select { |t| p.target_names.include?(t.name) } }
78
+ end
79
+
80
+ def resolve_recursive_dependencies(platform: nil)
81
+ products.to_h { |p| [p, p.recursive_targets(platform: platform)] }
82
+ end
83
+
84
+ def local?
85
+ # Workaround: If the pkg dir is under the build checkouts dir -> remote
86
+ !src_dir.to_s.start_with?((config.spm_build_dir / "checkouts").to_s)
87
+ end
88
+
89
+ def traverse
90
+ nodes, edges, parents = [], [], {}
91
+ to_visit = targets.dup
92
+ visited = Set.new
93
+ until to_visit.empty?
94
+ cur = to_visit.pop
95
+ next if visited.include?(cur)
96
+
97
+ visited << cur
98
+ nodes << cur
99
+ yield cur if block_given?
100
+
101
+ # For macro impl, we don't need their dependencies, just the tool binary
102
+ # So, no need to care about swift-syntax dependencies
103
+ next if cur.macro?
104
+ cur.direct_dependency_targets.each do |t|
105
+ to_visit << t
106
+ edges << [cur, t]
107
+ parents[t] ||= []
108
+ parents[t] << cur
109
+ end
110
+ end
111
+ [nodes, edges, parents]
112
+ end
113
+
114
+ def git
115
+ @git ||= Git.new(src_dir) if Dir.git?(src_dir)
116
+ end
117
+
118
+ def self.combine_descs(descs)
119
+ descs_by_name = descs.flat_map { |d| [[d.name, d], [d.pkg_slug, d]] }.to_h
120
+ descs.each { |d| d.retrieve_pkg_desc = proc { |name| descs_by_name[name] } }
121
+ descs_by_name
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,36 @@
1
+ require "xccache/spm/desc/base"
2
+
3
+ module XCCache
4
+ module SPM
5
+ class Package
6
+ class Product < BaseObject
7
+ include Cacheable
8
+ cacheable :recursive_targets
9
+
10
+ def target_names
11
+ raw["targets"]
12
+ end
13
+
14
+ def flatten_as_targets
15
+ targets
16
+ end
17
+
18
+ def targets
19
+ @targets ||= root.targets.select { |t| target_names.include?(t.name) }
20
+ end
21
+
22
+ def library?
23
+ type == :library
24
+ end
25
+
26
+ def type
27
+ @type ||= raw["type"].keys.first.to_sym
28
+ end
29
+
30
+ def recursive_targets(platform: nil)
31
+ targets + targets.flat_map { |t| t.recursive_targets(platform: platform) }.uniq
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,8 @@
1
+ module XCCache
2
+ module SPM
3
+ class Package
4
+ class BinaryTarget < Target
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module XCCache
2
+ module SPM
3
+ class Package
4
+ class MacroTarget < Target
5
+ end
6
+ end
7
+ end
8
+ end