xccache 0.0.1a2 → 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.
- checksums.yaml +4 -4
- data/lib/xccache/assets/templates/cachemap.html.template +1 -0
- data/lib/xccache/assets/templates/cachemap.js.template +35 -34
- data/lib/xccache/assets/templates/cachemap.style.css.template +3 -1
- data/lib/xccache/assets/templates/framework.modulemap.template +1 -1
- data/lib/xccache/assets/templates/resource_bundle_accessor.m.template +3 -3
- data/lib/xccache/assets/templates/umbrella.Package.swift.template +43 -7
- data/lib/xccache/cache/cachemap.rb +11 -15
- data/lib/xccache/command/base.rb +29 -0
- data/lib/xccache/command/build.rb +12 -6
- data/lib/xccache/command/pkg/build.rb +2 -6
- data/lib/xccache/command/pkg.rb +1 -0
- data/lib/xccache/command/remote/pull.rb +15 -0
- data/lib/xccache/command/remote/push.rb +15 -0
- data/lib/xccache/command/remote.rb +39 -0
- data/lib/xccache/command/rollback.rb +2 -1
- data/lib/xccache/command/use.rb +3 -2
- data/lib/xccache/command.rb +18 -2
- data/lib/xccache/core/config.rb +30 -2
- data/lib/xccache/core/git.rb +11 -3
- data/lib/xccache/core/log.rb +11 -1
- data/lib/xccache/core/parallel.rb +10 -0
- data/lib/xccache/core/sh.rb +4 -3
- data/lib/xccache/core/syntax/plist.rb +17 -0
- data/lib/xccache/core/system.rb +8 -0
- data/lib/xccache/installer/build.rb +2 -6
- data/lib/xccache/installer/rollback.rb +1 -0
- data/lib/xccache/installer/use.rb +3 -2
- data/lib/xccache/installer.rb +50 -4
- data/lib/xccache/spm/build.rb +53 -0
- data/lib/xccache/spm/desc/base.rb +10 -3
- data/lib/xccache/spm/desc/desc.rb +31 -15
- data/lib/xccache/spm/desc/target/binary.rb +8 -0
- data/lib/xccache/spm/desc/target/macro.rb +8 -0
- data/lib/xccache/spm/desc/target.rb +28 -2
- data/lib/xccache/spm/macro.rb +44 -0
- data/lib/xccache/spm/mixin.rb +4 -1
- data/lib/xccache/spm/pkg/base.rb +48 -44
- data/lib/xccache/spm/pkg/umbrella/build.rb +2 -2
- data/lib/xccache/spm/pkg/umbrella/cachemap.rb +44 -27
- data/lib/xccache/spm/pkg/umbrella/descs.rb +2 -13
- data/lib/xccache/spm/pkg/umbrella/manifest.rb +1 -1
- data/lib/xccache/spm/pkg/umbrella/viz.rb +3 -3
- data/lib/xccache/spm/pkg/umbrella/xcconfigs.rb +31 -0
- data/lib/xccache/spm/pkg/umbrella.rb +20 -10
- data/lib/xccache/spm/xcframework/metadata.rb +41 -0
- data/lib/xccache/{framework → spm/xcframework}/slice.rb +50 -53
- data/lib/xccache/spm/xcframework/xcframework.rb +56 -0
- data/lib/xccache/spm/xcframework.rb +2 -0
- data/lib/xccache/storage/base.rb +26 -0
- data/lib/xccache/storage/git.rb +46 -0
- data/lib/xccache/storage/s3.rb +53 -0
- data/lib/xccache/storage.rb +1 -0
- data/lib/xccache/swift/sdk.rb +21 -2
- data/lib/xccache/xcodeproj/build_configuration.rb +20 -0
- data/lib/xccache/xcodeproj/config.rb +9 -0
- data/lib/xccache/xcodeproj/file_system_synchronized_root_group.rb +17 -0
- data/lib/xccache/xcodeproj/group.rb +26 -0
- data/lib/xccache/xcodeproj/pkg.rb +5 -1
- data/lib/xccache/xcodeproj/project.rb +21 -1
- data/lib/xccache/xcodeproj/target.rb +5 -3
- metadata +40 -5
- data/lib/xccache/framework/xcframework.rb +0 -51
data/lib/xccache/spm/pkg/base.rb
CHANGED
@@ -1,15 +1,20 @@
|
|
1
1
|
require "json"
|
2
|
-
require "xccache/
|
3
|
-
require "xccache/
|
2
|
+
require "xccache/spm/xcframework/slice"
|
3
|
+
require "xccache/spm/xcframework/xcframework"
|
4
|
+
require "xccache/spm/xcframework/metadata"
|
4
5
|
require "xccache/swift/sdk"
|
5
6
|
|
6
7
|
module XCCache
|
7
8
|
module SPM
|
8
9
|
class Package
|
10
|
+
include Cacheable
|
11
|
+
cacheable :pkg_desc_of_target
|
12
|
+
|
9
13
|
attr_reader :root_dir
|
10
14
|
|
11
15
|
def initialize(options = {})
|
12
16
|
@root_dir = Pathname(options[:root_dir] || ".").expand_path
|
17
|
+
@warn_if_not_direct_target = options.fetch(:warn_if_not_direct_target, true)
|
13
18
|
end
|
14
19
|
|
15
20
|
def build(options = {})
|
@@ -17,44 +22,50 @@ module XCCache
|
|
17
22
|
targets = options.delete(:targets) || []
|
18
23
|
raise GeneralError, "No targets were specified" if targets.empty?
|
19
24
|
|
20
|
-
targets.map { |t| t.split("/")[-1] }.
|
21
|
-
UI.section("\n▶ Building target: #{t}".bold.magenta) do
|
25
|
+
targets.map { |t| t.split("/")[-1] }.each_with_index do |t, i|
|
26
|
+
UI.section("\n▶ Building target: #{t} (#{i + 1}/#{targets.count})".bold.magenta) do
|
22
27
|
build_target(**options, target: t)
|
23
28
|
rescue StandardError => e
|
24
29
|
UI.error("Failed to build target: #{t}. Error: #{e}")
|
25
|
-
raise e unless
|
30
|
+
raise e unless Config.instance.ignore_build_errors?
|
26
31
|
end
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
30
|
-
def build_target(target: nil,
|
31
|
-
target_pkg_desc = pkg_desc_of_target(target,
|
35
|
+
def build_target(target: nil, sdks: nil, config: nil, out_dir: nil, **options)
|
36
|
+
target_pkg_desc = pkg_desc_of_target(target, skip_resolving_dependencies: options[:skip_resolving_dependencies])
|
32
37
|
if target_pkg_desc.binary_targets.any? { |t| t.name == target }
|
33
38
|
return UI.warn("Target #{target} is a binary target -> no need to build")
|
34
39
|
end
|
35
40
|
|
36
|
-
|
41
|
+
target = target_pkg_desc.get_target(target)
|
37
42
|
|
38
43
|
out_dir = Pathname(out_dir || ".")
|
39
|
-
out_dir /= target if options[:checksum]
|
40
|
-
basename = options[:checksum] ? "#{target}-#{
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
44
|
+
out_dir /= target.name if options[:checksum]
|
45
|
+
basename = options[:checksum] ? "#{target.name}-#{target.checksum}" : target.name
|
46
|
+
basename += target.macro? ? ".macro" : ".xcframework"
|
47
|
+
|
48
|
+
Dir.create_tmpdir do |_tmpdir|
|
49
|
+
cls = target.macro? ? Macro : XCFramework
|
50
|
+
cls.new(
|
51
|
+
name: target.name,
|
52
|
+
pkg_dir: root_dir,
|
53
|
+
config: config,
|
54
|
+
sdks: sdks,
|
55
|
+
path: out_dir / basename,
|
56
|
+
tmpdir: Dir.create_tmpdir,
|
57
|
+
pkg_desc: target_pkg_desc,
|
58
|
+
library_evolution: options[:library_evolution],
|
59
|
+
).build(**options)
|
60
|
+
end
|
50
61
|
end
|
51
62
|
|
52
63
|
def resolve(force: false)
|
53
64
|
return if @resolved && !force
|
54
65
|
|
55
|
-
UI.
|
56
|
-
|
57
|
-
|
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
|
58
69
|
@resolved = true
|
59
70
|
end
|
60
71
|
|
@@ -65,39 +76,32 @@ module XCCache
|
|
65
76
|
raise GeneralError, "No Package.swift in #{root_dir}. Are you sure you're running on a package dir?"
|
66
77
|
end
|
67
78
|
|
68
|
-
def pkg_desc_of_target(name,
|
69
|
-
#
|
70
|
-
|
71
|
-
# The current package contains the given target
|
72
|
-
return pkg_desc if pkg_desc.has_target?(name)
|
79
|
+
def pkg_desc_of_target(name, skip_resolving_dependencies: false)
|
80
|
+
# The current package contains the given target
|
81
|
+
return pkg_desc if pkg_desc.has_target?(name)
|
73
82
|
|
83
|
+
if @warn_if_not_direct_target
|
74
84
|
UI.message(
|
75
85
|
"#{name.yellow.dark} is not a direct target of package #{root_dir.basename.to_s.dark} " \
|
76
86
|
"-> trigger from dependencies"
|
77
87
|
)
|
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
88
|
end
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
@
|
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}"
|
90
100
|
end
|
91
101
|
|
92
102
|
def pkg_desc
|
93
103
|
@pkg_desc ||= Description.in_dir(root_dir)
|
94
104
|
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
105
|
end
|
102
106
|
end
|
103
107
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
module XCCache
|
2
2
|
module SPM
|
3
3
|
class Package
|
4
|
-
module
|
4
|
+
module UmbrellaBuildMixin
|
5
5
|
def build(options = {})
|
6
6
|
to_build = targets_to_build(options)
|
7
7
|
return UI.warn("Detected no targets to build among cache-missed targets") if to_build.empty?
|
8
8
|
|
9
9
|
UI.info("-> Targets to build: #{to_build.to_s.bold}")
|
10
10
|
super(options.merge(:targets => to_build))
|
11
|
-
sync_cachemap
|
11
|
+
sync_cachemap(sdks: options[:sdks])
|
12
12
|
end
|
13
13
|
|
14
14
|
def targets_to_build(options)
|
@@ -2,27 +2,29 @@ module XCCache
|
|
2
2
|
module SPM
|
3
3
|
class Package
|
4
4
|
module UmbrellaCachemapMixin
|
5
|
-
def sync_cachemap
|
6
|
-
UI.section("Syncing cachemap")
|
5
|
+
def sync_cachemap(sdks: [])
|
6
|
+
UI.section("Syncing cachemap (sdks: #{sdks.map(&:name)})")
|
7
7
|
nodes, edges, parents = xccache_desc.traverse
|
8
|
-
cache_data = gen_cache_data(nodes, parents)
|
9
|
-
targets_data, deps_data = {}, {}
|
8
|
+
cache_data = gen_cache_data(nodes, parents, sdks)
|
9
|
+
targets_data, macros_data, deps_data = {}, {}, {}
|
10
10
|
xccache_desc.targets.each do |agg_target|
|
11
|
-
|
11
|
+
targets, macros = [], []
|
12
|
+
agg_target.direct_dependencies.each do |d|
|
13
|
+
all_hit = d.recursive_targets.all? { |t| cache_data[t] == :hit }
|
12
14
|
# If any associated targets is missed -> use original product form
|
13
15
|
# Otherwise, replace with recursive targets' binaries
|
14
|
-
deps_data[d.full_name] = d.recursive_targets.map(&:
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end.uniq.sort_by(&:downcase)
|
16
|
+
deps_data[d.full_name] = d.recursive_targets.map(&:xccache_id)
|
17
|
+
targets << (all_hit ? "#{d.full_name}.binary" : d.full_name)
|
18
|
+
macros += d.recursive_targets.select(&:macro?).map(&:full_name) if all_hit
|
19
|
+
end
|
20
|
+
targets_data[agg_target.name] = targets.uniq.sort_by(&:downcase)
|
21
|
+
macros_data[agg_target.name] = macros.uniq
|
21
22
|
end
|
22
23
|
|
23
24
|
config.cachemap.raw = {
|
24
25
|
"manifest" => {
|
25
26
|
"targets" => targets_data,
|
27
|
+
"macros" => macros_data,
|
26
28
|
"deps" => deps_data,
|
27
29
|
},
|
28
30
|
"cache" => cache_data.transform_keys(&:full_name),
|
@@ -37,11 +39,11 @@ module XCCache
|
|
37
39
|
|
38
40
|
private
|
39
41
|
|
40
|
-
def gen_cache_data(nodes, parents)
|
42
|
+
def gen_cache_data(nodes, parents, sdks)
|
41
43
|
result = nodes.to_h do |node|
|
42
44
|
res = if config.ignore?(node.full_name) then :ignored
|
43
45
|
else
|
44
|
-
verify_binary?(node) ? :hit : :missed
|
46
|
+
verify_binary?(node, sdks) ? :hit : :missed
|
45
47
|
end
|
46
48
|
[node, res]
|
47
49
|
end
|
@@ -56,35 +58,50 @@ module XCCache
|
|
56
58
|
result[node] = :missed if result[node] == :hit
|
57
59
|
to_visit += parents[node] if parents.key?(node)
|
58
60
|
end
|
59
|
-
result
|
61
|
+
result.reject { |k, _| k.name.end_with?(".xccache") }
|
60
62
|
end
|
61
63
|
|
62
|
-
def verify_binary?(target)
|
64
|
+
def verify_binary?(target, sdks)
|
63
65
|
return true if target.binary?
|
64
66
|
|
65
|
-
bpath = binary_path(target.
|
66
|
-
bpath_with_checksum = binary_path(target.
|
67
|
-
|
67
|
+
bpath = binary_path(target.xccache_id)
|
68
|
+
bpath_with_checksum = binary_path(target.xccache_id, checksum: target.checksum, in_repo: true)
|
69
|
+
|
70
|
+
check = proc do
|
71
|
+
# For macro, we just need the tool binary to exist
|
72
|
+
# For regular targets, the xcframework must satisfy the sdk constraints (ie. containing all the slices)
|
73
|
+
next bpath_with_checksum.exist? if target.macro?
|
74
|
+
|
75
|
+
metadata = XCFramework::Metadata.new(bpath_with_checksum / "Info.plist")
|
76
|
+
expected_triples = sdks.map { |sdk| sdk.triple(without_vendor: true) }
|
77
|
+
missing_triples = expected_triples - metadata.triples
|
78
|
+
missing_triples.empty?
|
79
|
+
end
|
80
|
+
|
81
|
+
# If requirements are meet, create symlink `A-abc123.xcframework` -> `A.framework`
|
68
82
|
# Otherwise, remove symlink `A.xcframework`
|
69
|
-
if
|
83
|
+
if check.call
|
70
84
|
bpath_with_checksum.symlink_to(bpath)
|
71
85
|
elsif bpath.exist?
|
72
86
|
bpath.rmtree
|
73
87
|
end
|
74
|
-
|
88
|
+
bpath.exist?
|
75
89
|
end
|
76
90
|
|
77
|
-
def binary_path(name, checksum: nil)
|
91
|
+
def binary_path(name, checksum: nil, in_repo: false)
|
78
92
|
suffix = checksum.nil? ? "" : "-#{checksum}"
|
79
|
-
|
80
|
-
|
93
|
+
ext = File.extname(name) == ".macro" ? ".macro" : ".xcframework"
|
94
|
+
binaries_dir = in_repo ? config.spm_repo_dir : config.spm_binaries_dir
|
95
|
+
p = binaries_dir / File.basename(name, ".*")
|
96
|
+
p / "#{p.basename}#{suffix}#{ext}"
|
81
97
|
end
|
82
98
|
|
83
99
|
def target_to_cytoscape_node(x, cache_data)
|
84
100
|
h = { :id => x.full_name, :cache => cache_data[x] }
|
85
|
-
h[:type] =
|
86
|
-
|
87
|
-
h[:
|
101
|
+
h[:type] = if x.name.end_with?(".xccache") then "agg"
|
102
|
+
elsif x.macro? then "macro" end
|
103
|
+
h[:checksum] = x.checksum
|
104
|
+
h[:binary] = binary_path(x.xccache_id) if cache_data[x] == :hit
|
88
105
|
h
|
89
106
|
end
|
90
107
|
end
|
@@ -3,19 +3,8 @@ module XCCache
|
|
3
3
|
class Package
|
4
4
|
module UmbrellaDescsMixin
|
5
5
|
def gen_metadata
|
6
|
-
UI.section("Generating metadata of packages") do
|
7
|
-
|
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
|
6
|
+
UI.section("Generating metadata of packages", timing: true) do
|
7
|
+
@descs, @descs_by_name = Description.descs_in_dir(root_dir, save_to_dir: config.spm_metadata_dir)
|
19
8
|
end
|
20
9
|
end
|
21
10
|
|
@@ -3,7 +3,7 @@ module XCCache
|
|
3
3
|
class Package
|
4
4
|
module UmbrellaManifestMixin
|
5
5
|
def write_manifest(no_cache: false)
|
6
|
-
UI.info("Writing Package.swift (package: #{root_dir.basename
|
6
|
+
UI.info("Writing Package.swift (package: #{root_dir.basename})")
|
7
7
|
Template.new("umbrella.Package.swift").render(
|
8
8
|
{
|
9
9
|
:timestamp => Time.new.strftime("%F %T"),
|
@@ -13,15 +13,15 @@ module XCCache
|
|
13
13
|
p.to_s.start_with?(root_dir.to_s) ? p.relative_path_from(root_dir).to_s : p.to_s
|
14
14
|
end
|
15
15
|
|
16
|
-
UI.
|
16
|
+
UI.info("Cachemap visualization: #{html_path}")
|
17
17
|
Template.new("cachemap.html").render(
|
18
18
|
{
|
19
19
|
:root_dir => root_dir.to_s,
|
20
20
|
:root_dir_short => root_dir.basename.to_s,
|
21
21
|
:lockfile_path => config.lockfile.path.to_s,
|
22
22
|
:lockfile_path_short => to_relative.call(config.lockfile.path),
|
23
|
-
:binaries_dir => config.
|
24
|
-
:binaries_dir_short => to_relative.call(config.
|
23
|
+
:binaries_dir => config.spm_binaries_dir.to_s,
|
24
|
+
:binaries_dir_short => to_relative.call(config.spm_binaries_dir),
|
25
25
|
:desc_hit => stats[:hit],
|
26
26
|
:desc_missed => stats[:missed],
|
27
27
|
:desc_ignored => stats[:ignored],
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module XCCache
|
2
|
+
module SPM
|
3
|
+
class Package
|
4
|
+
module UmbrellaXCConfigsMixin
|
5
|
+
def gen_xcconfigs
|
6
|
+
UI.section("Generating xcconfigs") do
|
7
|
+
macros_config_by_targets.each do |target, hash|
|
8
|
+
xcconfig_path = config.spm_xcconfig_dir / "#{target}.xcconfig"
|
9
|
+
UI.message("XCConfig of target #{target} at: #{xcconfig_path}")
|
10
|
+
Xcodeproj::Config.new(hash).save_as(xcconfig_path)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def macros_config_by_targets
|
18
|
+
config.cachemap.manifest_data["macros"].to_h do |target, macros|
|
19
|
+
swift_flags = macros.map do |m|
|
20
|
+
basename = File.basename(m, ".*")
|
21
|
+
binary_path = config.spm_binaries_dir / basename / "#{basename}.macro"
|
22
|
+
"-load-plugin-executable #{binary_path}##{basename}"
|
23
|
+
end
|
24
|
+
hash = { "OTHER_SWIFT_FLAGS" => "$(inherited) #{swift_flags.join(' ')}" }
|
25
|
+
[File.basename(target, ".*"), hash]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -12,9 +12,10 @@ module XCCache
|
|
12
12
|
include Config::Mixin
|
13
13
|
include UmbrellaCachemapMixin
|
14
14
|
include UmbrellaDescsMixin
|
15
|
-
include
|
15
|
+
include UmbrellaBuildMixin
|
16
16
|
include UmbrellaManifestMixin
|
17
17
|
include UmbrellaVizMixin
|
18
|
+
include UmbrellaXCConfigsMixin
|
18
19
|
|
19
20
|
def initialize(options = {})
|
20
21
|
super
|
@@ -25,12 +26,13 @@ module XCCache
|
|
25
26
|
|
26
27
|
def prepare(options = {})
|
27
28
|
create
|
28
|
-
resolve unless options[:
|
29
|
-
|
29
|
+
resolve unless options[:skip_resolving_dependencies]
|
30
|
+
create_symlinks_for_convenience
|
31
|
+
create_symlinks_to_local_pkgs
|
30
32
|
gen_metadata
|
31
33
|
resolve_recursive_dependencies
|
32
34
|
create_symlinks_to_artifacts
|
33
|
-
sync_cachemap
|
35
|
+
sync_cachemap(sdks: options[:sdks])
|
34
36
|
end
|
35
37
|
|
36
38
|
def resolve_recursive_dependencies
|
@@ -51,22 +53,30 @@ module XCCache
|
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
|
-
def
|
56
|
+
def create_symlinks_for_convenience
|
55
57
|
# Symlinks for convenience
|
56
58
|
(root_dir / "binaries").symlink_to(root_dir.parent / "binaries")
|
57
59
|
(root_dir / ".build").symlink_to(root_dir.parent / ".build")
|
58
60
|
(root_dir / ".build/checkouts").symlink_to(root_dir.parent / "checkouts")
|
59
61
|
end
|
60
62
|
|
63
|
+
def create_symlinks_to_local_pkgs
|
64
|
+
pkg_desc.dependencies.select(&:local?).each do |dep|
|
65
|
+
# For metadata generation
|
66
|
+
dep.path.symlink_to(root_dir / ".build/checkouts/#{dep.slug}")
|
67
|
+
# For convenience, synced group under `xccache.config` group in xcodeproj
|
68
|
+
dep.path.symlink_to(Config.instance.spm_local_pkgs_dir / dep.slug)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
61
72
|
def create_symlinks_to_artifacts
|
62
|
-
# Clean up
|
63
|
-
config.
|
64
|
-
p.rmtree if p.symlink?
|
73
|
+
# Clean up symlinks beforehand
|
74
|
+
config.spm_binaries_dir.glob("*/*.{xcframework,macro}").each do |p|
|
75
|
+
p.rmtree if p.symlink?
|
65
76
|
end
|
66
77
|
|
67
|
-
UI.message("Creating symlinks to binary artifacts of targets: #{binary_targets.map(&:full_name).to_s.dark}")
|
68
78
|
binary_targets.each do |target|
|
69
|
-
dst_path = config.
|
79
|
+
dst_path = config.spm_binaries_dir / target.name / "#{target.name}.xcframework"
|
70
80
|
# For local xcframework, just symlink to the path
|
71
81
|
# Zip frameworks (either of local or remote pkgs) are unzipped in the build artifacts
|
72
82
|
target.local_binary_path.symlink_to(dst_path) if target.local_binary_path&.extname == ".xcframework"
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "xccache/core/syntax/plist"
|
2
|
+
|
3
|
+
module XCCache
|
4
|
+
module SPM
|
5
|
+
class XCFramework
|
6
|
+
class Metadata < PlistRepresentable
|
7
|
+
class Library < Hash
|
8
|
+
def id
|
9
|
+
self["LibraryIdentifier"]
|
10
|
+
end
|
11
|
+
|
12
|
+
def platform
|
13
|
+
self["SupportedPlatform"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def archs
|
17
|
+
self["SupportedArchitectures"]
|
18
|
+
end
|
19
|
+
|
20
|
+
def simulator?
|
21
|
+
self["SupportedPlatformVariant"] == "simulator"
|
22
|
+
end
|
23
|
+
|
24
|
+
def triples
|
25
|
+
@triples ||= archs.map do |arch|
|
26
|
+
simulator? ? "#{arch}-#{platform}-simulator" : "#{arch}-#{platform}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def available_libraries
|
32
|
+
@available_libraries ||= raw.fetch("AvailableLibraries", []).map { |h| Library.new.merge(h) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def triples
|
36
|
+
@triples ||= available_libraries.flat_map(&:triples)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,34 +1,11 @@
|
|
1
1
|
require "xccache/utils/template"
|
2
2
|
|
3
3
|
module XCCache
|
4
|
-
|
5
|
-
class
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(options = {})
|
9
|
-
@name = options[:name]
|
10
|
-
@module_name = @name.c99extidentifier
|
11
|
-
@pkg_dir = Pathname(options[:pkg_dir] || ".").expand_path
|
12
|
-
@pkg_desc = options[:pkg_desc]
|
13
|
-
@sdk = options[:sdk]
|
14
|
-
@config = options[:config] || "debug"
|
15
|
-
@path = options[:path]
|
16
|
-
@tmpdir = options[:tmpdir]
|
17
|
-
end
|
18
|
-
|
19
|
-
def build
|
4
|
+
module SPM
|
5
|
+
class FrameworkSlice < Buildable
|
6
|
+
def build(_options = {})
|
20
7
|
UI.section("Building slice: #{name} (#{config}, #{sdk})".bold) do
|
21
|
-
|
22
|
-
cmd << "--package-path" << pkg_dir
|
23
|
-
cmd << "--target" << name
|
24
|
-
cmd << "--sdk" << sdk.sdk_path
|
25
|
-
# Workaround for swiftinterface emission
|
26
|
-
# https://github.com/swiftlang/swift/issues/64669#issuecomment-1535335601
|
27
|
-
cmd << "-Xswiftc" << "-enable-library-evolution"
|
28
|
-
cmd << "-Xswiftc" << "-alias-module-names-in-module-interface"
|
29
|
-
cmd << "-Xswiftc" << "-emit-module-interface"
|
30
|
-
cmd << "-Xswiftc" << "-no-verify-emitted-module-interface"
|
31
|
-
Sh.run(cmd, suppress_err: /(dependency '.*' is not used by any target|unable to create symbolic link)/)
|
8
|
+
swift_build
|
32
9
|
create_framework
|
33
10
|
end
|
34
11
|
end
|
@@ -102,36 +79,59 @@ module XCCache
|
|
102
79
|
end
|
103
80
|
|
104
81
|
def create_headers
|
105
|
-
|
106
|
-
copy_headers if use_clang?
|
82
|
+
copy_headers
|
107
83
|
end
|
108
84
|
|
109
85
|
def create_modules
|
110
|
-
|
111
|
-
return copy_swiftmodules unless use_clang?
|
86
|
+
copy_swiftmodules unless use_clang?
|
112
87
|
|
113
88
|
UI.message("Creating framework modulemap")
|
114
89
|
Template.new("framework.modulemap").render(
|
115
|
-
{ :module_name => module_name },
|
116
|
-
save_to:
|
90
|
+
{ :module_name => module_name, :target => name },
|
91
|
+
save_to: modules_dir / "module.modulemap"
|
117
92
|
)
|
118
93
|
end
|
119
94
|
|
120
95
|
def copy_headers
|
121
96
|
UI.message("Copying headers")
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
97
|
+
swift_header_paths = products_dir.glob("#{module_name}.build/*-Swift.h")
|
98
|
+
paths = swift_header_paths + pkg_target.header_paths
|
99
|
+
paths.each { |p| process_header(p) }
|
100
|
+
|
101
|
+
umbrella_header_content = paths.map { |p| "#include <#{module_name}/#{p.basename}>" }.join("\n")
|
102
|
+
(headers_dir / "#{name}-umbrella.h").write(umbrella_header_content)
|
103
|
+
end
|
104
|
+
|
105
|
+
def process_header(path)
|
106
|
+
handle_angle_bracket_import = proc do |statement, header|
|
107
|
+
next statement if header.include?("/")
|
108
|
+
|
109
|
+
# NOTE: If importing a header with flat angle-bracket style (ex. `#import <foo.h>`)
|
110
|
+
# The header `foo.h` may belong to a dependency's headers.
|
111
|
+
# When packaging into xcframework, `#import <foo.h>` no longer works because `foo.h`
|
112
|
+
# coz it's not visible within the framework's headers
|
113
|
+
# -> We need to explicitly specify the module it belongs to, ex. `#import <foo/foo.h>`
|
114
|
+
targets = [pkg_target] + pkg_target.recursive_targets
|
115
|
+
target = targets.find { |t| t.header_paths.any? { |p| p.basename.to_s == header } }
|
116
|
+
next statement if target.nil?
|
117
|
+
|
118
|
+
corrected_statement = statement.sub("<#{header}>", "<#{target.module_name}/#{header}>")
|
119
|
+
<<~CONTENT
|
120
|
+
// -------------------------------------------------------------------------------------------------
|
121
|
+
// NOTE: This import was corrected by xccache, from flat angle-bracket to nested angle-bracket style
|
122
|
+
// Original: `#{statement}`
|
123
|
+
#{corrected_statement}
|
124
|
+
// -------------------------------------------------------------------------------------------------
|
125
|
+
CONTENT
|
126
|
+
end
|
127
|
+
|
128
|
+
content = path.read.gsub(/^ *#import <(.+)>/) { |m| handle_angle_bracket_import.call(m, $1) }
|
129
|
+
(headers_dir / path.basename).write(content)
|
130
130
|
end
|
131
131
|
|
132
132
|
def copy_swiftmodules
|
133
133
|
UI.message("Copying swiftmodules")
|
134
|
-
swiftmodule_dir = Dir.prepare("#{
|
134
|
+
swiftmodule_dir = Dir.prepare("#{modules_dir}/#{module_name}.swiftmodule")
|
135
135
|
swiftinterfaces = products_dir.glob("#{module_name}.build/#{module_name}.swiftinterface")
|
136
136
|
to_copy = products_dir.glob("Modules/#{module_name}.*") + swiftinterfaces
|
137
137
|
to_copy.each do |p|
|
@@ -160,23 +160,20 @@ module XCCache
|
|
160
160
|
@products_dir ||= pkg_dir / ".build" / sdk.triple / config
|
161
161
|
end
|
162
162
|
|
163
|
-
def swift_build_args
|
164
|
-
[
|
165
|
-
"--configuration", config,
|
166
|
-
"--triple", sdk.triple,
|
167
|
-
]
|
168
|
-
end
|
169
|
-
|
170
163
|
def use_clang?
|
171
164
|
pkg_target.use_clang?
|
172
165
|
end
|
173
166
|
|
174
|
-
def
|
175
|
-
@
|
167
|
+
def resource_bundle_product_path
|
168
|
+
@resource_bundle_product_path ||= products_dir / pkg_target.resource_bundle_name
|
176
169
|
end
|
177
170
|
|
178
|
-
def
|
179
|
-
@
|
171
|
+
def headers_dir
|
172
|
+
@headers_dir ||= Dir.prepare(path / "Headers")
|
173
|
+
end
|
174
|
+
|
175
|
+
def modules_dir
|
176
|
+
@modules_dir ||= Dir.prepare(path / "Modules")
|
180
177
|
end
|
181
178
|
end
|
182
179
|
end
|