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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/lib/xccache/assets/templates/cachemap.html.template +1 -0
  3. data/lib/xccache/assets/templates/cachemap.js.template +35 -34
  4. data/lib/xccache/assets/templates/cachemap.style.css.template +3 -1
  5. data/lib/xccache/assets/templates/framework.modulemap.template +1 -1
  6. data/lib/xccache/assets/templates/resource_bundle_accessor.m.template +3 -3
  7. data/lib/xccache/assets/templates/umbrella.Package.swift.template +43 -7
  8. data/lib/xccache/cache/cachemap.rb +11 -15
  9. data/lib/xccache/command/base.rb +29 -0
  10. data/lib/xccache/command/build.rb +12 -6
  11. data/lib/xccache/command/pkg/build.rb +2 -6
  12. data/lib/xccache/command/pkg.rb +1 -0
  13. data/lib/xccache/command/remote/pull.rb +15 -0
  14. data/lib/xccache/command/remote/push.rb +15 -0
  15. data/lib/xccache/command/remote.rb +39 -0
  16. data/lib/xccache/command/rollback.rb +2 -1
  17. data/lib/xccache/command/use.rb +3 -2
  18. data/lib/xccache/command.rb +18 -2
  19. data/lib/xccache/core/config.rb +30 -2
  20. data/lib/xccache/core/git.rb +11 -3
  21. data/lib/xccache/core/log.rb +11 -1
  22. data/lib/xccache/core/parallel.rb +10 -0
  23. data/lib/xccache/core/sh.rb +4 -3
  24. data/lib/xccache/core/syntax/plist.rb +17 -0
  25. data/lib/xccache/core/system.rb +8 -0
  26. data/lib/xccache/installer/build.rb +2 -6
  27. data/lib/xccache/installer/rollback.rb +1 -0
  28. data/lib/xccache/installer/use.rb +3 -2
  29. data/lib/xccache/installer.rb +50 -4
  30. data/lib/xccache/spm/build.rb +53 -0
  31. data/lib/xccache/spm/desc/base.rb +10 -3
  32. data/lib/xccache/spm/desc/desc.rb +31 -15
  33. data/lib/xccache/spm/desc/target/binary.rb +8 -0
  34. data/lib/xccache/spm/desc/target/macro.rb +8 -0
  35. data/lib/xccache/spm/desc/target.rb +28 -2
  36. data/lib/xccache/spm/macro.rb +44 -0
  37. data/lib/xccache/spm/mixin.rb +4 -1
  38. data/lib/xccache/spm/pkg/base.rb +48 -44
  39. data/lib/xccache/spm/pkg/umbrella/build.rb +2 -2
  40. data/lib/xccache/spm/pkg/umbrella/cachemap.rb +44 -27
  41. data/lib/xccache/spm/pkg/umbrella/descs.rb +2 -13
  42. data/lib/xccache/spm/pkg/umbrella/manifest.rb +1 -1
  43. data/lib/xccache/spm/pkg/umbrella/viz.rb +3 -3
  44. data/lib/xccache/spm/pkg/umbrella/xcconfigs.rb +31 -0
  45. data/lib/xccache/spm/pkg/umbrella.rb +20 -10
  46. data/lib/xccache/spm/xcframework/metadata.rb +41 -0
  47. data/lib/xccache/{framework → spm/xcframework}/slice.rb +50 -53
  48. data/lib/xccache/spm/xcframework/xcframework.rb +56 -0
  49. data/lib/xccache/spm/xcframework.rb +2 -0
  50. data/lib/xccache/storage/base.rb +26 -0
  51. data/lib/xccache/storage/git.rb +46 -0
  52. data/lib/xccache/storage/s3.rb +53 -0
  53. data/lib/xccache/storage.rb +1 -0
  54. data/lib/xccache/swift/sdk.rb +21 -2
  55. data/lib/xccache/xcodeproj/build_configuration.rb +20 -0
  56. data/lib/xccache/xcodeproj/config.rb +9 -0
  57. data/lib/xccache/xcodeproj/file_system_synchronized_root_group.rb +17 -0
  58. data/lib/xccache/xcodeproj/group.rb +26 -0
  59. data/lib/xccache/xcodeproj/pkg.rb +5 -1
  60. data/lib/xccache/xcodeproj/project.rb +21 -1
  61. data/lib/xccache/xcodeproj/target.rb +5 -3
  62. metadata +40 -5
  63. data/lib/xccache/framework/xcframework.rb +0 -51
