crosspack 0.2.1 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b365ad4ba6ab874d40dec1ef153ab18104f329ee6ebb7a638a27267089eb7c43
4
- data.tar.gz: c8b0eef099fecc0f97f7964560e093812fe50e7627bb5a092bfadcbf8322f668
3
+ metadata.gz: 4f1b1bcc611377577384101685763d88198c0b27dc9323a8370fda526b7440d8
4
+ data.tar.gz: b3c121e02cfebbcfe8112bd4597f2009b35aecee53019a0c51aa2bb791f51007
5
5
  SHA512:
6
- metadata.gz: 7c3d441c8d44206115a24ca0668843781b97631fdf3064a933d51709735911153861057bd0efa81b99406d4276b7c92b70993f12905b6e4ac6e9012b39db1f1d
7
- data.tar.gz: d5bc914e071fd1be81b6cd1703b3120ce3d260a2a94188bba34b3b13f8e672f271d24c985642f43005fde6dbefc4dca542f67b74b213631eba071958e7e02a1f
6
+ metadata.gz: 2f69bb73a73517c21764f8e9bd7c31436f2d6a8615b437ea7912c06e4790a353c8952b7d4834c5a883352773fedb00ee73786689a3864a51e95f6835760aa003
7
+ data.tar.gz: df5e860abc414316410211b72fdf22d82f18a95cc211fa37d9316503625ae5fbffee86fa6a66f959a15f3a3a4cc44661d6dac05b002c2c1b6e438923dbc71d41
data/CHANGELOG.md CHANGED
@@ -5,6 +5,75 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.5.0] - 2026-09-13
9
+
10
+ ### Breaking
11
+ - One config file: `build.yaml`, `package.yaml` and `deps.yaml` are merged
12
+ into a single `crosspack.yml` with `build:` / `package:` / `deps:`
13
+ sections and a shared top-level `name:` (plus optional `version:`, the
14
+ build version scheme). The three old files are no longer read — move each
15
+ file's body under its section, hoist `name`/`version` to the top. No
16
+ migration path is provided (0.4.0 was never published beyond local gem
17
+ builds).
18
+ - CLI: `-f/--file` now points at crosspack.yml (default `./crosspack.yml`);
19
+ the `--package` and `--build` flags are gone.
20
+ - Library API: `Crosspack.pack` takes `config:` (a Config or a path to
21
+ crosspack.yml); `Crossbuild.build` accepts a Config, a path or a ready
22
+ BuildManifest; the manifest classes no longer read files —
23
+ `Manifest.load` / `PackageManifest.load` / `BuildManifest.load` are gone,
24
+ `Crosspack::Config.load` is the single loader.
25
+
26
+ ### Added
27
+ - `Crosspack::Config`: top-level validation (name required once, version
28
+ scheme, unknown keys) with the shared name/version injected into the
29
+ build section and the name into the package section; each section keeps
30
+ its own schema and path-pointing error messages.
31
+
32
+ ## [0.4.0] - 2026-09-13
33
+
34
+ ### Breaking
35
+ - The separate `crossbuild` executable is gone: one command, one staged
36
+ pipeline — `crosspack deps <target>` → `crosspack build <target>` →
37
+ `crosspack pack <target>`. The former crossbuild commands moved under
38
+ `crosspack` (validate/matrix/version gained build.yaml coverage; the gem
39
+ now installs only the `crosspack` binary).
40
+ - build.yaml `deps:` schema: host rules are now mappings with per-system
41
+ `verify` and `install` commands instead of a global `check:` plus a
42
+ bare command string. The 0.3.0 schema was never released outside the
43
+ local gem build, so no migration path is provided.
44
+ - `crosspack pack <target>` takes the target as a positional argument
45
+ (the old `--target` flag is gone) and `--version` is now optional: it
46
+ defaults to the version stamped by the build stage.
47
+
48
+ ### Added
49
+ - Stage pipeline with gating: `crosspack build <target>` resolves the
50
+ matrix entry distributing to that target, runs the deps stage first,
51
+ and fans artifacts out into that target's builds/ directory only;
52
+ `crosspack pack <target>` refuses to run before the build stage
53
+ ("Build stage not done for target ... — Run: crosspack build <target>").
54
+ - The build stage stamps `.crosspack-build` (version) into every
55
+ `builds/<target>/` directory; `crosspack pack` reads it as the default
56
+ package version.
57
+ - deps.yaml schema versioning: an optional top-level `version:` (default
58
+ 1). A deps.yaml declaring a newer schema than this crosspack is
59
+ rejected at load time with an upgrade hint; the key is reserved and no
60
+ longer parsed as a canonical dependency name.
61
+ - deps: rules may be verify-only (no install command) — presence is
62
+ checked, and a missing dependency reports which `install:` to add.
63
+
64
+ ## [0.3.0] - 2026-09-13
65
+
66
+ ### Added
67
+ - build.yaml `deps:` section: build dependencies with per-host install
68
+ commands (e.g. `ubuntu: sudo apt-get install -y imagemagick`, `windows:
69
+ winget install ...`, or any project script). Host selectors resolve in
70
+ order: distro id from `/etc/os-release`, `ID_LIKE` tokens, os, `"*"`.
71
+ - `crossbuild deps` command: doctor + install of the deps: dependencies;
72
+ `--check` reports presence only and exits 1 when something is missing.
73
+ - `crossbuild build` now verifies/installs deps: dependencies before the
74
+ matrix entries; `--no-deps` skips the pass.
75
+ - `crossbuild matrix` lists how each deps: entry resolves on this host.
76
+
8
77
  ## [0.2.1] - 2026-09-11
