xccache 0.0.2 → 1.0.0
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/cache/cachemap.rb +28 -11
- data/lib/xccache/command/base.rb +1 -4
- data/lib/xccache/command/build.rb +1 -14
- data/lib/xccache/command/off.rb +26 -0
- data/lib/xccache/command.rb +10 -1
- data/lib/xccache/core/config.rb +10 -4
- data/lib/xccache/core/git.rb +1 -1
- data/lib/xccache/core/lockfile.rb +4 -0
- data/lib/xccache/core/sh.rb +3 -17
- data/lib/xccache/core/syntax/hash.rb +4 -0
- data/lib/xccache/core/system.rb +3 -1
- data/lib/xccache/installer/build.rb +3 -1
- data/lib/xccache/installer/integration/build.rb +28 -0
- data/lib/xccache/installer/integration/descs.rb +27 -0
- data/lib/xccache/installer/integration/supporting_files.rb +29 -0
- data/lib/xccache/installer/integration/viz.rb +38 -0
- data/lib/xccache/installer/integration.rb +12 -0
- data/lib/xccache/installer.rb +33 -9
- data/lib/xccache/spm/build.rb +1 -1
- data/lib/xccache/spm/desc/base.rb +1 -4
- data/lib/xccache/spm/desc/desc.rb +2 -30
- data/lib/xccache/spm/mixin.rb +5 -4
- data/lib/xccache/spm/pkg/base.rb +40 -34
- data/lib/xccache/spm/pkg/proxy.rb +60 -0
- data/lib/xccache/spm/pkg/proxy_executable.rb +77 -0
- data/lib/xccache/utils/template.rb +1 -1
- data/lib/xccache/xcodeproj/pkg.rb +1 -1
- data/lib/xccache/xcodeproj/pkg_product_dependency.rb +2 -2
- data/lib/xccache/xcodeproj/project.rb +3 -3
- data/lib/xccache/xcodeproj/target.rb +2 -2
- data/lib/xccache.rb +5 -0
- metadata +10 -10
- data/lib/xccache/assets/templates/umbrella.Package.swift.template +0 -182
- data/lib/xccache/spm/pkg/umbrella/build.rb +0 -30
- data/lib/xccache/spm/pkg/umbrella/cachemap.rb +0 -110
- data/lib/xccache/spm/pkg/umbrella/descs.rb +0 -35
- data/lib/xccache/spm/pkg/umbrella/manifest.rb +0 -83
- data/lib/xccache/spm/pkg/umbrella/viz.rb +0 -40
- data/lib/xccache/spm/pkg/umbrella/xcconfigs.rb +0 -31
- data/lib/xccache/spm/pkg/umbrella.rb +0 -91
data/lib/xccache/spm/pkg/base.rb
CHANGED
@@ -2,11 +2,15 @@ require "json"
|
|
2
2
|
require "xccache/spm/xcframework/slice"
|
3
3
|
require "xccache/spm/xcframework/xcframework"
|
4
4
|
require "xccache/spm/xcframework/metadata"
|
5
|
+
require "xccache/spm/pkg/proxy"
|
5
6
|
require "xccache/swift/sdk"
|
6
7
|
|
7
8
|
module XCCache
|
8
9
|
module SPM
|
9
10
|
class Package
|
11
|
+
include Config::Mixin
|
12
|
+
include Proxy::Mixin
|
13
|
+
|
10
14
|
include Cacheable
|
11
15
|
cacheable :pkg_desc_of_target
|
12
16
|
|
@@ -14,7 +18,6 @@ module XCCache
|
|
14
18
|
|
15
19
|
def initialize(options = {})
|
16
20
|
@root_dir = Pathname(options[:root_dir] || ".").expand_path
|
17
|
-
@warn_if_not_direct_target = options.fetch(:warn_if_not_direct_target, true)
|
18
21
|
end
|
19
22
|
|
20
23
|
def build(options = {})
|
@@ -33,7 +36,10 @@ module XCCache
|
|
33
36
|
end
|
34
37
|
|
35
38
|
def build_target(target: nil, sdks: nil, config: nil, out_dir: nil, **options)
|
36
|
-
target_pkg_desc = pkg_desc_of_target(
|
39
|
+
target_pkg_desc = pkg_desc_of_target(
|
40
|
+
target,
|
41
|
+
ensure_exist: true,
|
42
|
+
)
|
37
43
|
if target_pkg_desc.binary_targets.any? { |t| t.name == target }
|
38
44
|
return UI.warn("Target #{target} is a binary target -> no need to build")
|
39
45
|
end
|
@@ -42,33 +48,44 @@ module XCCache
|
|
42
48
|
|
43
49
|
out_dir = Pathname(out_dir || ".")
|
44
50
|
out_dir /= target.name if options[:checksum]
|
51
|
+
ext = target.macro? ? ".macro" : ".xcframework"
|
45
52
|
basename = options[:checksum] ? "#{target.name}-#{target.checksum}" : target.name
|
46
|
-
|
53
|
+
binary_path = out_dir / "#{basename}#{ext}"
|
47
54
|
|
48
|
-
Dir.create_tmpdir do |
|
55
|
+
Dir.create_tmpdir do |tmpdir|
|
49
56
|
cls = target.macro? ? Macro : XCFramework
|
50
57
|
cls.new(
|
51
58
|
name: target.name,
|
52
59
|
pkg_dir: root_dir,
|
53
60
|
config: config,
|
54
61
|
sdks: sdks,
|
55
|
-
path:
|
56
|
-
tmpdir:
|
62
|
+
path: binary_path,
|
63
|
+
tmpdir: tmpdir,
|
57
64
|
pkg_desc: target_pkg_desc,
|
58
65
|
library_evolution: options[:library_evolution],
|
59
66
|
).build(**options)
|
60
67
|
end
|
68
|
+
return if (symlinks_dir = options[:symlinks_dir]).nil?
|
69
|
+
binary_path.symlink_to(symlinks_dir / target.name / "#{target.name}#{ext}")
|
61
70
|
end
|
62
71
|
|
63
|
-
def resolve
|
64
|
-
return if @resolved
|
65
|
-
|
66
|
-
UI.section("Resolving package dependencies (package: #{root_dir.basename})", timing: true) do
|
67
|
-
Sh.run("swift package resolve --package-path #{root_dir} 2>&1")
|
68
|
-
end
|
72
|
+
def resolve
|
73
|
+
return if @resolved
|
74
|
+
xccache_proxy.run("resolve --pkg #{root_dir} --metadata #{metadata_dir}")
|
69
75
|
@resolved = true
|
70
76
|
end
|
71
77
|
|
78
|
+
def pkg_desc_of_target(name, **options)
|
79
|
+
resolve
|
80
|
+
desc = descs.find { |d| d.has_target?(name) }
|
81
|
+
raise GeneralError, "Cannot find package with the given target #{name}" if options[:ensure_exist] && desc.nil?
|
82
|
+
desc
|
83
|
+
end
|
84
|
+
|
85
|
+
def get_target(name)
|
86
|
+
pkg_desc_of_target(name)&.get_target(name)
|
87
|
+
end
|
88
|
+
|
72
89
|
private
|
73
90
|
|
74
91
|
def validate!
|
@@ -76,31 +93,20 @@ module XCCache
|
|
76
93
|
raise GeneralError, "No Package.swift in #{root_dir}. Are you sure you're running on a package dir?"
|
77
94
|
end
|
78
95
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
96
|
+
def metadata_dir
|
97
|
+
config.in_installation? ? config.spm_metadata_dir : root_dir / ".build/metadata"
|
98
|
+
end
|
82
99
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
# Otherwise, it's inside one of the dependencies. Need to resolve then find it
|
90
|
-
resolve unless skip_resolving_dependencies
|
91
|
-
|
92
|
-
@descs ||= if Config.instance.in_installation?
|
93
|
-
then Description.descs_in_metadata_dir[0]
|
94
|
-
else
|
95
|
-
Description.descs_in_dir(Pathname(".").expand_path)[0]
|
96
|
-
end
|
97
|
-
desc = @descs.find { |d| d.has_target?(name) }
|
98
|
-
return desc unless desc.nil?
|
99
|
-
raise GeneralError, "Cannot find package with the given target #{name}"
|
100
|
+
def descs
|
101
|
+
@descs ||= load_descs[0]
|
102
|
+
end
|
103
|
+
|
104
|
+
def descs_by_name
|
105
|
+
@descs_by_name ||= load_descs[1]
|
100
106
|
end
|
101
107
|
|
102
|
-
def
|
103
|
-
@
|
108
|
+
def load_descs
|
109
|
+
@descs, @descs_by_name = Description.descs_in_metadata_dir(metadata_dir)
|
104
110
|
end
|
105
111
|
end
|
106
112
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
Dir["#{__dir__}/#{File.basename(__FILE__, '.rb')}/*.rb"].sort.each { |f| require f }
|
2
|
+
require_relative "proxy_executable"
|
3
|
+
|
4
|
+
module XCCache
|
5
|
+
module SPM
|
6
|
+
class Package
|
7
|
+
class Proxy < Package
|
8
|
+
module Mixin
|
9
|
+
def xccache_proxy
|
10
|
+
@xccache_proxy ||= Executable.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
include Mixin
|
15
|
+
|
16
|
+
def umbrella
|
17
|
+
@umbrella ||= Package.new(root_dir: config.spm_umbrella_sandbox)
|
18
|
+
end
|
19
|
+
|
20
|
+
def prepare(options = {})
|
21
|
+
xccache_proxy.run("gen-umbrella")
|
22
|
+
umbrella.resolve
|
23
|
+
invalidate_cache(sdks: options[:sdks])
|
24
|
+
gen_proxy
|
25
|
+
end
|
26
|
+
|
27
|
+
def gen_proxy
|
28
|
+
xccache_proxy.run("gen-proxy")
|
29
|
+
config.cachemap.update_from_graph(graph.reload)
|
30
|
+
end
|
31
|
+
|
32
|
+
def invalidate_cache(sdks: [])
|
33
|
+
UI.message("Invalidating cache (sdks: #{sdks.map(&:name)})")
|
34
|
+
|
35
|
+
config.spm_cache_dir.glob("*/*.{xcframework,macro}").each do |p|
|
36
|
+
cmps = p.basename(".*").to_s.split("-")
|
37
|
+
name, checksum = cmps[...-1].join("-"), cmps[-1]
|
38
|
+
p_without_checksum = config.spm_binaries_dir / name / "#{name}#{p.extname}"
|
39
|
+
accept_cache = proc { p.symlink_to(p_without_checksum) }
|
40
|
+
reject_cache = proc { p_without_checksum.rmtree if p_without_checksum.exist? }
|
41
|
+
next reject_cache.call if (target = umbrella.get_target(name)).nil?
|
42
|
+
next reject_cache.call if target.checksum != checksum
|
43
|
+
# For macro, we just need the tool binary to exist
|
44
|
+
next accept_cache.call if target.macro?
|
45
|
+
|
46
|
+
# For regular targets, the xcframework must satisfy the sdk constraints (ie. containing all the slices)
|
47
|
+
metadata = XCFramework::Metadata.new(p / "Info.plist")
|
48
|
+
expected_triples = sdks.map { |sdk| sdk.triple(without_vendor: true) }
|
49
|
+
missing_triples = expected_triples - metadata.triples
|
50
|
+
missing_triples.empty? ? accept_cache.call : reject_cache.call
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def graph
|
55
|
+
@graph ||= JSONRepresentable.new(root_dir / "graph.json")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module XCCache
|
2
|
+
module SPM
|
3
|
+
class Package
|
4
|
+
class Proxy < Package
|
5
|
+
class Executable
|
6
|
+
REPO_URL = "https://github.com/trinhngocthuyen/xccache-proxy".freeze
|
7
|
+
VERSION_OR_SHA = "0.0.1".freeze
|
8
|
+
|
9
|
+
def run(cmd)
|
10
|
+
env = { "FORCE_OUTPUT" => "console", "FORCE_COLOR" => "1" } if Config.instance.ansi?
|
11
|
+
cmd = cmd.is_a?(Array) ? [bin_path.to_s] + cmd : [bin_path.to_s, cmd]
|
12
|
+
Sh.run(cmd, env: env)
|
13
|
+
end
|
14
|
+
|
15
|
+
def bin_path
|
16
|
+
@bin_path ||= lookup
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def lookup
|
22
|
+
[
|
23
|
+
local_bin_path,
|
24
|
+
default_bin_path,
|
25
|
+
].find(&:exist?) || download_or_build_from_source
|
26
|
+
end
|
27
|
+
|
28
|
+
def default_use_downloaded?
|
29
|
+
VERSION_OR_SHA.include?(".")
|
30
|
+
end
|
31
|
+
|
32
|
+
def download_or_build_from_source
|
33
|
+
default_use_downloaded? ? download : build_from_source
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_from_source
|
37
|
+
UI.section("Building xccache-proxy binary from source...".magenta) do
|
38
|
+
dir = Dir.prepare("~/.xccache/xccache-proxy", expand: true)
|
39
|
+
git = Git.new(dir)
|
40
|
+
git.init unless git.init?
|
41
|
+
git.remote("add", "origin", REPO_URL) unless git.remote(capture: true)[0].strip == "origin"
|
42
|
+
git.fetch("origin", VERSION_OR_SHA)
|
43
|
+
git.checkout("-f", "FETCH_HEAD", capture: true)
|
44
|
+
|
45
|
+
Dir.chdir(dir) { Sh.run("make build CONFIGURATION=release") }
|
46
|
+
(dir / ".build" / "release" / "xccache-proxy").copy(to: default_bin_path)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def download
|
51
|
+
UI.section("Downloading xccache-proxy binary from remote...".magenta) do
|
52
|
+
Dir.create_tmpdir do |dir|
|
53
|
+
url = "#{REPO_URL}/releases/download/#{VERSION_OR_SHA}/xccache-proxy.zip"
|
54
|
+
default_bin_path.parent.mkpath
|
55
|
+
tmp_path = dir / File.basename(url)
|
56
|
+
Sh.run("curl -fSL -o #{tmp_path} #{url} && unzip -d #{default_bin_path.parent} #{tmp_path}")
|
57
|
+
FileUtils.chmod("+x", default_bin_path)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
default_bin_path
|
61
|
+
end
|
62
|
+
|
63
|
+
def default_bin_path
|
64
|
+
@default_bin_path ||= begin
|
65
|
+
dir = LIBEXEC / (default_use_downloaded? ? ".download" : ".build")
|
66
|
+
dir / "xccache-proxy-#{VERSION_OR_SHA}" / "xccache-proxy"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def local_bin_path
|
71
|
+
@local_bin_path ||= LIBEXEC / ".local" / "xccache-proxy"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -12,12 +12,12 @@ module Xcodeproj
|
|
12
12
|
return package if package
|
13
13
|
return if @warned_missing_pkg
|
14
14
|
@warned_missing_pkg = true
|
15
|
-
|
15
|
+
Log.warn("Missing pkg of product dependency #{uuid}: #{to_hash}")
|
16
16
|
end
|
17
17
|
|
18
18
|
def remove_alongside_related
|
19
19
|
target = referrers.find { |x| x.is_a?(PBXNativeTarget) }
|
20
|
-
|
20
|
+
Log.info(
|
21
21
|
"(-) Remove #{product_name.red} from product dependencies of target #{target.display_name.bold}"
|
22
22
|
)
|
23
23
|
target.dependencies.each { |x| x.remove_from_project if x.product_ref == self }
|
@@ -41,7 +41,7 @@ module Xcodeproj
|
|
41
41
|
pkg_hash = XCCache::Lockfile::Pkg.from_h(hash)
|
42
42
|
pkg_hash["relative_path"] = pkg_hash.relative_path_from_dir(dir).to_s if pkg_hash.key == "path_from_root"
|
43
43
|
|
44
|
-
Log.
|
44
|
+
Log.info("Add package #{pkg_hash.id.bold} to project #{display_name.bold}")
|
45
45
|
cls = pkg_hash.local? ? XCLocalSwiftPackageReference : XCRemoteSwiftPackageReference
|
46
46
|
ref = new(cls)
|
47
47
|
custom_keys = ["path_from_root"]
|
@@ -51,13 +51,13 @@ module Xcodeproj
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def add_xccache_pkg
|
54
|
-
sandbox_path = XCCache::Config.instance.
|
54
|
+
sandbox_path = XCCache::Config.instance.spm_proxy_sandbox
|
55
55
|
add_pkg("relative_path" => sandbox_path.relative_path_from(path.parent).to_s)
|
56
56
|
end
|
57
57
|
|
58
58
|
def remove_pkgs(&block)
|
59
59
|
pkgs.select(&block).each do |pkg|
|
60
|
-
|
60
|
+
Log.info("(-) Remove #{pkg.display_name.red} from package refs of project #{display_name.bold}")
|
61
61
|
pkg.remove_from_project
|
62
62
|
end
|
63
63
|
end
|
@@ -19,14 +19,14 @@ module Xcodeproj
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def add_pkg_product_dependency(name)
|
22
|
-
Log.
|
22
|
+
Log.info("(+) Add dependency #{name.blue} to target #{display_name.bold}")
|
23
23
|
pkg_name, product_name = name.split("/")
|
24
24
|
pkg = project.get_pkg(pkg_name)
|
25
25
|
pkg_product_dependencies << pkg.create_target_dependency_ref(product_name).product_ref
|
26
26
|
end
|
27
27
|
|
28
28
|
def add_xccache_product_dependency
|
29
|
-
add_pkg_product_dependency("
|
29
|
+
add_pkg_product_dependency("proxy/#{name}.xccache")
|
30
30
|
end
|
31
31
|
|
32
32
|
def remove_xccache_product_dependencies
|
data/lib/xccache.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xccache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thuyen Trinh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-05-
|
11
|
+
date: 2025-05-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: claide
|
@@ -69,7 +69,6 @@ files:
|
|
69
69
|
- lib/xccache/assets/templates/framework.modulemap.template
|
70
70
|
- lib/xccache/assets/templates/resource_bundle_accessor.m.template
|
71
71
|
- lib/xccache/assets/templates/resource_bundle_accessor.swift.template
|
72
|
-
- lib/xccache/assets/templates/umbrella.Package.swift.template
|
73
72
|
- lib/xccache/cache/cachemap.rb
|
74
73
|
- lib/xccache/command.rb
|
75
74
|
- lib/xccache/command/base.rb
|
@@ -77,6 +76,7 @@ files:
|
|
77
76
|
- lib/xccache/command/cache.rb
|
78
77
|
- lib/xccache/command/cache/clean.rb
|
79
78
|
- lib/xccache/command/cache/list.rb
|
79
|
+
- lib/xccache/command/off.rb
|
80
80
|
- lib/xccache/command/pkg.rb
|
81
81
|
- lib/xccache/command/pkg/build.rb
|
82
82
|
- lib/xccache/command/remote.rb
|
@@ -102,6 +102,11 @@ files:
|
|
102
102
|
- lib/xccache/core/system.rb
|
103
103
|
- lib/xccache/installer.rb
|
104
104
|
- lib/xccache/installer/build.rb
|
105
|
+
- lib/xccache/installer/integration.rb
|
106
|
+
- lib/xccache/installer/integration/build.rb
|
107
|
+
- lib/xccache/installer/integration/descs.rb
|
108
|
+
- lib/xccache/installer/integration/supporting_files.rb
|
109
|
+
- lib/xccache/installer/integration/viz.rb
|
105
110
|
- lib/xccache/installer/rollback.rb
|
106
111
|
- lib/xccache/installer/use.rb
|
107
112
|
- lib/xccache/main.rb
|
@@ -119,13 +124,8 @@ files:
|
|
119
124
|
- lib/xccache/spm/mixin.rb
|
120
125
|
- lib/xccache/spm/pkg.rb
|
121
126
|
- lib/xccache/spm/pkg/base.rb
|
122
|
-
- lib/xccache/spm/pkg/
|
123
|
-
- lib/xccache/spm/pkg/
|
124
|
-
- lib/xccache/spm/pkg/umbrella/cachemap.rb
|
125
|
-
- lib/xccache/spm/pkg/umbrella/descs.rb
|
126
|
-
- lib/xccache/spm/pkg/umbrella/manifest.rb
|
127
|
-
- lib/xccache/spm/pkg/umbrella/viz.rb
|
128
|
-
- lib/xccache/spm/pkg/umbrella/xcconfigs.rb
|
127
|
+
- lib/xccache/spm/pkg/proxy.rb
|
128
|
+
- lib/xccache/spm/pkg/proxy_executable.rb
|
129
129
|
- lib/xccache/spm/xcframework.rb
|
130
130
|
- lib/xccache/spm/xcframework/metadata.rb
|
131
131
|
- lib/xccache/spm/xcframework/slice.rb
|
@@ -1,182 +0,0 @@
|
|
1
|
-
// swift-tools-version: 6.0
|
2
|
-
import Foundation
|
3
|
-
import PackageDescription
|
4
|
-
|
5
|
-
/*
|
6
|
-
MARK: INSTRUCTIONS
|
7
|
-
----------------------------------------------------------------------------------------------------------------------
|
8
|
-
This Package manifest is auto-generated from xccache (last updated: <%= timestamp %>).
|
9
|
-
----------------------------------------------------------------------------------------------------------------------
|
10
|
-
During local development, you can switch between *BINARY mode* and *SOURCE mode* by adding or removing the `.binary`
|
11
|
-
suffix in the JSON below.
|
12
|
-
As a user, you just need to care about the JSON. The rest is just the tool's internal logic.
|
13
|
-
|
14
|
-
NOTE that a product (ex. `pkg/foo.binary`) can only be used as binary if:
|
15
|
-
- Binary exists for `pkg/foo` (in `xccache/packages/binaries/foo/foo.xcframework`)
|
16
|
-
- Binary exists for all of its dependencies
|
17
|
-
The tool auto-fallbacks to the *SOURCE mode* if the conditions are not met.
|
18
|
-
----------------------------------------------------------------------------------------------------------------------
|
19
|
-
*/
|
20
|
-
let JSON = """
|
21
|
-
<%= json %>
|
22
|
-
"""
|
23
|
-
let DEPENDENCIES: [Package.Dependency] = [
|
24
|
-
<%= dependencies %>
|
25
|
-
]
|
26
|
-
let PLATFORMS: [SupportedPlatform] = [
|
27
|
-
<%= platforms %>
|
28
|
-
]
|
29
|
-
let PRODUCTS_TO_TARGETS: [String: [String]] = try parseJSON("""
|
30
|
-
<%= products_to_targets %>
|
31
|
-
""")
|
32
|
-
// ------------------------------------------------------------------------------------
|
33
|
-
|
34
|
-
// MARK: Main
|
35
|
-
_ = try XCCache.Package(parseJSON(JSON)).spm
|
36
|
-
|
37
|
-
// MARK: XCCache
|
38
|
-
enum XCCache {
|
39
|
-
// MARK: Config
|
40
|
-
@MainActor
|
41
|
-
struct Config {
|
42
|
-
static let pkgDir = URL(filePath: #filePath).deletingLastPathComponent()
|
43
|
-
// NOTE: Do NOT change `binariesDir` to `static let`
|
44
|
-
// Somehow, incremental resolution doesnt work causing the `binaryExist` wrongly cached
|
45
|
-
static var binariesDir: URL { pkgDir.appending(path: "binaries") }
|
46
|
-
}
|
47
|
-
|
48
|
-
// MARK: Package
|
49
|
-
@MainActor
|
50
|
-
final class Package {
|
51
|
-
let targets: [XCCache.Target]
|
52
|
-
init(_ dict: [String: [String]]) {
|
53
|
-
targets = dict.map { XCCache.Target($0, $1) }
|
54
|
-
}
|
55
|
-
|
56
|
-
var spm: PackageDescription.Package {
|
57
|
-
let regularTargets = targets.map(\.spm)
|
58
|
-
let binaryTargets = targets.flatMap(\.flattenRegularDeps).unique(by: \.name).compactMap(\.spmBinaryTarget)
|
59
|
-
return .init(
|
60
|
-
name: "xccache",
|
61
|
-
platforms: PLATFORMS,
|
62
|
-
products: targets.map(\.spmProduct),
|
63
|
-
dependencies: DEPENDENCIES,
|
64
|
-
targets: regularTargets + binaryTargets
|
65
|
-
)
|
66
|
-
}
|
67
|
-
}
|
68
|
-
|
69
|
-
// MARK: Target
|
70
|
-
@MainActor
|
71
|
-
final class Target {
|
72
|
-
let name: String
|
73
|
-
let deps: [UmbrellaDependency]
|
74
|
-
init(_ name: String, _ deps: [String]) {
|
75
|
-
self.name = name
|
76
|
-
self.deps = deps.map { UmbrellaDependency($0) }
|
77
|
-
}
|
78
|
-
|
79
|
-
var spmProduct: PackageDescription.Product {
|
80
|
-
.library(name: name, targets: [name])
|
81
|
-
}
|
82
|
-
var spm: PackageDescription.Target {
|
83
|
-
.target(
|
84
|
-
name: name,
|
85
|
-
dependencies: flattenRegularDeps.map(\.spm),
|
86
|
-
path: ".Sources/\(name)",
|
87
|
-
swiftSettings: [
|
88
|
-
.unsafeFlags(macroFlags),
|
89
|
-
]
|
90
|
-
)
|
91
|
-
}
|
92
|
-
var flattenDeps: [Dependency] { deps.flatMap(\.toBinariesIfOk).unique(by: \.name) }
|
93
|
-
var flattenRegularDeps: [Dependency] { flattenDeps.filter(\.regular) }
|
94
|
-
var macroFlags: [String] { flattenDeps.filter(\.macroBinary).flatMap(\.macroFlags) }
|
95
|
-
}
|
96
|
-
|
97
|
-
// MARK: Dependency
|
98
|
-
@MainActor
|
99
|
-
class Dependency {
|
100
|
-
let name: String
|
101
|
-
let bareName: String
|
102
|
-
let binaryURL: URL
|
103
|
-
let binary: Bool
|
104
|
-
let macro: Bool
|
105
|
-
var regular: Bool { !macro }
|
106
|
-
var regularBinary: Bool { binary && regular }
|
107
|
-
var macroBinary: Bool { binary && macro }
|
108
|
-
|
109
|
-
init(_ name: String) {
|
110
|
-
self.name = name
|
111
|
-
self.bareName = String(name.basename.split(separator: ".")[0])
|
112
|
-
self.macro = name.contains(".macro")
|
113
|
-
self.binaryURL = if macro {
|
114
|
-
Config.binariesDir.appending(path: "\(bareName)/\(bareName).macro").readlink()
|
115
|
-
} else {
|
116
|
-
Config.binariesDir.appending(path: "\(bareName)/\(bareName).xcframework").readlink()
|
117
|
-
}
|
118
|
-
self.binary = name.hasSuffix(".binary") && binaryURL.exist
|
119
|
-
}
|
120
|
-
|
121
|
-
var macroFlags: [String] {
|
122
|
-
["-load-plugin-executable", "\(binaryURL.path())#\(bareName)"]
|
123
|
-
}
|
124
|
-
|
125
|
-
var spm: PackageDescription.Target.Dependency {
|
126
|
-
if binary { return .byName(name: name) }
|
127
|
-
return .product(name: bareName, package: name.slug)
|
128
|
-
}
|
129
|
-
var spmBinaryTarget: PackageDescription.Target? {
|
130
|
-
regularBinary ? .binaryTarget(name: name, path: "binaries/\(bareName)/\(bareName).xcframework") : nil
|
131
|
-
}
|
132
|
-
}
|
133
|
-
|
134
|
-
@MainActor
|
135
|
-
class UmbrellaDependency: Dependency {
|
136
|
-
let binaries: [Dependency]
|
137
|
-
override init(_ name: String) {
|
138
|
-
binaries = (PRODUCTS_TO_TARGETS[name.withoutBinary] ?? []).map { Dependency("\($0).binary") }
|
139
|
-
super.init(name)
|
140
|
-
}
|
141
|
-
var toBinariesIfOk: [Dependency] {
|
142
|
-
if name.hasSuffix(".binary"), !binaries.isEmpty && binaries.allSatisfy(\.binary) { return binaries }
|
143
|
-
return [Dependency(name.withoutBinary)]
|
144
|
-
}
|
145
|
-
}
|
146
|
-
}
|
147
|
-
|
148
|
-
// MARK: Helpers
|
149
|
-
func parseJSON<T>(_ content: String) throws -> T {
|
150
|
-
if let data = content.data(using: .utf8), let result = try JSONSerialization.jsonObject(with: data) as? T {
|
151
|
-
return result
|
152
|
-
}
|
153
|
-
throw NSError(domain: "Invalid JSON:\n\(content)", code: 111)
|
154
|
-
}
|
155
|
-
|
156
|
-
extension URL {
|
157
|
-
var basename: String { lastPathComponent }
|
158
|
-
var exist: Bool { FileManager.default.fileExists(atPath: readlink().path()) }
|
159
|
-
/// Resolve symlinks recursively, equivalent to `readlink -f` in bash
|
160
|
-
func readlink() -> URL {
|
161
|
-
var prev = self, cur = resolvingSymlinksInPath()
|
162
|
-
while prev != cur {
|
163
|
-
prev = cur
|
164
|
-
cur = cur.resolvingSymlinksInPath()
|
165
|
-
}
|
166
|
-
return cur
|
167
|
-
}
|
168
|
-
}
|
169
|
-
|
170
|
-
extension String {
|
171
|
-
var slug: String { (self as NSString).deletingLastPathComponent.basename }
|
172
|
-
var basename: String { (self as NSString).lastPathComponent }
|
173
|
-
var withoutExtenstion: String { (self as NSString).deletingPathExtension }
|
174
|
-
var withoutBinary: String { replacing(#/\.binary$/#, with: "") }
|
175
|
-
}
|
176
|
-
|
177
|
-
extension Sequence {
|
178
|
-
func unique<T: Hashable>(by: (Element) -> T) -> [Element] {
|
179
|
-
var seen: Set<T> = []
|
180
|
-
return filter { seen.insert(by($0)).inserted }
|
181
|
-
}
|
182
|
-
}
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module XCCache
|
2
|
-
module SPM
|
3
|
-
class Package
|
4
|
-
module UmbrellaBuildMixin
|
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(sdks: options[:sdks])
|
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
|