@@ -9,11 +9,21 @@ module XCCache
9
9
  include Config::Mixin
10
10
  attr_accessor :indent
11
11
 
12
- def section(title)
12
+ def section(title, timing: false)
13
+ start = Time.new if timing
13
14
  UI.puts(title)
14
15
  self.indent += 2
15
16
  res = yield if block_given?
16
17
  self.indent -= 2
18
+ if timing
19
+ duration = (Time.new - start).to_i
20
+ duration = if duration < 60 then "#{duration}s"
21
+ elsif duration < 60 * 60 then "#{duration / 60}m"
22
+ else
23
+ "#{duration / 3600}h"
24
+ end
25
+ UI.puts("-> Finished: #{title.dark} (#{duration})")
26
+ end
17
27
  res
18
28
  end
19
29
 
@@ -0,0 +1,10 @@
1
+ require "parallel"
2
+
3
+ class Array
4
+ def parallel_map(options = {})
5
+ # By default, use in_threads (IO-bound tasks)
6
+ default = {}
7
+ default[:in_threads] = Parallel.processor_count unless options.key?(:in_processes)
8
+ Parallel.map(self, { **default, **options }) { |x| yield x if block_given? }
9
+ end
10
+ end
@@ -15,8 +15,8 @@ module XCCache
15
15
  run(cmd, capture: true, log_cmd: false)[0].strip
16
16
  end
17
17
 
18
- def run(cmd, options = {})
19
- cmd = cmd.join(" ") if cmd.is_a?(Array)
18
+ def run(*args, env: nil, **options)
19
+ cmd = args.join(" ")
20
20
  UI.message("$ #{cmd}".cyan.dark) if config.verbose? && options[:log_cmd] != false
21
21
 
22
22
  out, err = [], []
@@ -35,7 +35,8 @@ module XCCache
35
35
  end
36
36
  end
37
37
 
38
- Open3.popen3(cmd) do |_stdin, stdout, stderr, wait_thr|
38
+ popen3_args = env ? [env, cmd] : [cmd]
39
+ Open3.popen3(*popen3_args) do |_stdin, stdout, stderr, wait_thr|
39
40
  stdout_thread = Thread.new { stdout.each { |l| handle_out.call(l) } }
40
41
  stderr_thread = Thread.new { stderr.each { |l| handle_err.call(l) } }
41
42
  [stdout_thread, stderr_thread].each(&:join)
@@ -0,0 +1,17 @@
1
+ require "cfpropertylist"
2
+ require_relative "hash"
3
+
4
+ module XCCache
5
+ class PlistRepresentable < HashRepresentable
6
+ def load
7
+ plist = CFPropertyList::List.new(file: path)
8
+ CFPropertyList.native_types(plist.value)
9
+ rescue StandardError
10
+ {}
11
+ end
12
+
13
+ def save(to: nil)
14
+ raise NotImplementedError
15
+ end
16
+ end
17
+ end
@@ -1,4 +1,5 @@
1
1
  require "digest"
2
+ require "mkmf"
2
3
 
3
4
  class String
4
5
  def c99extidentifier
@@ -6,6 +7,12 @@ class String
6
7
  end
7
8
  end
8
9
 
10
+ class File
11
+ def self.which(bin)
12
+ find_executable0(bin)
13
+ end
14
+ end
15
+
9
16
  class Dir
10
17
  def self.prepare(dir)
11
18
  dir = Pathname(dir)
@@ -36,6 +43,7 @@ class Pathname
36
43
  def copy(to: nil, to_dir: nil)
37
44
  dst = to || (Pathname(to_dir) / basename)
38
45
  dst.rmtree if dst.exist? || dst.symlink?
46
+ dst.parent.mkpath
39
47
  FileUtils.copy_entry(self, dst)
40
48
  dst
41
49
  end
@@ -6,19 +6,15 @@ module XCCache
6
6
  def initialize(options = {})
7
7
  super
8
8
  @targets = options[:targets]
9
- @sdk = options[:sdk]
10
- @recursive = options[:recursive]
11
9
  end
12
10
 
13
11
  def install!
14
12
  perform_install do
15
13
  umbrella_pkg.build(
16
14
  targets: @targets,
17
- sdk: @sdk,
18
- out_dir: config.spm_binaries_frameworks_dir,
15
+ out_dir: config.spm_repo_dir,
19
16
  checksum: true,
20
- recursive: @recursive,
21
- skip_resolve: @skip_resolving_dependencies,
17
+ **@build_options,
22
18
  )