9
78
 
10
79
  ### Fixed
data/README.md CHANGED
@@ -1,53 +1,122 @@
1
1
  # crosspack
2
2
 
3
3
  Cross-platform build **and** pack toolchain for Wails-style desktop apps,
4
- shipped as one gem with two cooperating commands:
4
+ shipped as one gem, one command, one config file and a staged pipeline:
5
5
 
6
- - **`crossbuild`** — runs the build steps of a project from a small
7
- `build.yaml` matrix and fans the produced artifacts out into the
8
- per-target tree `builds/<family>[/<version>]/<arch>/`.
9
- - **`crosspack`** — turns that compiled tree into native packages
10
- (deb / rpm / PKGBUILD / WiX / app bundle) for any target
11
- distro × version × arch, driven by two small manifests.
6
+ ```
7
+ crosspack deps <target> # verify/install the build host's dependencies
8
+ crosspack build <target> # compile the target's matrix entry into builds/
9
+ crosspack pack <target> # package builds/<target> as a native package
10
+ ```
12
11
 
13
- The two commands share the same target vocabulary (`Crosspack::Target`),
14
- so the build tree and the pack tree never drift apart. Build and pack
15
- stay cleanly separated: `crossbuild` never packages, `crosspack` never
16
- compiles — feed the output of one straight into the other:
12
+ Every stage is keyed by the same target vocabulary — `debian-12`,
13
+ `ubuntu-24.04`, `fedora-41`, `arch`, `macos`, `windows-11.0` — and gated by
14
+ the previous one: `build` refuses to start before the deps stage passes, and
15
+ `pack` refuses to run before `build` filled `builds/<target>/`. The build
16
+ stage also stamps the version into the tree, so `pack` normally needs no
17
+ `--version`:
17
18
 
18
19
  ```
19
- crossbuild build # builds/ tree
20
- crosspack pack --target debian-12 --version 2026.08.31-33837
20
+ crosspack deps ubuntu-24.04
21
+ crosspack build ubuntu-24.04
22
+ crosspack pack ubuntu-24.04
21
23
  ```
22
24
 
23
- ## crossbuild: build.yaml
25
+ ## crosspack.yml — one file, three sections
26
+
27
+ The top level carries the shared facts — `name` (required once) and the
28
+ optional `version` scheme — and three sections: `build:` (what and where to
29
+ build), `package:` (what and how to pack) and `deps:` (runtime dependencies
30
+ resolved to concrete packages per distro). The shared name/version feed the
31
+ build section, the name feeds the package section; each section keeps its
32
+ own schema.
24
33
 
