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.
- checksums.yaml +4 -4
- data/lib/xccache/assets/templates/cachemap.html.template +55 -0
- data/lib/xccache/assets/templates/cachemap.js.template +95 -0
- data/lib/xccache/assets/templates/cachemap.style.css.template +94 -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 +183 -0
- data/lib/xccache/cache/cachemap.rb +56 -0
- data/lib/xccache/command/base.rb +29 -0
- data/lib/xccache/command/build.rb +41 -0
- data/lib/xccache/command/pkg/build.rb +30 -0
- 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 +14 -0
- data/lib/xccache/command/use.rb +19 -0
- data/lib/xccache/command.rb +27 -1
- data/lib/xccache/core/cacheable.rb +28 -0
- data/lib/xccache/core/config.rb +110 -0
- data/lib/xccache/core/error.rb +7 -0
- data/lib/xccache/core/git.rb +27 -0
- data/lib/xccache/core/hash.rb +21 -0
- data/lib/xccache/core/lockfile.rb +40 -0
- data/lib/xccache/core/log.rb +51 -0
- data/lib/xccache/core/parallel.rb +10 -0
- data/lib/xccache/core/sh.rb +51 -0
- data/lib/xccache/core/syntax/hash.rb +31 -0
- data/lib/xccache/core/syntax/json.rb +16 -0
- data/lib/xccache/core/syntax/plist.rb +17 -0
- data/lib/xccache/core/syntax/yml.rb +16 -0
- data/lib/xccache/core/syntax.rb +1 -0
- data/lib/xccache/core/system.rb +62 -0
- data/lib/xccache/core.rb +1 -0
- data/lib/xccache/installer/build.rb +23 -0
- data/lib/xccache/installer/rollback.rb +37 -0
- data/lib/xccache/installer/use.rb +28 -0
- data/lib/xccache/installer.rb +113 -0
- data/lib/xccache/main.rb +3 -1
- data/lib/xccache/spm/build.rb +53 -0
- data/lib/xccache/spm/desc/base.rb +68 -0
- data/lib/xccache/spm/desc/dep.rb +40 -0
- data/lib/xccache/spm/desc/desc.rb +126 -0
- data/lib/xccache/spm/desc/product.rb +36 -0
- 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 +164 -0
- data/lib/xccache/spm/desc.rb +1 -0
- data/lib/xccache/spm/macro.rb +44 -0
- data/lib/xccache/spm/mixin.rb +12 -0
- data/lib/xccache/spm/pkg/base.rb +107 -0
- data/lib/xccache/spm/pkg/umbrella/build.rb +30 -0
- data/lib/xccache/spm/pkg/umbrella/cachemap.rb +110 -0
- data/lib/xccache/spm/pkg/umbrella/descs.rb +35 -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/xcconfigs.rb +31 -0
- data/lib/xccache/spm/pkg/umbrella.rb +91 -0
- data/lib/xccache/spm/pkg.rb +1 -0
- data/lib/xccache/spm/xcframework/metadata.rb +41 -0
- data/lib/xccache/spm/xcframework/slice.rb +180 -0
- data/lib/xccache/spm/xcframework/xcframework.rb +56 -0
- data/lib/xccache/spm/xcframework.rb +2 -0
- data/lib/xccache/spm.rb +1 -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 +49 -0
- data/lib/xccache/swift/swiftc.rb +16 -0
- data/lib/xccache/utils/template.rb +21 -0
- 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 +73 -0
- data/lib/xccache/xcodeproj/pkg_product_dependency.rb +19 -0
- data/lib/xccache/xcodeproj/project.rb +83 -0
- data/lib/xccache/xcodeproj/target.rb +52 -0
- data/lib/xccache/xcodeproj.rb +2 -0
- metadata +107 -2
@@ -0,0 +1,91 @@
|
|
1
|
+
require "xccache/swift/swiftc"
|
2
|
+
require "xccache/utils/template"
|
3
|
+
require "xccache/cache/cachemap"
|
4
|
+
require "xccache/spm/pkg/base"
|
5
|
+
|
6
|
+
Dir["#{__dir__}/#{File.basename(__FILE__, '.rb')}/*.rb"].sort.each { |f| require f }
|
7
|
+
|
8
|
+
module XCCache
|
9
|
+
module SPM
|
10
|
+
class Package
|
11
|
+
class Umbrella < Package
|
12
|
+
include Config::Mixin
|
13
|
+
include UmbrellaCachemapMixin
|
14
|
+
include UmbrellaDescsMixin
|
15
|
+
include UmbrellaBuildMixin
|
16
|
+
include UmbrellaManifestMixin
|
17
|
+
include UmbrellaVizMixin
|
18
|
+
include UmbrellaXCConfigsMixin
|
19
|
+
|
20
|
+
def initialize(options = {})
|
21
|
+
super
|
22
|
+
@descs = []
|
23
|
+
@descs_by_name = {}
|
24
|
+
@dependency_targets_by_products = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def prepare(options = {})
|
28
|
+
create
|
29
|
+
resolve unless options[:skip_resolving_dependencies]
|
30
|
+
create_symlinks_for_convenience
|
31
|
+
create_symlinks_to_local_pkgs
|
32
|
+
gen_metadata
|
33
|
+
resolve_recursive_dependencies
|
34
|
+
create_symlinks_to_artifacts
|
35
|
+
sync_cachemap(sdks: options[:sdks])
|
36
|
+
end
|
37
|
+
|
38
|
+
def resolve_recursive_dependencies
|
39
|
+
UI.section("Resolving recursive dependencies")
|
40
|
+
@descs.each do |desc|
|
41
|
+
@dependency_targets_by_products.merge!(desc.resolve_recursive_dependencies.transform_keys(&:full_name))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def create
|
46
|
+
UI.info("Creating umbrella package")
|
47
|
+
# Initially, write json with the original data in lockfile (without cache)
|
48
|
+
write_manifest(no_cache: true)
|
49
|
+
# Create dummy sources dirs prefixed with `.` so that they do not show up in Xcode
|
50
|
+
config.project_targets.each do |target|
|
51
|
+
dir = Dir.prepare(root_dir / ".Sources" / "#{target.name}.xccache")
|
52
|
+
(dir / "dummy.swift").write("")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_symlinks_for_convenience
|
57
|
+
# Symlinks for convenience
|
58
|
+
(root_dir / "binaries").symlink_to(root_dir.parent / "binaries")
|
59
|
+
(root_dir / ".build").symlink_to(root_dir.parent / ".build")
|
60
|
+
(root_dir / ".build/checkouts").symlink_to(root_dir.parent / "checkouts")
|
61
|
+
end
|
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
|
+
|
72
|
+
def create_symlinks_to_artifacts
|
73
|
+
# Clean up symlinks beforehand
|
74
|
+
config.spm_binaries_dir.glob("*/*.{xcframework,macro}").each do |p|
|
75
|
+
p.rmtree if p.symlink?
|
76
|
+
end
|
77
|
+
|
78
|
+
binary_targets.each do |target|
|
79
|
+
dst_path = config.spm_binaries_dir / target.name / "#{target.name}.xcframework"
|
80
|
+
# For local xcframework, just symlink to the path
|
81
|
+
# Zip frameworks (either of local or remote pkgs) are unzipped in the build artifacts
|
82
|
+
target.local_binary_path.symlink_to(dst_path) if target.local_binary_path&.extname == ".xcframework"
|
83
|
+
config.spm_artifacts_dir.glob("#{target.full_name}/*.xcframework").each do |p|
|
84
|
+
p.symlink_to(dst_path)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dir["#{__dir__}/#{File.basename(__FILE__, '.rb')}/*.rb"].sort.each { |f| require f }
|
@@ -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
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require "xccache/utils/template"
|
2
|
+
|
3
|
+
module XCCache
|
4
|
+
module SPM
|
5
|
+
class FrameworkSlice < Buildable
|
6
|
+
def build(_options = {})
|
7
|
+
UI.section("Building slice: #{name} (#{config}, #{sdk})".bold) do
|
8
|
+
swift_build
|
9
|
+
create_framework
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def override_resource_bundle_accessor
|
16
|
+
# By default, Swift generates resource_bundle_accessor.swift for targets having resources
|
17
|
+
# (Check .build/<Target>.build/DerivedSources/resource_bundle_accessor.swift)
|
18
|
+
# This enables accessing the resource bundle via `Bundle.module`.
|
19
|
+
# However, `Bundle.module` expects the resource bundle to be under the app bundle,
|
20
|
+
# which is not the case for binary targets. Instead the bundle is under `Frameworks/<Target>.framework`
|
21
|
+
# WORKAROUND:
|
22
|
+
# - Overriding resource_bundle_accessor.swift to add `Frameworks/<Target>.framework` to the search list
|
23
|
+
# - Compiling this file into an `.o` file before using `libtool` to create the framework binary
|
24
|
+
UI.message("Override resource_bundle_accessor")
|
25
|
+
template_name = use_clang? ? "resource_bundle_accessor.m" : "resource_bundle_accessor.swift"
|
26
|
+
source_path = tmpdir / File.basename(template_name)
|
27
|
+
obj_path = products_dir / "#{module_name}.build" / "#{source_path.basename}.o"
|
28
|
+
Template.new(template_name).render(
|
29
|
+
{ :pkg => pkg_target.pkg_name, :target => name, :module_name => module_name },
|
30
|
+
save_to: source_path
|
31
|
+
)
|
32
|
+
|
33
|
+
if use_clang?
|
34
|
+
cmd = ["xcrun", "clang"]
|
35
|
+
cmd << "-x" << "objective-c"
|
36
|
+
cmd << "-target" << sdk.triple << "-isysroot" << sdk.sdk_path
|
37
|
+
cmd << "-o" << obj_path.to_s
|
38
|
+
cmd << "-c" << source_path
|
39
|
+
else
|
40
|
+
cmd = ["xcrun", "swiftc"]
|
41
|
+
cmd << "-emit-library" << "-emit-object"
|
42
|
+
cmd << "-module-name" << module_name
|
43
|
+
cmd << "-target" << sdk.triple << "-sdk" << sdk.sdk_path
|
44
|
+
cmd << "-o" << obj_path.to_s
|
45
|
+
cmd << source_path
|
46
|
+
end
|
47
|
+
Sh.run(cmd)
|
48
|
+
end
|
49
|
+
|
50
|
+
def create_framework
|
51
|
+
override_resource_bundle_accessor if resource_bundle_product_path.exist?
|
52
|
+
create_info_plist
|
53
|
+
create_framework_binary
|
54
|
+
create_headers
|
55
|
+
create_modules
|
56
|
+
copy_resource_bundles if resource_bundle_product_path.exist?
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_framework_binary
|
60
|
+
# Write .o file list into a file
|
61
|
+
obj_paths = products_dir.glob("#{module_name}.build/**/*.o")
|
62
|
+
raise GeneralError, "Detected no object files for #{name}" if obj_paths.empty?
|
63
|
+
|
64
|
+
objlist_path = tmpdir / "objects.txt"
|
65
|
+
objlist_path.write(obj_paths.map(&:to_s).join("\n"))
|
66
|
+
|
67
|
+
cmd = ["libtool", "-static"]
|
68
|
+
cmd << "-o" << "#{path}/#{module_name}"
|
69
|
+
cmd << "-filelist" << objlist_path.to_s
|
70
|
+
Sh.run(cmd)
|
71
|
+
FileUtils.chmod("+x", path / module_name)
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_info_plist
|
75
|
+
Template.new("framework.info.plist").render(
|
76
|
+
{ :module_name => module_name },
|
77
|
+
save_to: path / "Info.plist",
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_headers
|
82
|
+
copy_headers
|
83
|
+
end
|
84
|
+
|
85
|
+
def create_modules
|
86
|
+
copy_swiftmodules unless use_clang?
|
87
|
+
|
88
|
+
UI.message("Creating framework modulemap")
|
89
|
+
Template.new("framework.modulemap").render(
|
90
|
+
{ :module_name => module_name, :target => name },
|
91
|
+
save_to: modules_dir / "module.modulemap"
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
def copy_headers
|
96
|
+
UI.message("Copying headers")
|
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
|
+
end
|
131
|
+
|
132
|
+
def copy_swiftmodules
|
133
|
+
UI.message("Copying swiftmodules")
|
134
|
+
swiftmodule_dir = Dir.prepare("#{modules_dir}/#{module_name}.swiftmodule")
|
135
|
+
swiftinterfaces = products_dir.glob("#{module_name}.build/#{module_name}.swiftinterface")
|
136
|
+
to_copy = products_dir.glob("Modules/#{module_name}.*") + swiftinterfaces
|
137
|
+
to_copy.each do |p|
|
138
|
+
p.copy(to: swiftmodule_dir / p.basename.sub(module_name, sdk.triple))
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def copy_resource_bundles
|
143
|
+
resolve_resource_symlinks
|
144
|
+
UI.message("Copy resource bundle to framework: #{resource_bundle_product_path.basename}")
|
145
|
+
resource_bundle_product_path.copy(to_dir: path)
|
146
|
+
end
|
147
|
+
|
148
|
+
def resolve_resource_symlinks
|
149
|
+
# Well, Xcode seems to well handle symlinks in resources. In xcodebuild log, you would see something like:
|
150
|
+
# CpResource: builtin-copy ... -resolve-src-symlinks
|
151
|
+
# But this is not the case if we build with `swift build`. Here, we have to manually handle it
|
152
|
+
resource_bundle_product_path.glob("**/*").select(&:symlink?).reject(&:exist?).each do |p|
|
153
|
+
UI.message("Resolve resource symlink: #{p}")
|
154
|
+
original = pkg_target.resource_paths.find { |rp| rp.symlink? && rp.readlink == p.readlink }
|
155
|
+
original&.realpath&.copy(to: p)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def products_dir
|
160
|
+
@products_dir ||= pkg_dir / ".build" / sdk.triple / config
|
161
|
+
end
|
162
|
+
|
163
|
+
def use_clang?
|
164
|
+
pkg_target.use_clang?
|
165
|
+
end
|
166
|
+
|
167
|
+
def resource_bundle_product_path
|
168
|
+
@resource_bundle_product_path ||= products_dir / pkg_target.resource_bundle_name
|
169
|
+
end
|
170
|
+
|
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")
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "xccache/spm/build"
|
2
|
+
|
3
|
+
module XCCache
|
4
|
+
module SPM
|
5
|
+
class XCFramework < Buildable
|
6
|
+
attr_reader :slices
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
super
|
10
|
+
@slices ||= @sdks.map do |sdk|
|
11
|
+
FrameworkSlice.new(
|
12
|
+
**options,
|
13
|
+
sdks: [sdk],
|
14
|
+
path: Dir.prepare(tmpdir / sdk.triple / "#{module_name}.framework"),
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def build(merge_slices: false, **_options)
|
20
|
+
tmp_new_path = tmpdir / "new.xcframework"
|
21
|
+
tmp_existing_path = tmpdir / "existing.framework"
|
22
|
+
|
23
|
+
slices.each(&:build)
|
24
|
+
UI.section("Creating #{name}.xcframework from slices") do
|
25
|
+
create_xcframework(from: slices.map(&:path), to: tmp_new_path)
|
26
|
+
end
|
27
|
+
|
28
|
+
path.copy(to: tmp_existing_path) if path.exist? && merge_slices
|
29
|
+
path.rmtree if path.exist?
|
30
|
+
|
31
|
+
if merge_slices && tmp_existing_path.exist?
|
32
|
+
UI.section("Merging #{name}.xcframework with existing slices") do
|
33
|
+
framework_paths =
|
34
|
+
[tmp_new_path, tmp_existing_path]
|
35
|
+
.flat_map { |p| p.glob("*/*.framework") }
|
36
|
+
.uniq { |p| p.parent.basename.to_s } # uniq by id (ex. ios-arm64), preferred new ones
|
37
|
+
create_xcframework(from: framework_paths, to: path)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
path.parent.mkpath
|
41
|
+
tmp_new_path.copy(to: path)
|
42
|
+
end
|
43
|
+
UI.info("-> XCFramework: #{path.to_s.dark}")
|
44
|
+
end
|
45
|
+
|
46
|
+
def create_xcframework(options = {})
|
47
|
+
cmd = ["xcodebuild", "-create-xcframework"]
|
48
|
+
cmd << "-allow-internal-distribution" unless library_evolution?
|
49
|
+
cmd << "-output" << options[:to]
|
50
|
+
options[:from].each { |p| cmd << "-framework" << p }
|
51
|
+
cmd << "> /dev/null" # Only care about errors
|
52
|
+
Sh.run(cmd)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/xccache/spm.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Dir["#{__dir__}/#{File.basename(__FILE__, '.rb')}/*.rb"].sort.each { |f| require f }
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module XCCache
|
2
|
+
class Storage
|
3
|
+
include Config::Mixin
|
4
|
+
|
5
|
+
def initialize(options = {}); end
|
6
|
+
|
7
|
+
def pull
|
8
|
+
print_warnings
|
9
|
+
end
|
10
|
+
|
11
|
+
def push
|
12
|
+
print_warnings
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def print_warnings
|
18
|
+
UI.warn <<~DESC
|
19
|
+
Do nothing as remote cache is not set up yet.
|
20
|
+
|
21
|
+
To set it up, specify `remote` in `xccache.yml`.
|
22
|
+
See: https://github.com/trinhngocthuyen/xccache/blob/main/docs/configuration.md#remote
|
23
|
+
DESC
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
|
3
|
+
module XCCache
|
4
|
+
class GitStorage < Storage
|
5
|
+
attr_reader :branch
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
super
|
9
|
+
if (@remote = options[:remote])
|
10
|
+
@remote = File.expand_path(@remote) unless ["http", "https", "git"].include?(URI.parse(@remote).scheme)
|
11
|
+
ensure_remote
|
12
|
+
end
|
13
|
+
@branch = options[:branch]
|
14
|
+
end
|
15
|
+
|
16
|
+
def pull
|
17
|
+
git.fetch("--depth 1 origin #{branch}")
|
18
|
+
git.switch("--detach FETCH_HEAD", capture: true)
|
19
|
+
git.clean("-dfx", capture: true)
|
20
|
+
# Re-create local branch so that it has the latest from remote
|
21
|
+
git.branch("-D #{branch} || true", capture: true)
|
22
|
+
git.checkout("-b #{branch}", capture: true)
|
23
|
+
end
|
24
|
+
|
25
|
+
def push
|
26
|
+
return UI.info("No changes to push, cache repo is clean".magenta) if git.clean?
|
27
|
+
|
28
|
+
git.add(".")
|
29
|
+
git.commit("-m \"Update cache at #{Time.new}\"")
|
30
|
+
git.push("origin #{branch}")
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def git
|
36
|
+
@git ||= Git.new(config.spm_repo_dir)
|
37
|
+
end
|
38
|
+
|
39
|
+
def ensure_remote
|
40
|
+
existing = git.remote("get-url origin || true", capture: true, log_cmd: false)[0].strip
|
41
|
+
return if @remote == existing
|
42
|
+
return git.remote("add origin #{@remote}") if existing.empty?
|
43
|
+
git.remote("set-url origin #{@remote}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
|
3
|
+
module XCCache
|
4
|
+
class S3Storage < Storage
|
5
|
+
def initialize(options = {})
|
6
|
+
super
|
7
|
+
@uri = options[:uri]
|
8
|
+
@creds_path = Pathname(options[:creds_path] || "~/.xccache/s3.creds.json").expand_path
|
9
|
+
creds = JSONRepresentable.new(@creds_path)
|
10
|
+
@access_key_id = creds["access_key_id"]
|
11
|
+
@secret_access_key = creds["secret_access_key"]
|
12
|
+
end
|
13
|
+
|
14
|
+
def pull
|
15
|
+
s3_sync(src: @uri, dst: config.spm_repo_dir)
|
16
|
+
end
|
17
|
+
|
18
|
+
def push
|
19
|
+
s3_sync(src: config.spm_repo_dir, dst: @uri)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def s3_sync(src: nil, dst: nil)
|
25
|
+
validate!
|
26
|
+
UI.info("Syncing cache from #{src.to_s.bold} to #{dst.to_s.bold}...")
|
27
|
+
env = {
|
28
|
+
"AWS_ACCESS_KEY_ID" => @access_key_id,
|
29
|
+
"AWS_SECRET_ACCESS_KEY" => @secret_access_key,
|
30
|
+
}
|
31
|
+
cmd = ["aws", "s3", "sync"]
|
32
|
+
cmd << "--exact-timestamps" << "--delete"
|
33
|
+
cmd << "--include" << "*.xcframework"
|
34
|
+
cmd << "--include" << "*.macro"
|
35
|
+
cmd << src << dst
|
36
|
+
Sh.run(cmd, env: env)
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate!
|
40
|
+
if File.which("awsss").nil?
|
41
|
+
raise GeneralError, "awscli is not installed. Please install it via brew: `brew install awscli`"
|
42
|
+
end
|
43
|
+
return unless @access_key_id.nil? || @secret_access_key.nil?
|
44
|
+
raise GeneralError, <<~DESC
|
45
|
+
Please ensure the credentials json at #{@creds_path}. Example:
|
46
|
+
{
|
47
|
+
"access_key_id": <ACCESS_KEY_ID>,
|
48
|
+
"secret_access_key": <SECRET_ACCESS_KEY>
|
49
|
+
}
|
50
|
+
DESC
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dir["#{__dir__}/#{File.basename(__FILE__, '.rb')}/*.rb"].sort.each { |f| require f }
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "xccache/core/sh"
|
2
|
+
|
3
|
+
module XCCache
|
4
|
+
module Swift
|
5
|
+
class Sdk
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
NAME_TO_TRIPLE = {
|
9
|
+
:iphonesimulator => "arm64-apple-ios-simulator",
|
10
|
+
:iphoneos => "arm64-apple-ios",
|
11
|
+
:macosx => "arm64-apple-macosx",
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
def initialize(name)
|
15
|
+
@name = name
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
name
|
20
|
+
end
|
21
|
+
|
22
|
+
def triple(without_vendor: false)
|
23
|
+
res = NAME_TO_TRIPLE[name.to_sym]
|
24
|
+
res = res.sub("-apple", "") if without_vendor
|
25
|
+
res
|
26
|
+
end
|
27
|
+
|
28
|
+
def sdk_path
|
29
|
+
# rubocop:disable Layout/LineLength
|
30
|
+
# /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
|
31
|
+
# rubocop:enable Layout/LineLength
|
32
|
+
@sdk_path ||= Pathname(Sh.capture_output("xcrun --sdk #{name} --show-sdk-path")).realpath
|
33
|
+
end
|
34
|
+
|
35
|
+
def sdk_platform_developer_path
|
36
|
+
@sdk_platform_developer_path ||= sdk_path.parent.parent # iPhoneSimulator.platform/Developer
|
37
|
+
end
|
38
|
+
|
39
|
+
def swiftc_args
|
40
|
+
developer_library_frameworks_path = sdk_platform_developer_path / "Library" / "Frameworks"
|
41
|
+
developer_usr_lib_path = sdk_platform_developer_path / "usr" / "lib"
|
42
|
+
[
|
43
|
+
"-F#{developer_library_frameworks_path}",
|
44
|
+
"-I#{developer_usr_lib_path}",
|
45
|
+
]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module XCCache
|
2
|
+
module Swift
|
3
|
+
module Swiftc
|
4
|
+
def self.version
|
5
|
+
@version ||= begin
|
6
|
+
m = /Apple Swift version ([\d\.]+)/.match(Sh.capture_output("xcrun swift -version"))
|
7
|
+
m.nil? ? "6.0" : m[1]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.version_without_patch
|
12
|
+
version.split(".")[...2].join(".")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "erb"
|
2
|
+
require "xccache/core/error"
|
3
|
+
|
4
|
+
module XCCache
|
5
|
+
class Template
|
6
|
+
attr_reader :name, :path
|
7
|
+
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
@path = Gem.find_files("xccache/assets/templates/#{name}.template").first
|
11
|
+
end
|
12
|
+
|
13
|
+
def render(hash = {}, save_to: nil)
|
14
|
+
raise GeneralError, "Template not found: #{name}" if path.nil?
|
15
|
+
|
16
|
+
rendered = ERB.new(File.read(@path)).result_with_hash(hash)
|
17
|
+
Pathname(save_to).write(rendered) unless save_to.nil?
|
18
|
+
rendered
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "xcodeproj"
|
2
|
+
|
3
|
+
module Xcodeproj
|
4
|
+
class Project
|
5
|
+
module Object
|
6
|
+
class XCBuildConfiguration
|
7
|
+
def base_configuration_xcconfig
|
8
|
+
path = base_configuration_xcconfig_path
|
9
|
+
Config.new(path) if path
|
10
|
+
end
|
11
|
+
|
12
|
+
def base_configuration_xcconfig_path
|
13
|
+
return base_configuration_reference.real_path if base_configuration_reference
|
14
|
+
return unless base_configuration_reference_anchor && base_configuration_reference_relative_path
|
15
|
+
project.dir / base_configuration_reference_anchor.path / base_configuration_reference_relative_path
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "xcodeproj"
|
2
|
+
|
3
|
+
module Xcodeproj
|
4
|
+
class Project
|
5
|
+
module Object
|
6
|
+
class PBXFileSystemSynchronizedRootGroup
|
7
|
+
attribute :name, String
|
8
|
+
|
9
|
+
def display_name
|
10
|
+
return name if name
|
11
|
+
return File.basename(path) if path
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "xcodeproj"
|
2
|
+
|
3
|
+
module Xcodeproj
|
4
|
+
class Project
|
5
|
+
module Object
|
6
|
+
class PBXGroup
|
7
|
+
def synced_groups
|
8
|
+
children.grep(PBXFileSystemSynchronizedRootGroup)
|
9
|
+
end
|
10
|
+
|
11
|
+
def ensure_synced_group(name: nil, path: nil)
|
12
|
+
synced_groups.find { |g| g.name == name } || new_synced_group(name: name, path: path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def new_synced_group(name: nil, path: nil)
|
16
|
+
path = path.relative_path_from(project.dir) unless path.relative?
|
17
|
+
synced_group = project.new(PBXFileSystemSynchronizedRootGroup)
|
18
|
+
synced_group.path = path.to_s
|
19
|
+
synced_group.name = name || path.basename.to_s
|
20
|
+
self << synced_group
|
21
|
+
synced_group
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|