23
19
  end
24
20
  end
@@ -30,6 +30,7 @@ module XCCache
30
30
 
31
31
  # Remove .binary product from the project
32
32
  project.targets.each(&:remove_xccache_product_dependencies)
33
+ project.xccache_pkg&.remove_from_project
33
34
  end
34
35
  end
35
36
  end
@@ -4,8 +4,8 @@ module XCCache
4
4
  class Installer
5
5
  class Use < Installer
6
6
  def install!
7
- perform_install do
8
- update_projects do |project|
7
+ update_projects do |project|
8
+ perform_install do
9
9
  UI.section("Using cache for project #{project.display_name}".bold.green) do
10
10
  replace_binaries_for_project(project)
11
11
  end
@@ -21,6 +21,7 @@ module XCCache
21
21
  target.add_xccache_product_dependency unless target.has_xccache_product_dependency?
22
22
  target.remove_pkg_product_dependencies { |d| !d.pkg.xccache_pkg? }
23
23
  end
24
+ project.remove_pkgs(&:non_xccache_pkg?) unless config.keep_pkgs_in_project?
24
25
  end
25
26
  end
26
27
  end
@@ -6,16 +6,28 @@ module XCCache
6
6
  include PkgMixin
7
7
 
8
8
  def initialize(options = {})
9
+ ctx = options[:ctx]
10
+ raise GeneralError, "Missing context (Command) for #{self.class}" if ctx.nil?
9
11
  @umbrella_pkg = options[:umbrella_pkg]
10
- @skip_resolving_dependencies = options[:skip_resolving_dependencies]
12
+ @install_options = ctx.install_options
13
+ @build_options = ctx.build_options
11
14
  end
12
15
 
13
16
  def perform_install
17
+ config.in_installation = true
14
18
  verify_projects!
15
- sync_lockfile if @umbrella_pkg.nil?
16
- umbrella_pkg.prepare(skip_resolve: @skip_resolving_dependencies) if @umbrella_pkg.nil?
19
+ if @umbrella_pkg.nil?
20
+ sync_lockfile
21
+ umbrella_pkg.prepare(**@install_options)
22
+ end
23
+
17
24
  yield
18
25
  umbrella_pkg.write_manifest
26
+ umbrella_pkg.gen_xcconfigs
27
+ projects.each do |project|
28
+ add_xccache_refs_to_project(project)
29
+ inject_xcconfig_to_project(project)
30
+ end
19
31
  umbrella_pkg.gen_cachemap_viz
20
32
  end
21
33
 
@@ -51,7 +63,7 @@ module XCCache
51
63
 
52
64
  def lockfile_hash_for_project(project)
53
65
  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}" }
66
+ deps = target.non_xccache_pkg_product_dependencies.select(&:pkg).map { |d| "#{d.pkg.slug}/#{d.product_name}" }
55
67
  [target.name, deps]
56
68
  end