25
34
  ```yaml
26
35
  name: myapp
27
36
  version: calver # calver | git-tag | env:VAR | any literal string
28
- output: builds # default builds; mirrors package.yaml `sources`
29
-
30
- matrix:
31
- - build: linux/amd64 # os/arch this entry builds on (arch may be "any")
32
- env: # extra env for the steps of this entry
33
- CGO_ENABLED: "1"
34
- steps: # shell commands, run from the project root
35
- - wails build -platform linux/amd64 -ldflags "-X myapp/internal/app.appVersion={{version}}"
36
- - cargo build --release -p helper --locked
37
- artifacts:
38
- from: build/bin # what got built
39
- include: [myapp, helper, "*.onnx", "*.so*"] # default: ["*"]
40
- to: [ubuntu-22.04, ubuntu-24.04, debian-12, debian-13]
41
- mode: symlink # symlink | copy, default symlink
42
-
43
- - id: windows # defaults to the build platform string
44
- build: windows/amd64
45
- steps:
46
- - wails build -platform windows/amd64 -ldflags "-X ...={{version}}"
47
- artifacts:
48
- from: build/bin
49
- include: [myapp.exe]
50
- to: [windows-11.0]
37
+
38
+ build:
39
+ output: builds # default builds; mirrors package `sources`
40
+
41
+ deps: # build dependencies, per-host verify/install
42
+ imagemagick:
43
+ hosts: # first selector matching this host wins
44
+ ubuntu:
45
+ verify: dpkg -s imagemagick
46
+ install: sudo apt-get install -y imagemagick
47
+ darwin:
48
+ verify: magick -version
49
+ install: brew install imagemagick
50
+ windows:
51
+ verify: where magick
52
+ install: winget install -e --id ImageMagick.ImageMagick
53
+ "*": # any command — package manager or a project script
54
+ install: ./scripts/install-imagemagick.sh
55
+
56
+ matrix:
57
+ - build: linux/amd64 # os/arch this entry builds on (arch may be "any")
58
+ env: # extra env for the steps of this entry
59
+ CGO_ENABLED: "1"
60
+ steps: # shell commands, run from the project root
61
+ - wails build -platform linux/amd64 -ldflags "-X myapp/internal/app.appVersion={{version}}"
62
+ - cargo build --release -p helper --locked
63
+ artifacts:
64
+ from: build/bin # what got built
65
+ include: [myapp, helper, "*.onnx", "*.so*"] # default: ["*"]
66
+ to: [ubuntu-22.04, ubuntu-24.04, debian-12, debian-13]
67
+ mode: symlink # symlink | copy, default symlink
68
+
69
+ - id: windows # defaults to the build platform string
70
+ build: windows/amd64
71
+ steps:
72
+ - wails build -platform windows/amd64 -ldflags "-X ...={{version}}"
73
+ artifacts:
74
+ from: build/bin
75
+ include: [myapp.exe]
76
+ to: [windows-11.0]
77
+
78
+ package:
79
+ maintainer: Your Name <you@example.com>
80
+ summary: MyApp - cross-platform desktop application # optional
81
+ description: |-
82
+ MyApp - cross-platform desktop application.
83
+ Bundles helper binaries and model assets alongside the GUI...
84
+ license: Proprietary # optional, default Proprietary
85
+ section: utils # optional, deb only
86
+
87
+ sources: builds # compiled artifacts tree written by the build
88
+ # stage: builds/<family>/<version>/<arch>/ —
89
+ # pack without a matching build dir fails honestly
90
+ prefix: usr/local # install prefix inside the package
91
+ lib_dir: lib/myapp # optional, default lib/<name>
92
+
93
+ payload: # files from sources -> <prefix>/<lib_dir>/
94
+ - myapp
95
+ - helper
96
+ - model.onnx
97
+
98
+ executables: [myapp, helper] # chmod 755, must be in payload
99
+
100
+ links: # symlinks relative to prefix
101
+ bin/myapp: ../lib/myapp/myapp
102
+
103
+ desktop: # optional, .desktop generated
104
+ name: MyApp
105
+ comment: Cross-platform desktop application
106
+ categories: Utility;Audio
107
+
108
+ icon: build/appicon.png # optional, -> <prefix>/share/pixmaps/<name>.png
109
+
110
+ deps:
111
+ # canonical dependency names resolved to concrete packages per distro
112
+ # family/version; an optional `version:` declares the deps schema version
113
+ webkit2gtk:
114
+ targets:
115
+ debian:
116
+ "12": [libwebkit2gtk-4.1-0, libwebkit2gtk-4.0-37] # deb: a | b
117
+ fedora: { "*": [webkit2gtk4.1] }
118
+ macos: system # OS component
119
+ windows: system # preinstalled (WebView2)
51
120
  ```
