crosspack 0.2.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 +7 -0
- data/CHANGELOG.md +36 -0
- data/LICENSE +18 -0
- data/README.md +192 -0
- data/exe/crossbuild +76 -0
- data/exe/crosspack +119 -0
- data/lib/crossbuild/build_manifest.rb +374 -0
- data/lib/crossbuild/builder.rb +72 -0
- data/lib/crossbuild/distributor.rb +76 -0
- data/lib/crossbuild/matrix.rb +52 -0
- data/lib/crossbuild/platform.rb +40 -0
- data/lib/crossbuild/runner.rb +62 -0
- data/lib/crossbuild/version.rb +5 -0
- data/lib/crossbuild/version_scheme.rb +51 -0
- data/lib/crossbuild.rb +27 -0
- data/lib/crosspack/builders/app_dir.rb +88 -0
- data/lib/crosspack/builders/deb.rb +121 -0
- data/lib/crosspack/builders/pkgbuild.rb +64 -0
- data/lib/crosspack/builders/rpm.rb +149 -0
- data/lib/crosspack/builders/wix.rb +119 -0
- data/lib/crosspack/builds.rb +61 -0
- data/lib/crosspack/manifest.rb +237 -0
- data/lib/crosspack/matrix.rb +67 -0
- data/lib/crosspack/package_manifest.rb +219 -0
- data/lib/crosspack/packer.rb +223 -0
- data/lib/crosspack/resolver.rb +91 -0
- data/lib/crosspack/target.rb +93 -0
- data/lib/crosspack/version.rb +5 -0
- data/lib/crosspack.rb +38 -0
- metadata +78 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
|
|
6
|
+
module Crosspack
|
|
7
|
+
# Single entry point: takes a compiled artifacts directory (per
|
|
8
|
+
# package.yaml), resolves dependencies for the target (per deps.yaml) and
|
|
9
|
+
# produces a native package under output_base/<family>/<version>/<arch>/.
|
|
10
|
+
# The builder is chosen by the target format — deb, rpm or PKGBUILD.
|
|
11
|
+
class Packer
|
|
12
|
+
def self.pack(manifest:, deps:, target:, version:, root: Dir.pwd,
|
|
13
|
+
output_base: 'crosspacks')
|
|
14
|
+
new(manifest: manifest, deps: deps, target: target, version: version,
|
|
15
|
+
root: root, output_base: output_base).pack
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize(manifest:, deps:, target:, version:, root:, output_base:)
|
|
19
|
+
@pkg_path = manifest
|
|
20
|
+
@deps_path = deps
|
|
21
|
+
@target = target
|
|
22
|
+
@version = version.to_s
|
|
23
|
+
@root = root
|
|
24
|
+
@output_base = output_base
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def pack
|
|
28
|
+
@pkg = PackageManifest.load(@pkg_path)
|
|
29
|
+
@deps = Manifest.load(@deps_path)
|
|
30
|
+
validate_both!
|
|
31
|
+
@pkg_data = build_layout
|
|
32
|
+
|
|
33
|
+
case @target.format
|
|
34
|
+
when :deb
|
|
35
|
+
pack_deb
|
|
36
|
+
when :rpm
|
|
37
|
+
pack_rpm
|
|
38
|
+
when :pkgbuild
|
|
39
|
+
pack_pkgbuild
|
|
40
|
+
when :winget
|
|
41
|
+
pack_windows
|
|
42
|
+
when :cask
|
|
43
|
+
pack_macos
|
|
44
|
+
else
|
|
45
|
+
raise BuildError,
|
|
46
|
+
"packing is not defined for target #{@target} (format #{@target.format})"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def validate_both!
|
|
53
|
+
errors = []
|
|
54
|
+
errors << @pkg.error_report unless @pkg.valid?
|
|
55
|
+
errors << @deps.error_report unless @deps.valid?
|
|
56
|
+
return if errors.empty?
|
|
57
|
+
|
|
58
|
+
raise InvalidManifestError, errors.join("\n")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Layout shared by every format. The artifacts tree mirrors the output
|
|
62
|
+
# tree exactly — builds/<family>[/<version>]/<arch>/ — so the source
|
|
63
|
+
# directory for a target is computed the same way as its output dir.
|
|
64
|
+
# A Linux binary can never sneak into a Windows package: if the target's
|
|
65
|
+
# build directory is absent, packing fails.
|
|
66
|
+
def build_layout
|
|
67
|
+
src_dir = File.expand_path(@target.output_dir(@pkg.sources, @target.format), @root)
|
|
68
|
+
unless File.directory?(src_dir)
|
|
69
|
+
available = Builds.available(File.expand_path(@pkg.sources, @root))
|
|
70
|
+
hint = available.empty? ? 'no built targets at all.' :
|
|
71
|
+
"available builds: #{available.map { |t| "#{t} (#{t.output_dir(@pkg.sources, t.format)})" }.join(', ')}."
|
|
72
|
+
raise BuildError,
|
|
73
|
+
"No compiled artifacts for target #{@target}: expected them in #{src_dir}.\n" \
|
|
74
|
+
"The builds tree (#{@pkg.sources}) mirrors the output tree: builds/<family>/<version>/<arch>/. " \
|
|
75
|
+
"Currently #{hint}\n" \
|
|
76
|
+
'Build the application for this target first, then pack.'
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
lib_dst = File.join(@pkg.prefix, @pkg.lib_dir)
|
|
80
|
+
files = @pkg.payload.to_h { |n| [File.join(src_dir, n), File.join(lib_dst, n)] }
|
|
81
|
+
|
|
82
|
+
if @pkg.icon
|
|
83
|
+
icon_src = File.expand_path(@pkg.icon, @root)
|
|
84
|
+
raise BuildError, "icon: file not found #{icon_src}" unless File.file?(icon_src)
|
|
85
|
+
|
|
86
|
+
files[icon_src] = File.join(@pkg.prefix, 'share', 'pixmaps', "#{@pkg.name}.png")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
if @pkg.desktop
|
|
90
|
+
files[desktop_entry_path] = File.join(@pkg.prefix, 'share', 'applications', "#{@pkg.name}.desktop")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
symlinks = @pkg.links.to_h { |link, target| [File.join(@pkg.prefix, link), target] }
|
|
94
|
+
executables = @pkg.executables.map { |exe| File.join(lib_dst, exe) }
|
|
95
|
+
|
|
96
|
+
{ files: files, symlinks: symlinks, executables: executables,
|
|
97
|
+
src_dir: src_dir, lib_dst: lib_dst }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Writes the .desktop file next to other staging artifacts and returns
|
|
101
|
+
# its path (rooted under build/ so repeated runs reuse one location).
|
|
102
|
+
def desktop_entry_path
|
|
103
|
+
dir = File.join(@root, 'build', 'pkg')
|
|
104
|
+
FileUtils.mkdir_p(dir)
|
|
105
|
+
path = File.join(dir, "#{@pkg.name}.desktop")
|
|
106
|
+
d = @pkg.desktop
|
|
107
|
+
File.write(path, <<~DESKTOP)
|
|
108
|
+
[Desktop Entry]
|
|
109
|
+
Type=Application
|
|
110
|
+
Name=#{d['name'] || @pkg.name}
|
|
111
|
+
Comment=#{d['comment'] || @pkg.summary}
|
|
112
|
+
Exec=#{d['exec'] || @pkg.name}
|
|
113
|
+
Icon=#{@pkg.name}
|
|
114
|
+
Terminal=false
|
|
115
|
+
Categories=#{d['categories'] || 'Utility;'}
|
|
116
|
+
DESKTOP
|
|
117
|
+
path
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def resolved_depends(format)
|
|
121
|
+
Resolver.new(@deps).depends(@target, format: format)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def pack_deb
|
|
125
|
+
output = File.join(@target.output_dir(@output_base, :deb),
|
|
126
|
+
"#{@pkg.name}_#{@version}_#{@target.package_arch(:deb)}.deb")
|
|
127
|
+
Builders::Deb.build(
|
|
128
|
+
name: @pkg.name,
|
|
129
|
+
version: @version,
|
|
130
|
+
architecture: @target.package_arch(:deb),
|
|
131
|
+
maintainer: @pkg.maintainer,
|
|
132
|
+
description: @pkg.description,
|
|
133
|
+
depends: resolved_depends(:deb),
|
|
134
|
+
files: @pkg_data[:files],
|
|
135
|
+
symlinks: @pkg_data[:symlinks],
|
|
136
|
+
executables: @pkg_data[:executables],
|
|
137
|
+
section: @pkg.section,
|
|
138
|
+
output: output
|
|
139
|
+
)
|
|
140
|
+
output
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def pack_rpm
|
|
144
|
+
version, release = split_version
|
|
145
|
+
output = File.join(@target.output_dir(@output_base, :rpm),
|
|
146
|
+
"#{@pkg.name}-#{version}-#{release}.#{@target.package_arch(:rpm)}.rpm")
|
|
147
|
+
Builders::Rpm.build(
|
|
148
|
+
name: @pkg.name,
|
|
149
|
+
version: version,
|
|
150
|
+
release: release,
|
|
151
|
+
summary: @pkg.summary,
|
|
152
|
+
license: @pkg.license,
|
|
153
|
+
requires: resolved_depends(:rpm),
|
|
154
|
+
files: @pkg_data[:files],
|
|
155
|
+
symlinks: @pkg_data[:symlinks],
|
|
156
|
+
executables: @pkg_data[:executables],
|
|
157
|
+
description: @pkg.summary,
|
|
158
|
+
arch: @target.package_arch(:rpm),
|
|
159
|
+
output: output
|
|
160
|
+
)
|
|
161
|
+
output
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def pack_pkgbuild
|
|
165
|
+
version, release = split_version
|
|
166
|
+
output = File.join(@target.output_dir(@output_base, :pkgbuild), 'PKGBUILD')
|
|
167
|
+
Builders::Pkgbuild.generate(
|
|
168
|
+
name: @pkg.name,
|
|
169
|
+
version: version,
|
|
170
|
+
release: release,
|
|
171
|
+
pkgdesc: @pkg.summary,
|
|
172
|
+
maintainer: @pkg.maintainer,
|
|
173
|
+
depends: resolved_depends(:pkgbuild),
|
|
174
|
+
arch: [@target.package_arch(:pkgbuild)],
|
|
175
|
+
# PKGBUILD sources are archive-relative names, not local paths.
|
|
176
|
+
files: @pkg.payload.to_h { |n| [n, File.join(@pkg_data[:lib_dst], n)] },
|
|
177
|
+
symlinks: @pkg_data[:symlinks],
|
|
178
|
+
executables: @pkg_data[:executables],
|
|
179
|
+
source: ["#{@pkg.name}-#{version}.tar.gz"],
|
|
180
|
+
license: @pkg.license,
|
|
181
|
+
output: output
|
|
182
|
+
)
|
|
183
|
+
output
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def split_version
|
|
187
|
+
parts = @version.split('-', 2)
|
|
188
|
+
parts << '1' if parts.size == 1
|
|
189
|
+
parts
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Windows: flat payload under Program Files/<name>/ via WiX. Unix symlinks
|
|
193
|
+
# do not apply; everything from `sources` goes in as regular files.
|
|
194
|
+
def pack_windows
|
|
195
|
+
output = File.join(@target.output_dir(@output_base, :winget),
|
|
196
|
+
"#{@pkg.name}-#{Builders::Wix.msi_version(@version)}.msi")
|
|
197
|
+
files = @pkg.payload.to_h { |n| [File.join(@pkg_data[:src_dir], n), n] }
|
|
198
|
+
Builders::Wix.build(
|
|
199
|
+
name: @pkg.name,
|
|
200
|
+
version: @version,
|
|
201
|
+
manufacturer: @pkg.maintainer,
|
|
202
|
+
summary: @pkg.summary,
|
|
203
|
+
files: files,
|
|
204
|
+
arch: @target.package_arch(:winget),
|
|
205
|
+
output: output
|
|
206
|
+
)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# macOS: .app bundle staging; DMG itself requires a Mac (hdiutil).
|
|
210
|
+
def pack_macos
|
|
211
|
+
output_dir = @target.output_dir(@output_base, :cask)
|
|
212
|
+
files = @pkg.payload.to_h { |n| [File.join(@pkg_data[:src_dir], n), n] }
|
|
213
|
+
Builders::AppDir.build(
|
|
214
|
+
name: @pkg.name,
|
|
215
|
+
version: @version,
|
|
216
|
+
summary: @pkg.summary,
|
|
217
|
+
files: files,
|
|
218
|
+
executables: @pkg.executables,
|
|
219
|
+
output: output_dir
|
|
220
|
+
)
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Crosspack
|
|
4
|
+
# Result of resolving one canonical dependency for one target.
|
|
5
|
+
# verdict: :packages — concrete names in `names` (ordered alternatives);
|
|
6
|
+
# :system / :none / :bundled — nothing to install.
|
|
7
|
+
class Resolution
|
|
8
|
+
attr_reader :canonical, :verdict, :names
|
|
9
|
+
|
|
10
|
+
def initialize(canonical, verdict, names = [])
|
|
11
|
+
@canonical = canonical
|
|
12
|
+
@verdict = verdict
|
|
13
|
+
@names = names
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def packages?
|
|
17
|
+
@verdict == :packages
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_s
|
|
21
|
+
packages? ? @names.join(' | ') : @verdict.to_s
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class ResolveError < StandardError; end
|
|
26
|
+
|
|
27
|
+
# Turns (manifest, target) into concrete package dependencies. No network,
|
|
28
|
+
# no search: everything comes from deps.yaml.
|
|
29
|
+
class Resolver
|
|
30
|
+
def initialize(manifest)
|
|
31
|
+
@manifest = manifest
|
|
32
|
+
@manifest.validate!
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Returns [Resolution] for every canonical name in the manifest.
|
|
36
|
+
def resolve(target)
|
|
37
|
+
family = target.family.to_s
|
|
38
|
+
@manifest.deps.map do |name, body|
|
|
39
|
+
rules = body['targets']
|
|
40
|
+
rule = rules[family]
|
|
41
|
+
if rule.nil?
|
|
42
|
+
available = rules.keys.map(&:to_s).join(', ')
|
|
43
|
+
raise ResolveError,
|
|
44
|
+
"#{name}: deps.yaml has no rules for family #{family} (target #{target}). " \
|
|
45
|
+
"Add the section #{name}: targets: #{family}: ... — currently described: #{available}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
value = resolve_rule(name, rule, target, family)
|
|
49
|
+
if value.is_a?(Array)
|
|
50
|
+
Resolution.new(name, :packages, value)
|
|
51
|
+
else
|
|
52
|
+
Resolution.new(name, value.to_sym)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Dependencies rendered for a package format:
|
|
58
|
+
# :deb -> "pkg-a | pkg-b, pkg-c" (Depends: line)
|
|
59
|
+
# :rpm -> ["pkg-a", "pkg-c"] (first alternative wins)
|
|
60
|
+
# :pkgbuild -> same as rpm
|
|
61
|
+
def depends(target, format: target.format)
|
|
62
|
+
packages = resolve(target).select(&:packages?)
|
|
63
|
+
case format
|
|
64
|
+
when :deb
|
|
65
|
+
packages.map { |r| r.names.join(' | ') }.join(', ')
|
|
66
|
+
when :rpm, :pkgbuild, :cask, :winget
|
|
67
|
+
packages.map { |r| r.names.first }
|
|
68
|
+
else
|
|
69
|
+
raise ArgumentError, "unknown format #{format.inspect} (allowed: :deb, :rpm, :pkgbuild)"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def resolve_rule(name, rule, target, family)
|
|
76
|
+
return rule if rule.is_a?(String) # whole-family verdict
|
|
77
|
+
|
|
78
|
+
key = target.version || '*'
|
|
79
|
+
return rule[key] if rule.key?(key)
|
|
80
|
+
|
|
81
|
+
fallback = rule['*']
|
|
82
|
+
return fallback unless fallback.nil?
|
|
83
|
+
|
|
84
|
+
versions = rule.keys.map { |v| v == '*' ? '"*" (any)' : v.inspect }.join(', ')
|
|
85
|
+
raise ResolveError,
|
|
86
|
+
"#{name}: no rules for #{family} #{target.version.inspect} and no \"*\" key. " \
|
|
87
|
+
"Versions described in the file: #{versions}. Add targets.#{family}.\"#{target.version}\" " \
|
|
88
|
+
'or a "*" wildcard rule'
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Crosspack
|
|
4
|
+
# A build target: distro family + optional version + CPU architecture.
|
|
5
|
+
# Parsed from strings like "debian-12", "ubuntu-24.04", "fedora-41", "arch".
|
|
6
|
+
class Target
|
|
7
|
+
FAMILIES = %i[debian ubuntu fedora almalinux rocky opensuse arch macos windows].freeze
|
|
8
|
+
VERSIONLESS_FAMILIES = %i[arch].freeze
|
|
9
|
+
# Version is allowed but not required (macos-15, windows-11.0, or bare).
|
|
10
|
+
OPTIONAL_VERSION_FAMILIES = %i[macos windows].freeze
|
|
11
|
+
FORMAT_BY_FAMILY = {
|
|
12
|
+
debian: :deb, ubuntu: :deb,
|
|
13
|
+
fedora: :rpm, almalinux: :rpm, rocky: :rpm, opensuse: :rpm,
|
|
14
|
+
arch: :pkgbuild, macos: :cask, windows: :winget
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
class Error < StandardError; end
|
|
18
|
+
|
|
19
|
+
attr_reader :family, :version, :arch
|
|
20
|
+
|
|
21
|
+
# Normalizes common architecture spellings: amd64/x64 -> x86_64,
|
|
22
|
+
# aarch64 -> arm64.
|
|
23
|
+
ARCH_ALIASES = {
|
|
24
|
+
amd64: :x86_64, 'x64': :x86_64, x86_64: :x86_64, x86: nil,
|
|
25
|
+
arm64: :arm64, aarch64: :arm64, armhf: nil
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
28
|
+
def self.normalize_arch(value)
|
|
29
|
+
norm = value.to_s.strip.downcase.sub(/\Ax86-64\z/, 'x86_64').to_sym
|
|
30
|
+
ARCH_ALIASES.fetch(norm, norm)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.parse(str, arch: :x86_64)
|
|
34
|
+
s = str.to_s.strip
|
|
35
|
+
raise Error, 'target string is empty' if s.empty?
|
|
36
|
+
|
|
37
|
+
family, version = s.split('-', 2)
|
|
38
|
+
family_sym = family.to_sym
|
|
39
|
+
unless FAMILIES.include?(family_sym)
|
|
40
|
+
raise Error, "unknown distro family #{family.inspect}; allowed: #{FAMILIES.join(', ')}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
norm_arch = normalize_arch(arch)
|
|
44
|
+
raise Error, "unsupported architecture #{arch.inspect} (supported: x86_64/amd64, arm64/aarch64)" if norm_arch.nil?
|
|
45
|
+
|
|
46
|
+
if VERSIONLESS_FAMILIES.include?(family_sym) && version
|
|
47
|
+
raise Error, "family #{family} does not use a version (remove \"-#{version}\")"
|
|
48
|
+
end
|
|
49
|
+
version_required = !VERSIONLESS_FAMILIES.include?(family_sym) &&
|
|
50
|
+
!OPTIONAL_VERSION_FAMILIES.include?(family_sym)
|
|
51
|
+
if version_required && version.to_s.strip.empty?
|
|
52
|
+
raise Error, "specify a version for family #{family}, e.g. #{family}-12"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
new(family_sym, version, norm_arch)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def initialize(family, version = nil, arch = :x86_64)
|
|
59
|
+
@family = family.to_sym
|
|
60
|
+
@version = version && version.to_s
|
|
61
|
+
@arch = arch
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def format
|
|
65
|
+
FORMAT_BY_FAMILY.fetch(@family)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Per-format architecture spelling: deb uses amd64/arm64, rpm and
|
|
69
|
+
# PKGBUILD use x86_64/aarch64.
|
|
70
|
+
def package_arch(format = self.format)
|
|
71
|
+
return 'amd64' if format == :deb && @arch == :x86_64
|
|
72
|
+
return 'arm64' if format == :deb && @arch == :arm64
|
|
73
|
+
return 'aarch64' if @arch == :arm64
|
|
74
|
+
|
|
75
|
+
'x86_64'
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def to_s
|
|
79
|
+
@version ? "#{@family}-#{@version}" : @family.to_s
|
|
80
|
+
end
|
|
81
|
+
alias inspect to_s
|
|
82
|
+
|
|
83
|
+
# Output layout for build artifacts: base/<family>[/<version>]/<arch>,
|
|
84
|
+
# e.g. crosspacks/ubuntu/24.04/amd64, crosspacks/windows/11.0/x86_64.
|
|
85
|
+
# Families without a version (arch, or bare macos/windows) skip the segment.
|
|
86
|
+
def output_dir(base, format = self.format)
|
|
87
|
+
parts = [base.to_s, @family.to_s]
|
|
88
|
+
parts << @version if @version
|
|
89
|
+
parts << package_arch(format)
|
|
90
|
+
File.join(*parts)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
data/lib/crosspack.rb
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'crosspack/version'
|
|
4
|
+
require_relative 'crosspack/target'
|
|
5
|
+
require_relative 'crosspack/manifest'
|
|
6
|
+
require_relative 'crosspack/package_manifest'
|
|
7
|
+
require_relative 'crosspack/resolver'
|
|
8
|
+
require_relative 'crosspack/matrix'
|
|
9
|
+
require_relative 'crosspack/builds'
|
|
10
|
+
require_relative 'crosspack/packer'
|
|
11
|
+
require_relative 'crosspack/builders/deb'
|
|
12
|
+
require_relative 'crosspack/builders/rpm'
|
|
13
|
+
require_relative 'crosspack/builders/pkgbuild'
|
|
14
|
+
require_relative 'crosspack/builders/wix'
|
|
15
|
+
require_relative 'crosspack/builders/app_dir'
|
|
16
|
+
|
|
17
|
+
module Crosspack
|
|
18
|
+
BUILDERS = {
|
|
19
|
+
deb: Builders::Deb,
|
|
20
|
+
rpm: Builders::Rpm,
|
|
21
|
+
pkgbuild: Builders::Pkgbuild,
|
|
22
|
+
winget: Builders::Wix,
|
|
23
|
+
cask: Builders::AppDir
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
# Shared options for all builders, expressed once so every format stages
|
|
27
|
+
# the same file tree:
|
|
28
|
+
# files: { source_path => destination_inside_package (no leading /) }
|
|
29
|
+
# symlinks: { link_path_inside_package => link_target }
|
|
30
|
+
# executables: [destination paths to chmod 0755]
|
|
31
|
+
class BuildError < StandardError; end
|
|
32
|
+
|
|
33
|
+
# Convenience wrapper: Crosspack.pack(manifest: 'package.yaml',
|
|
34
|
+
# deps: 'deps.yaml', target: Target.parse('debian-12'), version: '1.0-1')
|
|
35
|
+
def self.pack(**kwargs)
|
|
36
|
+
Packer.pack(**kwargs)
|
|
37
|
+
end
|
|
38
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: crosspack
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Oleg Orlov
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-09-08 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: 'Two cooperating commands: crossbuild runs the build steps of a project
|
|
14
|
+
from a build.yaml matrix and fans the produced artifacts out into the per-target
|
|
15
|
+
builds/ tree; crosspack turns that tree into native packages, driven by a cross-distro
|
|
16
|
+
deps.yaml. Both share the same target vocabulary so the trees never drift apart.'
|
|
17
|
+
email:
|
|
18
|
+
- orelcokolov@gmail.com
|
|
19
|
+
executables:
|
|
20
|
+
- crosspack
|
|
21
|
+
- crossbuild
|
|
22
|
+
extensions: []
|
|
23
|
+
extra_rdoc_files: []
|
|
24
|
+
files:
|
|
25
|
+
- CHANGELOG.md
|
|
26
|
+
- LICENSE
|
|
27
|
+
- README.md
|
|
28
|
+
- exe/crossbuild
|
|
29
|
+
- exe/crosspack
|
|
30
|
+
- lib/crossbuild.rb
|
|
31
|
+
- lib/crossbuild/build_manifest.rb
|
|
32
|
+
- lib/crossbuild/builder.rb
|
|
33
|
+
- lib/crossbuild/distributor.rb
|
|
34
|
+
- lib/crossbuild/matrix.rb
|
|
35
|
+
- lib/crossbuild/platform.rb
|
|
36
|
+
- lib/crossbuild/runner.rb
|
|
37
|
+
- lib/crossbuild/version.rb
|
|
38
|
+
- lib/crossbuild/version_scheme.rb
|
|
39
|
+
- lib/crosspack.rb
|
|
40
|
+
- lib/crosspack/builders/app_dir.rb
|
|
41
|
+
- lib/crosspack/builders/deb.rb
|
|
42
|
+
- lib/crosspack/builders/pkgbuild.rb
|
|
43
|
+
- lib/crosspack/builders/rpm.rb
|
|
44
|
+
- lib/crosspack/builders/wix.rb
|
|
45
|
+
- lib/crosspack/builds.rb
|
|
46
|
+
- lib/crosspack/manifest.rb
|
|
47
|
+
- lib/crosspack/matrix.rb
|
|
48
|
+
- lib/crosspack/package_manifest.rb
|
|
49
|
+
- lib/crosspack/packer.rb
|
|
50
|
+
- lib/crosspack/resolver.rb
|
|
51
|
+
- lib/crosspack/target.rb
|
|
52
|
+
- lib/crosspack/version.rb
|
|
53
|
+
homepage: https://github.com/OrelSokolov/crosspack
|
|
54
|
+
licenses:
|
|
55
|
+
- Nonstandard
|
|
56
|
+
metadata:
|
|
57
|
+
source_code_uri: https://github.com/OrelSokolov/crosspack
|
|
58
|
+
changelog_uri: https://github.com/OrelSokolov/crosspack/blob/main/CHANGELOG.md
|
|
59
|
+
post_install_message:
|
|
60
|
+
rdoc_options: []
|
|
61
|
+
require_paths:
|
|
62
|
+
- lib
|
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 3.2.0
|
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '0'
|
|
73
|
+
requirements: []
|
|
74
|
+
rubygems_version: 3.5.22
|
|
75
|
+
signing_key:
|
|
76
|
+
specification_version: 4
|
|
77
|
+
summary: Cross-platform build orchestrator and native package builder (deb/rpm/PKGBUILD/winget/cask)
|
|
78
|
+
test_files: []
|