57
69
  {
@@ -63,5 +75,39 @@ module XCCache
63
75
  def verify_projects!
64
76
  raise "No projects detected. Are you running on the correct project directory?" if projects.empty?
65
77
  end
78
+
79
+ def add_xccache_refs_to_project(project)
80
+ group = project.xccache_config_group
81
+ add_file = proc { |p| group[p.basename.to_s] || group.new_file(p) }
82
+ add_file.call(config.spm_umbrella_sandbox / "Package.swift")
83
+ add_file.call(config.lockfile.path)
84
+ add_file.call(config.path) if config.path.exist?
85
+ group.ensure_synced_group(name: "local-packages", path: config.spm_local_pkgs_dir)
86
+ end
87
+
88
+ def inject_xcconfig_to_project(project)
89
+ group = project.xccache_config_group.ensure_synced_group(name: "xcconfigs", path: config.spm_xcconfig_dir)
90
+ project.targets.each do |target|
91
+ xcconfig_path = config.spm_xcconfig_dir / "#{target.name}.xcconfig"
92
+ target.build_configurations.each do |build_config|
93
+ if (existing = build_config.base_configuration_xcconfig)
94
+ next if existing.path == xcconfig_path
95
+
96
+ relative_path = xcconfig_path.relative_path_from(existing.path.parent)
97
+ next if existing.includes.include?(relative_path.to_s)
98
+
99
+ UI.info("Injecting base configuration for #{target} (#{build_config}) (at: #{existing.path})")
100
+ existing.path.write <<~DESC
101
+ #include "#{relative_path}" // Injected by xccache, for prebuilt macros support
102
+ #{existing.path.read.strip}
103
+ DESC
104
+ else
105
+ UI.info("Setting base configuration #{target} (#{build_config}) as #{xcconfig_path}")
106
+ build_config.base_configuration_reference_anchor = group
107
+ build_config.base_configuration_reference_relative_path = xcconfig_path.basename.to_s
108
+ end
109
+ end
110
+ end
111
+ end
66
112
  end
67
113
  end
@@ -0,0 +1,53 @@
1
+ module XCCache
2
+ module SPM
3
+ class Buildable
4
+ attr_reader :name, :module_name, :pkg_dir, :pkg_desc, :sdk, :sdks, :config, :path, :tmpdir, :library_evolution
5
+ alias library_evolution? library_evolution
6
+
7
+ def initialize(options = {})
8
+ @name = options[:name]
9
+ @module_name = @name.c99extidentifier
10
+ @pkg_dir = Pathname(options[:pkg_dir] || ".").expand_path
11
+ @pkg_desc = options[:pkg_desc]
12
+ @sdks = options[:sdks] || []
13
+ @sdk = options[:sdk] || @sdks&.first
14
+ @config = options[:config] || "debug"
15
+ @path = options[:path]
16
+ @tmpdir = options[:tmpdir]
17
+ @library_evolution = options[:library_evolution]
18
+ end
19
+
20
+ def build(options = {})
21
+ raise NotImplementedError
22
+ end
23
+
24
+ def swift_build(target: nil)
25
+ cmd = ["swift", "build"] + swift_build_args
26
+ cmd << "--package-path" << pkg_dir
27
+ cmd << "--target" << (target || name)
28
+ cmd << "--sdk" << sdk.sdk_path
29
+ sdk.swiftc_args.each { |arg| cmd << "-Xswiftc" << arg }
30
+ if library_evolution?
31
+ # Workaround for swiftinterface emission
32
+ # https://github.com/swiftlang/swift/issues/64669#issuecomment-1535335601
33
+ cmd << "-Xswiftc" << "-enable-library-evolution"
34
+ cmd << "-Xswiftc" << "-alias-module-names-in-module-interface"
35
+ cmd << "-Xswiftc" << "-emit-module-interface"
36
+ cmd << "-Xswiftc" << "-no-verify-emitted-module-interface"
37
+ end
38
+ Sh.run(cmd, suppress_err: /(dependency '.*' is not used by any target|unable to create symbolic link)/)
39
+ end
40
+
41
+ def swift_build_args
42
+ [
43
+ "--configuration", config,
44
+ "--triple", sdk.triple,
45
+ ]
46
+ end
47
+
48
+ def pkg_target
49
+ @pkg_target ||= pkg_desc.get_target(name)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -6,7 +6,8 @@ module XCCache
6
6
  class BaseObject < JSONRepresentable
7
7
  include Config::Mixin
8
8
 
9
- attr_accessor :root, :retrieve_pkg_desc
9
+ ATTRS = %i[root retrieve_pkg_desc].freeze
10
+ attr_accessor(*ATTRS)
10
11
 
11
12
  def name
12
13
  raw["name"]
@@ -28,12 +29,18 @@ module XCCache
28
29
  "<#{self.class} name=#{display_name}>"
29
30
  end
30
31
 
32
+ def cast_to(cls)
33
+ o = cls.new(path, raw: raw)
34
+ ATTRS.each { |sym| o.send("#{sym}=", send(sym.to_s)) }
35
+ o
36
+ end
37
+
31
38
  def pkg_name
32
39
  @pkg_name ||= root.name
33
40
  end
34
41
 
35
42
  def pkg_slug
36
- @pkg_slug ||= root.path.basename(".json").to_s
43
+ @pkg_slug ||= src_dir.basename.to_s
37
44
  end
38
45
 
39
46
  def fetch(key, dtype)
@@ -51,7 +58,7 @@ module XCCache
51
58
 
52
59
  def src_dir
53
60
  @src_dir ||= begin
54
- path = raw.fetch("packageKind", {}).fetch("root", [])[0]
61
+ path = root.raw.fetch("packageKind", {}).fetch("root", [])[0]
55
62
  Pathname.new(path) unless path.nil?
56
63
  end
57
64
  end
@@ -7,18 +7,34 @@ module XCCache
7
7
  include Cacheable
8
8
  cacheable :resolve_recursive_dependencies
9
9
 
10
- def self.in_dir(dir, save_to_dir: nil, checksum: true)
10
+ def self.descs_in_metadata_dir
11
+ descs = Config.instance.spm_metadata_dir.glob("*.json").map { |p| Description.new(p) }
12
+ [descs, combine_descs(descs)]
13
+ end
14
+
15
+ def self.in_dir(dir, save_to_dir: nil)
11
16
  path = save_to_dir / "#{dir.basename}.json" unless save_to_dir.nil?
12
17
  begin
13
18
  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
19
+ Description.new(path, raw: raw)
17
20
  rescue StandardError => e
18
21
  UI.error("Failed to dump package in #{dir}. Error: #{e}")
19
22
  end
20
23
  end
21
24
 
25
+ def self.descs_in_dir(root_dir, save_to_dir: nil)
26
+ dirs = [root_dir] + root_dir.glob(".build/checkouts/*").reject { |p| p.glob("Package*.swift").empty? }
27
+ descs = dirs.parallel_map do |dir|
28
+ desc = Description.in_dir(dir, save_to_dir: save_to_dir)
29
+ unless save_to_dir.nil?
30
+ desc.save
31
+ desc.save(to: desc.path.parent / "#{desc.name}.json") if desc.name != dir.basename.to_s
32
+ end
33
+ desc
34
+ end
35
+ [descs, combine_descs(descs)]
36
+ end
37
+
22
38
  def root
23
39
  self
24
40
  end
@@ -27,10 +43,6 @@ module XCCache
27
43
  raw["_metadata"] ||= {}
28
44
  end
29
45
 
30
- def checksum
31
- metadata["checksum"]
32
- end
33
-
34
46
  def dependencies
35
47
  @dependencies ||= fetch("dependencies", Dependency)
36
48
  end
@@ -44,7 +56,7 @@ module XCCache
44
56
  end
45
57
 
46
58
  def targets
47
- @targets ||= fetch("targets", Target)
59
+ @targets ||= fetch("targets", Target).map(&:downcast)
48
60
  end
49
61
 
50
62
  def binary_targets
@@ -74,10 +86,6 @@ module XCCache
74
86
  !src_dir.to_s.start_with?((config.spm_build_dir / "checkouts").to_s)
75
87
  end
76
88
 
77
- def calc_checksum
78
- metadata["checksum"] = git.nil? ? src_dir.checksum : git.sha
79
- end
80
-
81
89
  def traverse
82
90
  nodes, edges, parents = [], [], {}
83
91
  to_visit = targets.dup
@@ -89,6 +97,10 @@ module XCCache
89
97
  visited << cur
90
98
  nodes << cur
91
99
  yield cur if block_given?
100
+
101
+ # For macro impl, we don't need their dependencies, just the tool binary
102
+ # So, no need to care about swift-syntax dependencies
103
+ next if cur.macro?
92
104
  cur.direct_dependency_targets.each do |t|
93
105
  to_visit << t
94
106
  edges << [cur, t]
@@ -99,11 +111,15 @@ module XCCache
99
111
  [nodes, edges, parents]
100
112
  end
101
113
 
102
- private
103
-
104
114
  def git
105
115
  @git ||= Git.new(src_dir) if Dir.git?(src_dir)
106
116
  end
117
+
118
+ def self.combine_descs(descs)
119
+ descs_by_name = descs.flat_map { |d| [[d.name, d], [d.pkg_slug, d]] }.to_h
120
+ descs.each { |d| d.retrieve_pkg_desc = proc { |name| descs_by_name[name] } }
121
+ descs_by_name
122
+ end
107
123
  end
108
124
  end
109
125
  end
@@ -0,0 +1,8 @@
1
+ module XCCache
2
+ module SPM
3
+ class Package
4
+ class BinaryTarget < Target
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module XCCache
2
+ module SPM
3
+ class Package
4
+ class MacroTarget < Target
5
+ end
6
+ end
7
+ end
8
+ end
@@ -7,15 +7,33 @@ module XCCache
7
7
  include Cacheable
8
8
  cacheable :recursive_targets, :direct_dependency_targets, :direct_dependencies
9
9
 
10
+ Dir["#{__dir__}/#{File.basename(__FILE__, '.rb')}/*.rb"].sort.each { |f| require f }
11
+
10
12
  def xccache?
11
13
  name.end_with?(".xccache")
12
14
  end
13
15
 
16
+ def xccache_id
17
+ macro? ? "#{full_name}.macro" : full_name
18
+ end
19
+
14
20
  def type
15
21
  @type ||= raw["type"].to_sym
16
22
  end
17
23
 
18
- def bundle_name
24
+ def downcast
25
+ cls = {
26
+ :binary => BinaryTarget,
27
+ :macro => MacroTarget,
28
+ }[type]
29
+ cls.nil? ? self : cast_to(cls)
30
+ end
31
+
32
+ def module_name
33
+ name.c99extidentifier
34
+ end
35
+
36
+ def resource_bundle_name
19
37
  "#{pkg_name}_#{name}.bundle"
20
38
  end
21
39
 
@@ -78,7 +96,7 @@ module XCCache
78
96
 
79
97
  def recursive_targets(platform: nil)
80
98
  children = direct_dependency_targets(platform: platform)
81
- children += children.flat_map { |t| t.recursive_targets(platform: platform) }
99
+ children += children.flat_map { |t| t.macro? ? [t] : t.recursive_targets(platform: platform) }
82
100
  children.uniq
83
101
  end
84
102
 
@@ -102,6 +120,10 @@ module XCCache
102
120
  true # FIXME: Handle this
103
121
  end
104
122
 
123
+ def macro?
124
+ type == :macro
125
+ end
126
+
105
127
  def binary?
106
128
  type == :binary
107
129
  end
@@ -114,6 +136,10 @@ module XCCache
114
136
  binary_path if binary? && root.local?
115
137
  end
116
138
 
139
+ def checksum
140
+ @checksum ||= root.git&.sha || sources_path.checksum
141
+ end
142
+
117
143
  private
118
144
 
119
145
  def find_deps(name, pkg_name, dep_type)
@@ -0,0 +1,44 @@
1
+ require_relative "build"
2
+
3
+ module XCCache
4
+ module SPM
5
+ class Macro < Buildable
6
+ attr_reader :macosx_sdk
7
+
8
+ def initialize(options = {})
9
+ super
10
+ @library_evolution = false # swift-syntax is not compatible with library evolution
11
+ @macosx_sdk = Swift::Sdk.new(:macosx)
12
+ end
13
+
14
+ def build(_options = {})
15
+ # NOTE: Building macro binary is tricky...
16
+ # --------------------------------------------------------------------------------
17
+ # Consider this manifest config: .target(Macro) -> .macro(MacroImpl)
18
+ # where `.target(Macro)` contains the interfaces
19
+ # and `.target(MacroImpl)` contains the implementation
20
+ # --------------------------------------------------------------------------------
21
+ # Building `.macro(MacroImpl)` does not produce the tool binary (MacroImpl-tool)... Only `.o` files.
22
+ # Yet, linking those files are exhaustive due to many dependencies in swift-syntax
23
+ # Luckily, building `.target(Macro)` does produce the tool binary.
24
+ # -> WORKAROUND: Find the associated regular target and build it, then collect the tool binary
25
+ # ---------------------------------------------------------------------------------
26
+ associated_target = pkg_desc.targets.find { |t| t.direct_dependency_targets.include?(pkg_target) }
27
+ UI.message(
28
+ "#{name.yellow.dark} is a macro target. " \
29
+ "Will build the associated target #{associated_target.name.dark} to get the tool binary."
30
+ )
31
+ swift_build(target: associated_target.name)
32
+ binary_path = products_dir / "#{module_name}-tool"
33
+ raise GeneralError, "Tool binary not exist at: #{binary_path}" unless binary_path.exist?
34
+ binary_path.copy(to: path)
35
+ FileUtils.chmod("+x", path)
36
+ UI.info("-> Macro binary: #{path.to_s.dark}")
37
+ end
38
+
39
+ def products_dir
40
+ @products_dir ||= pkg_dir / ".build" / macosx_sdk.triple / config
41
+ end
42
+ end
43
+ end
44
+ end
@@ -3,7 +3,10 @@ module XCCache
3
3
  include Config::Mixin
4
4
 
5
5
  def umbrella_pkg
6
- @umbrella_pkg ||= SPM::Package::Umbrella.new(root_dir: config.spm_umbrella_sandbox)
6
+ @umbrella_pkg ||= SPM::Package::Umbrella.new(
7
+ root_dir: config.spm_umbrella_sandbox,
8
+ warn_if_not_direct_target: false,
9
+ )
7
10
  end
8
11
  end
9
12
  end