52
121
 
53
122
  Placeholders `{{version}}`, `{{name}}`, `{{platform}}`, `{{os}}`, `{{arch}}`
@@ -63,6 +132,20 @@ builds/ubuntu/24.04/amd64/myapp -> build/bin/myapp (symlink)
63
132
  builds/windows/11.0/x86_64/myapp.exe
64
133
  ```
65
134
 
135
+ ### Build dependencies (build.deps)
136
+
137
+ `build.deps` is the host axis to the top-level `deps:` section's target
138
+ axis: the `deps:` section declares what a *package* needs at runtime;
139
+ `build.deps` declares what the *build host* must have. Checks differ per
140
+ system, so every host rule carries its own commands: `verify` (presence
141
+ probe; exit 0 = present; without it the name is looked up in PATH) and
142
+ `install` (how to install it; may be omitted for verify-only rules).
143
+ Selectors are tried in order: distro id from `/etc/os-release` (`ubuntu`),
144
+ then `ID_LIKE` tokens (`debian`), then the os (`linux`, `darwin`,
145
+ `windows`), then `"*"`. The deps stage runs verify → install → re-verify
146
+ for every dependency; a dependency that is still missing fails the stage.
147
+ Commands support the `{{name}}`/`{{os}}`/... placeholders.
148
+
66
149
  ### Version schemes
67
150
 
68
151
  | scheme | value |
@@ -72,81 +155,27 @@ builds/windows/11.0/x86_64/myapp.exe
72
155
  | `env:VAR` | taken from `VAR`; build fails loudly when unset |
73
156
  | other string | used verbatim, e.g. `version: 1.2.3` |
74
157
 
75
- ### crossbuild CLI
76
-
77
- ```
78
- crossbuild validate # build.yaml against the schema
79
- crossbuild matrix # entries × host buildability table
80
- crossbuild version # computed version
81
- crossbuild build [--target linux/amd64] # all host-buildable entries, or one
82
- crossbuild targets # what is in builds/ ready to pack
83
- ```
84
-
85
- `build` refuses invalid manifests with path-pointing errors, runs only the
86
- entries whose `build:` platform matches the host, then distributes.
87
-
88
- ## crosspack: two manifests
158
+ The whole file is validated against embedded schemas with actionable,
159
+ path-pointing error messages; every stage refuses to run on an invalid
160
+ config.
89
161
 
90
- **`package.yaml`** — what and how to pack:
91
-
92
- ```yaml
93
- name: myapp
94
- maintainer: Your Name <you@example.com>
95
- summary: MyApp - cross-platform desktop application # optional
96
- description: |-
97
- MyApp - cross-platform desktop application.
98
- Bundles helper binaries and model assets alongside the GUI...
99
- license: Proprietary # optional, default Proprietary
100
- section: utils # optional, deb only
101
-
102
- sources: builds # compiled artifacts tree written by crossbuild:
103
- # builds/<family>/<version>/<arch>/ — pack without
104
- # a matching build directory fails honestly
105
- prefix: usr/local # install prefix inside the package
106
- lib_dir: lib/myapp # optional, default lib/<name>
107
-
108
- payload: # files from sources -> <prefix>/<lib_dir>/
109
- - myapp
110
- - helper
111
- - model.onnx
112
-
113
- executables: [myapp, helper] # chmod 755, must be in payload
114
-
115
- links: # symlinks relative to prefix
116
- bin/myapp: ../lib/myapp/myapp
117
-
118
- desktop: # optional, .desktop generated
119
- name: MyApp
120
- comment: Cross-platform desktop application
121
- categories: Utility;Audio
122
-
123
- icon: build/appicon.png # optional, -> <prefix>/share/pixmaps/<name>.png
124
- ```
125
-
126
- **`deps.yaml`** — canonical dependency names resolved to concrete packages
127
- per distro family/version (see the file itself for the full schema):
162
+ ## CLI
128
163
 
129
- ```yaml
130
- webkit2gtk:
131
- targets:
132
- debian:
133
- "12": [libwebkit2gtk-4.1-0, libwebkit2gtk-4.0-37] # deb: a | b
134
- fedora: { "*": [webkit2gtk4.1] }
135
- macos: system # OS component
136
- windows: system # preinstalled (WebView2)
137
164
  ```
165
+ # staged pipeline
166
+ crosspack deps <target> [--check] # verify/install build deps (--check: report only)
167
+ crosspack build <target>|--all [--no-deps] [--version X]
168
+ crosspack pack <target> [--version X] # version defaults to the build stamp
138
169
 
139
- Both files are validated against embedded schemas with actionable,
140
- path-pointing error messages; every builder refuses to run on an invalid
141
- manifest.
170
+ # inspection
171
+ crosspack validate # crosspack.yml: top level + all sections
172
+ crosspack resolve <target> # Depends line for the target
173
+ crosspack matrix # deps x target + build matrix tables
174
+ crosspack version # computed build version
175
+ crosspack targets # what is in builds/ ready to pack
142
176
 
143
- ## CLI
144
-
145
- ```
146
- crosspack validate # both package.yaml and deps.yaml
147
- crosspack resolve --target debian-12 # Depends line for the target
148
- crosspack matrix # dependency x target table
149
- crosspack pack --target debian-12 --version 2026.08.31-1234 [--root .]
177
+ # options
178
+ -f, --file PATH # path to crosspack.yml (default: ./crosspack.yml)
150
179
  ```
151
180
 
152
181
  `pack` picks the builder from the target and writes to
@@ -163,27 +192,29 @@ crosspacks/arch/x86_64/PKGBUILD
163
192
  ```ruby
