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.
- checksums.yaml +4 -4
- data/lib/xccache/assets/templates/cachemap.html.template +54 -0
- data/lib/xccache/assets/templates/cachemap.js.template +94 -0
- data/lib/xccache/assets/templates/cachemap.style.css.template +92 -0
- data/lib/xccache/assets/templates/framework.info.plist.template +25 -0
- data/lib/xccache/assets/templates/framework.modulemap.template +6 -0
- data/lib/xccache/assets/templates/resource_bundle_accessor.m.template +27 -0
- data/lib/xccache/assets/templates/resource_bundle_accessor.swift.template +24 -0
- data/lib/xccache/assets/templates/umbrella.Package.swift.template +147 -0
- data/lib/xccache/cache/cachemap.rb +60 -0
- data/lib/xccache/command/build.rb +35 -0
- data/lib/xccache/command/pkg/build.rb +34 -0
- data/lib/xccache/command/rollback.rb +13 -0
- data/lib/xccache/command/use.rb +18 -0
- data/lib/xccache/command.rb +11 -1
- data/lib/xccache/core/cacheable.rb +28 -0
- data/lib/xccache/core/config.rb +82 -0
- data/lib/xccache/core/error.rb +7 -0
- data/lib/xccache/core/git.rb +19 -0
- data/lib/xccache/core/hash.rb +21 -0
- data/lib/xccache/core/lockfile.rb +40 -0
- data/lib/xccache/core/log.rb +41 -0
- data/lib/xccache/core/sh.rb +50 -0
- data/lib/xccache/core/syntax/hash.rb +31 -0
- data/lib/xccache/core/syntax/json.rb +16 -0
- data/lib/xccache/core/syntax/yml.rb +16 -0
- data/lib/xccache/core/syntax.rb +1 -0
- data/lib/xccache/core/system.rb +54 -0
- data/lib/xccache/core.rb +1 -0
- data/lib/xccache/framework/slice.rb +183 -0
- data/lib/xccache/framework/xcframework.rb +51 -0
- data/lib/xccache/installer/build.rb +27 -0
- data/lib/xccache/installer/rollback.rb +36 -0
- data/lib/xccache/installer/use.rb +27 -0
- data/lib/xccache/installer.rb +67 -0
- data/lib/xccache/main.rb +3 -1
- data/lib/xccache/spm/desc/base.rb +61 -0
- data/lib/xccache/spm/desc/dep.rb +40 -0
- data/lib/xccache/spm/desc/desc.rb +110 -0
- data/lib/xccache/spm/desc/product.rb +36 -0
- data/lib/xccache/spm/desc/target.rb +138 -0
- data/lib/xccache/spm/desc.rb +1 -0
- data/lib/xccache/spm/mixin.rb +9 -0
- data/lib/xccache/spm/pkg/base.rb +103 -0
- data/lib/xccache/spm/pkg/umbrella/build.rb +30 -0
- data/lib/xccache/spm/pkg/umbrella/cachemap.rb +93 -0
- data/lib/xccache/spm/pkg/umbrella/descs.rb +46 -0
- data/lib/xccache/spm/pkg/umbrella/manifest.rb +83 -0
- data/lib/xccache/spm/pkg/umbrella/viz.rb +40 -0
- data/lib/xccache/spm/pkg/umbrella.rb +81 -0
- data/lib/xccache/spm/pkg.rb +1 -0
- data/lib/xccache/spm.rb +1 -0
- data/lib/xccache/swift/sdk.rb +30 -0
- data/lib/xccache/swift/swiftc.rb +16 -0
- data/lib/xccache/utils/template.rb +21 -0
- data/lib/xccache/xcodeproj/pkg.rb +69 -0
- data/lib/xccache/xcodeproj/pkg_product_dependency.rb +19 -0
- data/lib/xccache/xcodeproj/project.rb +63 -0
- data/lib/xccache/xcodeproj/target.rb +50 -0
- data/lib/xccache/xcodeproj.rb +2 -0
- metadata +72 -2
@@ -0,0 +1,36 @@
|
|
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
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "xccache/spm"
|
2
|
+
|
3
|
+
module XCCache
|
4
|
+
class Installer
|
5
|
+
class Use < Installer
|
6
|
+
def install!
|
7
|
+
perform_install do
|
8
|
+
update_projects do |project|
|
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
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,67 @@
|
|
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
|
+
@umbrella_pkg = options[:umbrella_pkg]
|
10
|
+
@skip_resolving_dependencies = options[:skip_resolving_dependencies]
|
11
|
+
end
|
12
|
+
|
13
|
+
def perform_install
|
14
|
+
verify_projects!
|
15
|
+
sync_lockfile if @umbrella_pkg.nil?
|
16
|
+
umbrella_pkg.prepare(skip_resolve: @skip_resolving_dependencies) if @umbrella_pkg.nil?
|
17
|
+
yield
|
18
|
+
umbrella_pkg.write_manifest
|
19
|
+
umbrella_pkg.gen_cachemap_viz
|
20
|
+
end
|
21
|
+
|
22
|
+
def sync_lockfile
|
23
|
+
UI.info("Syncing lockfile")
|
24
|
+
update_projects do |project|
|
25
|
+
lockfile.deep_merge!(project.display_name => lockfile_hash_for_project(project))
|
26
|
+
end
|
27
|
+
lockfile.save
|
28
|
+
end
|
29
|
+
|
30
|
+
def lockfile
|
31
|
+
config.lockfile
|
32
|
+
end
|
33
|
+
|
34
|
+
def projects
|
35
|
+
config.projects
|
36
|
+
end
|
37
|
+
|
38
|
+
def save_projects
|
39
|
+
yield if block_given?
|
40
|
+
projects.each(&:save)
|
41
|
+
end
|
42
|
+
|
43
|
+
def update_projects
|
44
|
+
projects.each do |project|
|
45
|
+
yield project if block_given?
|
46
|
+
project.save
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def lockfile_hash_for_project(project)
|
53
|
+
deps_by_targets = project.targets.to_h do |target|
|
54
|
+
deps = target.non_xccache_pkg_product_dependencies.map { |d| "#{d.pkg.slug}/#{d.product_name}" }
|
55
|
+
[target.name, deps]
|
56
|
+
end
|
57
|
+
{
|
58
|
+
"packages" => project.non_xccache_pkgs.map(&:to_h),
|
59
|
+
"dependencies" => deps_by_targets,
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def verify_projects!
|
64
|
+
raise "No projects detected. Are you running on the correct project directory?" if projects.empty?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/xccache/main.rb
CHANGED
@@ -0,0 +1,61 @@
|
|
1
|
+
require "xccache/core"
|
2
|
+
|
3
|
+
module XCCache
|
4
|
+
module SPM
|
5
|
+
class Package
|
6
|
+
class BaseObject < JSONRepresentable
|
7
|
+
include Config::Mixin
|
8
|
+
|
9
|
+
attr_accessor :root, :retrieve_pkg_desc
|
10
|
+
|
11
|
+
def name
|
12
|
+
raw["name"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def full_name
|
16
|
+
is_a?(Description) ? name : "#{pkg_slug}/#{name}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def display_name
|
24
|
+
name
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"<#{self.class} name=#{display_name}>"
|
29
|
+
end
|
30
|
+
|
31
|
+
def pkg_name
|
32
|
+
@pkg_name ||= root.name
|
33
|
+
end
|
34
|
+
|
35
|
+
def pkg_slug
|
36
|
+
@pkg_slug ||= root.path.basename(".json").to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
def fetch(key, dtype)
|
40
|
+
raw[key].map do |h|
|
41
|
+
o = dtype.new(nil, raw: h)
|
42
|
+
o.root = root
|
43
|
+
o.retrieve_pkg_desc = retrieve_pkg_desc
|
44
|
+
o
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def pkg_desc_of(name)
|
49
|
+
retrieve_pkg_desc.call(name)
|
50
|
+
end
|
51
|
+
|
52
|
+
def src_dir
|
53
|
+
@src_dir ||= begin
|
54
|
+
path = raw.fetch("packageKind", {}).fetch("root", [])[0]
|
55
|
+
Pathname.new(path) unless path.nil?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
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,110 @@
|
|
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.in_dir(dir, save_to_dir: nil, checksum: true)
|
11
|
+
path = save_to_dir / "#{dir.basename}.json" unless save_to_dir.nil?
|
12
|
+
begin
|
13
|
+
raw = JSON.parse(Sh.capture_output("swift package dump-package --package-path #{dir}"))
|
14
|
+
this = Description.new(path, raw: raw)
|
15
|
+
this.calc_checksum if checksum
|
16
|
+
this
|
17
|
+
rescue StandardError => e
|
18
|
+
UI.error("Failed to dump package in #{dir}. Error: #{e}")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def root
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def metadata
|
27
|
+
raw["_metadata"] ||= {}
|
28
|
+
end
|
29
|
+
|
30
|
+
def checksum
|
31
|
+
metadata["checksum"]
|
32
|
+
end
|
33
|
+
|
34
|
+
def dependencies
|
35
|
+
@dependencies ||= fetch("dependencies", Dependency)
|
36
|
+
end
|
37
|
+
|
38
|
+
def uniform_dependencies
|
39
|
+
dependencies.filter_map(&:pkg_desc)
|
40
|
+
end
|
41
|
+
|
42
|
+
def products
|
43
|
+
@products ||= fetch("products", Product)
|
44
|
+
end
|
45
|
+
|
46
|
+
def targets
|
47
|
+
@targets ||= fetch("targets", Target)
|
48
|
+
end
|
49
|
+
|
50
|
+
def binary_targets
|
51
|
+
@binary_targets ||= targets.select(&:binary?)
|
52
|
+
end
|
53
|
+
|
54
|
+
def has_target?(name)
|
55
|
+
targets.any? { |t| t.name == name }
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_target(name)
|
59
|
+
targets.find { |t| t.name == name }
|
60
|
+
end
|
61
|
+
|
62
|
+
def targets_of_products(name)
|
63
|
+
matched_products = products.select { |p| p.name == name }
|
64
|
+
matched_products
|
65
|
+
.flat_map { |p| targets.select { |t| p.target_names.include?(t.name) } }
|
66
|
+
end
|
67
|
+
|
68
|
+
def resolve_recursive_dependencies(platform: nil)
|
69
|
+
products.to_h { |p| [p, p.recursive_targets(platform: platform)] }
|
70
|
+
end
|
71
|
+
|
72
|
+
def local?
|
73
|
+
# Workaround: If the pkg dir is under the build checkouts dir -> remote
|
74
|
+
!src_dir.to_s.start_with?((config.spm_build_dir / "checkouts").to_s)
|
75
|
+
end
|
76
|
+
|
77
|
+
def calc_checksum
|
78
|
+
metadata["checksum"] = git.nil? ? src_dir.checksum : git.sha
|
79
|
+
end
|
80
|
+
|
81
|
+
def traverse
|
82
|
+
nodes, edges, parents = [], [], {}
|
83
|
+
to_visit = targets.dup
|
84
|
+
visited = Set.new
|
85
|
+
until to_visit.empty?
|
86
|
+
cur = to_visit.pop
|
87
|
+
next if visited.include?(cur)
|
88
|
+
|
89
|
+
visited << cur
|
90
|
+
nodes << cur
|
91
|
+
yield cur if block_given?
|
92
|
+
cur.direct_dependency_targets.each do |t|
|
93
|
+
to_visit << t
|
94
|
+
edges << [cur, t]
|
95
|
+
parents[t] ||= []
|
96
|
+
parents[t] << cur
|
97
|
+
end
|
98
|
+
end
|
99
|
+
[nodes, edges, parents]
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def git
|
105
|
+
@git ||= Git.new(src_dir) if Dir.git?(src_dir)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
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,138 @@
|
|
1
|
+
require "xccache/spm/desc/base"
|
2
|
+
|
3
|
+
module XCCache
|
4
|
+
module SPM
|
5
|
+
class Package
|
6
|
+
class Target < BaseObject
|
7
|
+
include Cacheable
|
8
|
+
cacheable :recursive_targets, :direct_dependency_targets, :direct_dependencies
|
9
|
+
|
10
|
+
def xccache?
|
11
|
+
name.end_with?(".xccache")
|
12
|
+
end
|
13
|
+
|
14
|
+
def type
|
15
|
+
@type ||= raw["type"].to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
def bundle_name
|
19
|
+
"#{pkg_name}_#{name}.bundle"
|
20
|
+
end
|
21
|
+
|
22
|
+
def flatten_as_targets
|
23
|
+
[self]
|
24
|
+
end
|
25
|
+
|
26
|
+
def sources_path
|
27
|
+
@sources_path ||= begin
|
28
|
+
path = raw["path"] || "Sources/#{name}"
|
29
|
+
root.src_dir / path
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def use_clang?
|
34
|
+
!header_paths.empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
def header_paths(options = {})
|
38
|
+
paths = []
|
39
|
+
paths += public_header_paths if options.fetch(:public, true)
|
40
|
+
paths += header_search_paths if options.fetch(:search, false)
|
41
|
+
paths
|
42
|
+
.flat_map { |p| p.glob("**/*.h*") }
|
43
|
+
.map(&:realpath)
|
44
|
+
.uniq
|
45
|
+
end
|
46
|
+
|
47
|
+
def settings
|
48
|
+
raw["settings"]
|
49
|
+
end
|
50
|
+
|
51
|
+
def header_search_paths
|
52
|
+
@header_search_paths ||=
|
53
|
+
settings
|
54
|
+
.filter_map { |h| h.fetch("kind", {})["headerSearchPath"] }
|
55
|
+
.flat_map(&:values)
|
56
|
+
.map { |p| sources_path / p }
|
57
|
+
end
|
58
|
+
|
59
|
+
def public_header_paths
|
60
|
+
@public_header_paths ||= begin
|
61
|
+
res = []
|
62
|
+
implicit_path = sources_path / "include"
|
63
|
+
res << implicit_path unless implicit_path.glob("**/*.h*").empty?
|
64
|
+
res << (sources_path / raw["publicHeadersPath"]) if raw.key?("publicHeadersPath")
|
65
|
+
res
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def resource_paths
|
70
|
+
@resource_paths ||= begin
|
71
|
+
res = raw.fetch("resources", []).map { |h| sources_path / h["path"] }
|
72
|
+
# Refer to the following link for the implicit resources
|
73
|
+
# https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package#Add-resource-files
|
74
|
+
implicit = sources_path.glob("*.{xcassets,xib,storyboard,xcdatamodeld,lproj}")
|
75
|
+
res + implicit
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def recursive_targets(platform: nil)
|
80
|
+
children = direct_dependency_targets(platform: platform)
|
81
|
+
children += children.flat_map { |t| t.recursive_targets(platform: platform) }
|
82
|
+
children.uniq
|
83
|
+
end
|
84
|
+
|
85
|
+
def direct_dependencies(platform: nil)
|
86
|
+
raw["dependencies"].flat_map do |hash|
|
87
|
+
dep_types = ["byName", "target", "product"]
|
88
|
+
if (dep_type = dep_types.intersection(hash.keys).first).nil?
|
89
|
+
raise GeneralError, "Unexpected dependency type. Must be one of #{dep_types}. Hash: #{hash}"
|
90
|
+
end
|
91
|
+
next [] unless match_platform?(hash[dep_type][-1], platform)
|
92
|
+
pkg_name = hash[dep_type][1] if dep_type == "product"
|
93
|
+
find_deps(hash[dep_type][0], pkg_name, dep_type)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def direct_dependency_targets(platform: nil)
|
98
|
+
direct_dependencies(platform: platform).flat_map(&:flatten_as_targets).uniq
|
99
|
+
end
|
100
|
+
|
101
|
+
def match_platform?(_condition, _platform)
|
102
|
+
true # FIXME: Handle this
|
103
|
+
end
|
104
|
+
|
105
|
+
def binary?
|
106
|
+
type == :binary
|
107
|
+
end
|
108
|
+
|
109
|
+
def binary_path
|
110
|
+
sources_path if binary?
|
111
|
+
end
|
112
|
+
|
113
|
+
def local_binary_path
|
114
|
+
binary_path if binary? && root.local?
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def find_deps(name, pkg_name, dep_type)
|
120
|
+
# If `dep_type` is `target` -> constrained within current pkg only
|
121
|
+
# If `dep_type` is `product` -> `pkg_name` must be present
|
122
|
+
# If `dep_type` is `byName` -> it's either from this pkg, or its children/dependencies
|
123
|
+
res = []
|
124
|
+
descs = pkg_name.nil? ? [root] + root.uniform_dependencies : [pkg_desc_of(pkg_name)]
|
125
|
+
descs.each do |desc|
|
126
|
+
by_target = -> { desc.targets.select { |t| t.name == name } }
|
127
|
+
by_product = -> { desc.products.select { |t| t.name == name } }
|
128
|
+
return by_target.call if dep_type == "target"
|
129
|
+
return by_product.call if dep_type == "product"
|
130
|
+
return res unless (res = by_target.call).empty?
|
131
|
+
return res unless (res = by_product.call).empty?
|
132
|
+
end
|
133
|
+
[]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dir["#{__dir__}/#{File.basename(__FILE__, '.rb')}/*.rb"].sort.each { |f| require f }
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require "json"
|
2
|
+
require "xccache/framework/slice"
|
3
|
+
require "xccache/framework/xcframework"
|
4
|
+
require "xccache/swift/sdk"
|
5
|
+
|
6
|
+
module XCCache
|
7
|
+
module SPM
|
8
|
+
class Package
|
9
|
+
attr_reader :root_dir
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@root_dir = Pathname(options[:root_dir] || ".").expand_path
|
13
|
+
end
|
14
|
+
|
15
|
+
def build(options = {})
|
16
|
+
validate!
|
17
|
+
targets = options.delete(:targets) || []
|
18
|
+
raise GeneralError, "No targets were specified" if targets.empty?
|
19
|
+
|
20
|
+
targets.map { |t| t.split("/")[-1] }.each do |t|
|
21
|
+
UI.section("\n▶ Building target: #{t}".bold.magenta) do
|
22
|
+
build_target(**options, target: t)
|
23
|
+
rescue StandardError => e
|
24
|
+
UI.error("Failed to build target: #{t}. Error: #{e}")
|
25
|
+
raise e unless config.ignore_build_errors?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def build_target(target: nil, sdk: nil, config: nil, out_dir: nil, **options)
|
31
|
+
target_pkg_desc = pkg_desc_of_target(target, skip_resolve: options[:skip_resolve])
|
32
|
+
if target_pkg_desc.binary_targets.any? { |t| t.name == target }
|
33
|
+
return UI.warn("Target #{target} is a binary target -> no need to build")
|
34
|
+
end
|
35
|
+
|
36
|
+
sdks = (sdk || "iphonesimulator").split(",")
|
37
|
+
|
38
|
+
out_dir = Pathname(out_dir || ".")
|
39
|
+
out_dir /= target if options[:checksum]
|
40
|
+
basename = options[:checksum] ? "#{target}-#{target_pkg_desc.checksum}.xcframework" : "#{target}.xcframework"
|
41
|
+
|
42
|
+
Framework::XCFramework.new(
|
43
|
+
name: target,
|
44
|
+
pkg_dir: root_dir,
|
45
|
+
config: config,
|
46
|
+
sdks: sdks,
|
47
|
+
path: out_dir / basename,
|
48
|
+
pkg_desc: target_pkg_desc,
|
49
|
+
).create
|
50
|
+
end
|
51
|
+
|
52
|
+
def resolve(force: false)
|
53
|
+
return if @resolved && !force
|
54
|
+
|
55
|
+
UI.info("Resolving package dependencies (package: #{root_dir.basename.to_s.dark})")
|
56
|
+
Sh.run("swift package resolve --package-path #{root_dir} 2>&1")
|
57
|
+
create_symlinks_to_local_pkgs
|
58
|
+
@resolved = true
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def validate!
|
64
|
+
return unless root_dir.glob("Package*.swift").empty?
|
65
|
+
raise GeneralError, "No Package.swift in #{root_dir}. Are you sure you're running on a package dir?"
|
66
|
+
end
|
67
|
+
|
68
|
+
def pkg_desc_of_target(name, skip_resolve: false)
|
69
|
+
# TODO: Refactor this resolution logic
|
70
|
+
find_pkg_desc = proc do
|
71
|
+
# The current package contains the given target
|
72
|
+
return pkg_desc if pkg_desc.has_target?(name)
|
73
|
+
|
74
|
+
UI.message(
|
75
|
+
"#{name.yellow.dark} is not a direct target of package #{root_dir.basename.to_s.dark} " \
|
76
|
+
"-> trigger from dependencies"
|
77
|
+
)
|
78
|
+
# Otherwise, it's inside one of the dependencies. Need to resolve then find it
|
79
|
+
resolve unless skip_resolve
|
80
|
+
root_dir.glob(".build/checkouts/*").each do |dir|
|
81
|
+
desc = Description.in_dir(dir)
|
82
|
+
return desc if desc.has_target?(name)
|
83
|
+
end
|
84
|
+
raise GeneralError, "Cannot find package with the given target #{name}"
|
85
|
+
end
|
86
|
+
|
87
|
+
@cache_pkg_desc_by_name ||= {}
|
88
|
+
@cache_pkg_desc_by_name[name] = find_pkg_desc.call unless @cache_pkg_desc_by_name.key?(name)
|
89
|
+
@cache_pkg_desc_by_name[name]
|
90
|
+
end
|
91
|
+
|
92
|
+
def pkg_desc
|
93
|
+
@pkg_desc ||= Description.in_dir(root_dir)
|
94
|
+
end
|
95
|
+
|
96
|
+
def create_symlinks_to_local_pkgs
|
97
|
+
pkg_desc.dependencies.select(&:local?).each do |dep|
|
98
|
+
dep.path.symlink_to(root_dir / ".build/checkouts/#{dep.slug}")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module XCCache
|
2
|
+
module SPM
|
3
|
+
class Package
|
4
|
+
module UmbrellaBuilldMixin
|
5
|
+
def build(options = {})
|
6
|
+
to_build = targets_to_build(options)
|
7
|
+
return UI.warn("Detected no targets to build among cache-missed targets") if to_build.empty?
|
8
|
+
|
9
|
+
UI.info("-> Targets to build: #{to_build.to_s.bold}")
|
10
|
+
super(options.merge(:targets => to_build))
|
11
|
+
sync_cachemap
|
12
|
+
end
|
13
|
+
|
14
|
+
def targets_to_build(options)
|
15
|
+
items = options[:targets] || []
|
16
|
+
items = config.cachemap.missed.map { |x| File.basename(x) } if items.empty?
|
17
|
+
targets = @descs.flat_map(&:targets).select { |t| items.include?(t.name) }
|
18
|
+
if options[:recursive]
|
19
|
+
UI.message("Will include cache-missed recursive targets")
|
20
|
+
targets += targets.flat_map do |t|
|
21
|
+
t.recursive_targets.select { |x| config.cachemap.missed?(x.full_name) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
# TODO: Sort by number of dependents
|
25
|
+
targets.map(&:full_name).uniq
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|