164
193
  require 'crosspack' # pulls in Crossbuild too
165
194
 
166
- Crossbuild.build('build.yaml', root: Dir.pwd) # all host entries
167
- Crossbuild.build('build.yaml', entry_id: 'windows') # one entry explicitly
195
+ Crossbuild.build('crosspack.yml', root: Dir.pwd) # all host entries
196
+ Crossbuild.build('crosspack.yml', target: 'ubuntu-24.04') # the stages' build, library-level
168
197
 
169
198
  Crosspack.pack(
170
- manifest: 'package.yaml', deps: 'deps.yaml',
199
+ config: 'crosspack.yml',
171
200
  target: Crosspack::Target.parse('debian-12', arch: 'amd64'),
172
- version: '2026.08.31-1234',
201
+ version: nil, # nil -> the version stamped by the build stage
173
202
  root: Dir.pwd, output_base: 'crosspacks'
174
203
  )
175
204
  ```
176
205
 
177
- Lower-level pieces are public too: on the build side `Crossbuild::
178
- BuildManifest`, `VersionScheme`, `Runner`, `Distributor`, `Matrix`,
179
- `Builder`, `Platform`; on the pack side `Crosspack::PackageManifest`,
180
- `Manifest`, `Resolver`, `Matrix`, `Target`, `Builders::*`.
206
+ `Crosspack::Config.load('crosspack.yml')` parses and validates the file
207
+ once and exposes the sections as manifests. Lower-level pieces are public
208
+ too: on the build side `Crossbuild::BuildManifest`, `VersionScheme`,
209
+ `Runner`, `Distributor`, `Matrix`, `Builder`, `DepInstaller`, `Platform`;
210
+ on the pack side `Crosspack::PackageManifest`, `Manifest`, `Resolver`,
211
+ `Matrix`, `Target`, `Builds`, `Builders::*`.
181
212
 
182
213
  Deb needs `dpkg-deb` (present on any Debian/Ubuntu), rpm needs
183
214
  `rpmbuild` (`sudo apt install rpm`), PKGBUILD generation needs nothing.
184
215
  The WiX builder generates `.wxs` sources (run the WiX Toolset yourself to
185
- get an MSI), the macOS builder stages the `.app` bundle (signing, notarization
186
- and .dmg creation are out of scope). Requires Ruby >= 3.2.
216
+ get an MSI), the macOS builder stages the `.app` bundle (signing,
217
+ notarization and .dmg creation are out of scope). Requires Ruby >= 3.2.
187
218
 
188
219
  ## Tests
189
220
 
data/exe/crosspack CHANGED
@@ -5,8 +5,10 @@ require 'optparse'
5
5
 
6
6
  begin
7
7
  require 'crosspack'
8
+ require 'crossbuild'
8
9
  rescue LoadError
9
10
  require_relative '../lib/crosspack'
11
+ require_relative '../lib/crossbuild'
10
12
  end
11
13
 
12
14
  def abort_with(message)
@@ -14,21 +16,26 @@ def abort_with(message)
14
16
  exit 1
15
17
  end
16
18
 
17
- options = { file: 'deps.yaml', package: 'package.yaml', arch: 'amd64', format: nil,
18
- version: nil, root: Dir.pwd, output_base: 'crosspacks' }
19
+ options = { file: Crosspack::Config::DEFAULT_PATH, arch: 'amd64',
20
+ format: nil, version: nil, root: Dir.pwd, output_base: nil,
21
+ check_only: false, no_deps: false, all: false }
19
22
  parser = OptionParser.new do |opts|
20
- opts.banner = "crosspack #{Crosspack::VERSION} — native package packer (deb/rpm/PKGBUILD/MSI/app)\n" \
23
+ opts.banner = "crosspack #{Crosspack::VERSION} — staged pipeline: deps → build → pack\n" \
21
24
  "Usage: crosspack <command> [options]\n" \
22
- 'Commands: validate, resolve, matrix, pack, targets'
25
+ "Stages: deps <target> [--check] verify/install build deps for the target's host\n" \
26
+ " build <target>|--all compile the target's matrix entry into builds/\n" \
27
+ " pack <target> [--version X] package builds/<target> (version from the build stamp)\n" \
28
+ 'Commands: validate, resolve, matrix, version, targets'
23
29
 
24
- opts.on('-f', '--file PATH', 'path to deps.yaml (default: ./deps.yaml)') { |v| options[:file] = v }
25
- opts.on('--package PATH', 'path to package.yaml (default: ./package.yaml)') { |v| options[:package] = v }
26
- opts.on('--target TARGET', 'target: debian-12, ubuntu-24.04, fedora-41, arch, macos, windows') { |v| options[:target] = v }
30
+ opts.on('-f', '--file PATH', 'path to crosspack.yml (default: ./crosspack.yml)') { |v| options[:file] = v }
27
31
  opts.on('--arch ARCH', 'architecture: amd64/x86_64, arm64/aarch64 (default: amd64)') { |v| options[:arch] = v }
28
32
  opts.on('--format FORMAT', 'output format for resolve: deb, rpm, pkgbuild (default: derived from the target)') { |v| options[:format] = v.to_sym }
29
- opts.on('--version VERSION', 'package version for pack (required for pack)') { |v| options[:version] = v }
33
+ opts.on('--version VERSION', 'override the version for build/pack') { |v| options[:version] = v }
30
34
  opts.on('--root PATH', 'project root for relative paths (default: .)') { |v| options[:root] = v }
31
- opts.on('--output-base DIR', 'output directory (default: ./crosspacks)') { |v| options[:output_base] = v }
35
+ opts.on('--output-base DIR', 'output directory (build stage: the manifest output, default builds; pack: default ./crosspacks)') { |v| options[:output_base] = v }
36
+ opts.on('--check', 'with `deps`: only report presence, never install (exit 1 if missing)') { options[:check_only] = true }
37
+ opts.on('--no-deps', 'with `build`: skip the deps stage') { options[:no_deps] = true }
38
+ opts.on('--all', 'with `build`: every host-buildable matrix entry (default target selection off)') { options[:all] = true }
32
39
  opts.on('-h', '--help', 'show help') do
33
40
  puts opts
34
41
  exit
@@ -43,77 +50,144 @@ end
43
50
  command = ARGV.shift
44
51
  abort_with 'No command given. See: crosspack --help' if command.nil?
45
52
 
46
- # Positional file path: crosspack validate path/to/deps.yaml
47
- options[:file] = ARGV.shift if ARGV.first && !ARGV.first.start_with?('-')
53
+ # Positional argument: a target for the stage commands (deps/build/pack,
54
+ # resolve), a crosspack.yml path for validate.
55
+ positional = ARGV.shift if ARGV.first && !ARGV.first.start_with?('-')
56
+
57
+ options[:file] = positional if command == 'validate' && positional
48
58
 
49
59
  begin
50
- manifest = Crosspack::Manifest.load(options[:file])
60
+ config = Crosspack::Config.load(File.expand_path(options[:file], options[:root]))
51
61
  rescue Crosspack::InvalidManifestError => e
52
62
  abort_with e.message
53
63
  end
54
64
 
65
+ # validate prints the full report itself and exits by status; everything
66
+ # else stops on a broken top level first.
67
+ if command != 'validate'
68
+ abort_with config.error_report unless config.valid?
69
+ end
70
+
71
+ # The matrix entry that builds `target` — the deps/build stages operate on
72
+ # its host platform.
73
+ def entry_for_target!(manifest, target, host_os, host_arch)
74
+ entry = manifest.entry_for_target(target)
75
+ host = "#{host_os}/#{Crossbuild::Platform.display_arch(host_arch)}"
76
+ unless entry.buildable_on?(host_os, host_arch)
77
+ abort_with "The entry for #{target.inspect} (#{entry.id}) builds on #{entry.platform}, " \
78
+ "this host is #{host} — cross-compilation is not supported"
79
+ end
80
+
81
+ entry
82
+ end
83
+
55
84
  case command
56
85
  when 'validate'
57
- begin
58
- pkg = Crosspack::PackageManifest.load(options[:package])
59
- rescue Crosspack::InvalidManifestError => e
60
- abort_with e.message
61
- end
62
- puts manifest.error_report
86
+ deps_manifest = config.deps_manifest
87
+ pkg = config.package_manifest
88
+ build_manifest = config.build_manifest
89
+ puts config.error_report
90
+ puts deps_manifest.error_report
63
91
  puts pkg.error_report
64
- exit(manifest.valid? && pkg.valid? ? 0 : 1)
92
+ puts build_manifest.error_report
93
+ ok = config.valid? && deps_manifest.valid? && pkg.valid? && build_manifest.valid?
94
+ exit(ok ? 0 : 1)
65
95
  when 'matrix'
66
- abort_with 'Schema is invalid — the matrix cannot be built. Run: crosspack validate' unless manifest.valid?
67
- puts Crosspack::Matrix.new(manifest).render
68
- when 'resolve'
69
- abort_with 'Specify a target: --target debian-12 (see crosspack --help)' unless options[:target]
70
- begin
71
- target = Crosspack::Target.parse(options[:target], arch: options[:arch])
72
- rescue Crosspack::Target::Error => e
73
- abort_with "Invalid target #{options[:target].inspect}: #{e.message}"
96
+ deps_manifest = config.deps_manifest
97
+ abort_with 'the deps: section is invalid — run: crosspack validate' unless deps_manifest.valid?
98
+ puts Crosspack::Matrix.new(deps_manifest).render
99
+ build_manifest = config.build_manifest
100
+ if build_manifest.valid?
101
+ puts "\n#{Crossbuild::Matrix.new(build_manifest).render}"
102
+ else
103
+ warn "\nnote: no valid build: section — build matrix not shown (crosspack validate)"
74
104
  end
105
+ when 'resolve'
106
+ target_str = positional
107
+ abort_with 'Specify a target: crosspack resolve debian-12 (see crosspack --help)' unless target_str
75
108
  begin
109
+ manifest = config.deps_manifest
110
+ target = Crosspack::Target.parse(target_str, arch: options[:arch])
76
111
  resolver = Crosspack::Resolver.new(manifest)
77
112
  format = options[:format] || target.format
78
113
  result = resolver.depends(target, format: format)
114
+ puts "# #{target} (#{format})"
79
115
  if result.is_a?(Array)
80
- puts "# #{target} (#{format})"
81
116
  result.each { |d| puts d }
82
117
  else
83
- puts "# #{target} (#{format})"
84
118
  puts "Depends: #{result}"
85
119
  end
86
- rescue Crosspack::ResolveError => e
87
- abort_with "Failed to resolve dependencies for #{target}:\n #{e.message}"
120
+ rescue Crosspack::Target::Error => e
121
+ abort_with "Invalid target #{target_str.inspect}: #{e.message}"
122
+ rescue Crosspack::InvalidManifestError, Crosspack::ResolveError => e
123
+ abort_with e.message
124
+ end
125
+ when 'version'
126
+ build_manifest = config.build_manifest
127
+ abort_with 'the build: section is invalid — run: crosspack validate' unless build_manifest.valid?
128
+ begin
129
+ puts Crossbuild::VersionScheme.new(build_manifest.version).compute(root: options[:root])
130
+ rescue Crossbuild::VersionScheme::Error => e
131
+ abort_with e.message
132
+ end
133
+ when 'deps'
134
+ abort_with 'Specify a target: crosspack deps debian-12 (see crosspack --help)' unless positional
135
+ build_manifest = config.build_manifest
136
+ abort_with 'the build: section is invalid — run: crosspack validate' unless build_manifest.valid?
137
+ entry_for_target!(build_manifest, positional, Crossbuild::Platform.os, Crossbuild::Platform.arch)
138
+ installer = Crossbuild::DepInstaller.new(build_manifest, root: options[:root])
139
+ begin
140
+ if options[:check_only]
141
+ installer.check_all
142
+ puts "deps on this host:\n#{installer.report}"
143
+ exit(installer.statuses.any? { |s| !s.installed } ? 1 : 0)
144
+ end
145
+ installer.ensure_all
146
+ rescue Crossbuild::Error => e
147
+ abort_with e.message
88
148
  end
149
+ puts "deps ready:\n#{installer.report}"
150
+ when 'build'
151
+ build_manifest = config.build_manifest
152
+ abort_with 'the build: section is invalid — run: crosspack validate' unless build_manifest.valid?
153
+ if options[:all]
154
+ target = nil
155
+ else
156
+ abort_with 'Specify a target: crosspack build ubuntu-24.04 (or --all for every host-buildable entry)' unless positional
157
+ target = positional
158
+ end
159
+ begin
160
+ result = Crossbuild::Builder.new(build_manifest, root: options[:root],
161
+ output_base: options[:output_base], version: options[:version],
162
+ deps: !options[:no_deps]).run(target: target)
163
+ rescue Crossbuild::Error, Crosspack::InvalidManifestError => e
164
+ abort_with e.message
165
+ end
166
+ puts "\n📦 #{result.entries.size} entr#{result.entries.size == 1 ? 'y' : 'ies'} built, version #{result.version}"
167
+ puts 'Pack with: crosspack pack <target>'
89
168
  when 'pack'
90
- abort_with 'Specify a target: --target debian-12 and a version: --version 1.0-1' unless options[:target] && options[:version]
169
+ abort_with 'Specify a target: crosspack pack debian-12' unless positional
91
170
  begin
92
- target = Crosspack::Target.parse(options[:target], arch: options[:arch])
171
+ target = Crosspack::Target.parse(positional, arch: options[:arch])
93
172
  path = Crosspack.pack(
94
- manifest: options[:package],
95
- deps: options[:file],
173
+ config: config,
96
174
  target: target,
97
175
  version: options[:version],
98
176
  root: options[:root],
99
- output_base: options[:output_base]
177
+ output_base: options[:output_base] || 'crosspacks'
100
178
  )
101
179
  rescue Crosspack::Target::Error => e
102
- abort_with "Invalid target #{options[:target].inspect}: #{e.message}"
180
+ abort_with "Invalid target #{positional.inspect}: #{e.message}"
103
181
  rescue Crosspack::InvalidManifestError, Crosspack::ResolveError, Crosspack::BuildError => e
104
182
  abort_with e.message
105
183
  end
106
184
  puts "📦 #{path}"
107
185
  when 'targets'
108
186
  # What can actually be packed: builds tree + resolvable deps per target.
109
- pkg_path = File.expand_path(options[:package], options[:root])
110
- sources = begin
111
- Crosspack::PackageManifest.load(pkg_path).sources
112
- rescue Crosspack::InvalidManifestError
113
- nil
114
- end
187
+ pkg = config.package_manifest
188
+ sources = pkg.valid? ? pkg.sources : nil
115
189
  root = sources ? File.expand_path(sources, options[:root]) : File.join(options[:root], 'builds')
116
190
  puts Crosspack::Builds.report(root)
117
191
  else
118
- abort_with "Unknown command #{command.inspect}. Available: validate, resolve, matrix, pack, targets"
192
+ abort_with "Unknown command #{command.inspect}. Available: deps, build, pack, validate, resolve, matrix, version, targets"
119
193
  end