Release pipeline after PhotoCraft's: AppImage/deb/rpm/Flatpak/tar.gz for Linux x86_64 and aarch64, FreeBSD, a macOS dmg and CLI zip, Windows x64/x86/arm64 MSI and portable zip (plus the plain x64 exe), web zip, SHA256SUMS.txt; version 0.1.2
Adapted from storytold/photocraft's release.yml and packaging/ (Apache-2.0; ATTRIBUTION.md and NOTICE say so). Packages install the licence files, THIRD-PARTY-LICENSES.txt included. The app icon is drawn by `solvecraft-cli icon` (a blue block with a bore, rendered on a transparent background; .ico too), so no image is committed. Notes: a short header plus GitHub's generated What's Changed. A dry run (Run workflow, publish off) keeps the files as the release-assets artifact. packaging-lint.yml checks the workflows, scripts and metadata. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
90fde44c32
commit
7e0618d64b
29 files changed
+1593
-267
No files matched your search
@@ -0,0 +1,65 @@
|
||||
# shellcheck shell=bash
|
||||
# Shared setup for the packaging scripts. Source it: `. "$(dirname "$0")/../env.sh"`.
|
||||
# Adapted from PhotoCraft's packaging (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
|
||||
#
|
||||
# Exports:
|
||||
# ROOT workspace root
|
||||
# VERSION [workspace.package] version from Cargo.toml (override: SOLVECRAFT_VERSION)
|
||||
# DIST output directory for release artifacts (default: $ROOT/dist/release)
|
||||
# SOLVECRAFT_BUILD_DATE UTC build date, YYYY-MM-DD
|
||||
# CARGO_TARGET_DIR cargo's target dir (default: $ROOT/target)
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
export ROOT
|
||||
|
||||
# The version lives in one place: `[workspace.package] version` in the root Cargo.toml.
|
||||
workspace_version() {
|
||||
awk '
|
||||
/^\[/ { in_pkg = ($0 == "[workspace.package]") ; next }
|
||||
in_pkg && $1 == "version" { gsub(/[" ]/, "", $3); print $3; exit }
|
||||
' "$ROOT/Cargo.toml"
|
||||
}
|
||||
|
||||
VERSION="${SOLVECRAFT_VERSION:-$(workspace_version)}"
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "error: could not read [workspace.package] version from $ROOT/Cargo.toml" >&2
|
||||
exit 1
|
||||
fi
|
||||
export VERSION
|
||||
|
||||
DIST="${DIST:-$ROOT/dist/release}"
|
||||
mkdir -p "$DIST"
|
||||
DIST="$(cd "$DIST" && pwd)"
|
||||
export DIST
|
||||
|
||||
export SOLVECRAFT_BUILD_DATE="${SOLVECRAFT_BUILD_DATE:-$(date -u +%Y-%m-%d)}"
|
||||
export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$ROOT/target}"
|
||||
APP_ID=ai.storyteller.solvecraft
|
||||
export APP_ID
|
||||
|
||||
# Emit a GitHub Actions warning (plain stderr outside Actions).
|
||||
warn() {
|
||||
if [ -n "${GITHUB_ACTIONS:-}" ]; then echo "::warning::$*"; else echo "warning: $*" >&2; fi
|
||||
}
|
||||
|
||||
# Copy the licence, notice and readme files into a package directory. The programs also carry
|
||||
# them (Help > About > Licences, `solvecraft-cli licences`).
|
||||
copy_docs() {
|
||||
local dest="$1" f
|
||||
for f in README.md LICENSE-MIT LICENSE-APACHE NOTICE THIRD-PARTY-LICENSES.txt; do
|
||||
if [ -f "$ROOT/$f" ]; then cp "$ROOT/$f" "$dest/"; fi
|
||||
done
|
||||
}
|
||||
|
||||
# The app icon in every size a package needs, drawn by the CLI (no image files are committed):
|
||||
# $1 = the solvecraft-cli to run, $2 = output dir. Writes hicolor/<n>x<n>/apps/$APP_ID.png,
|
||||
# solvecraft.ico and solvecraft-1024.png.
|
||||
make_icons() {
|
||||
local cli="$1" out="$2" s
|
||||
for s in 16 24 32 48 64 128 256 512; do
|
||||
mkdir -p "$out/hicolor/${s}x${s}/apps"
|
||||
"$cli" icon --size "$s" --out "$out/hicolor/${s}x${s}/apps/$APP_ID.png" >/dev/null
|
||||
done
|
||||
"$cli" icon --out "$out/solvecraft.ico" >/dev/null
|
||||
"$cli" icon --size 1024 --out "$out/solvecraft-1024.png" >/dev/null
|
||||
}
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build and package SolveCraft for FreeBSD:
|
||||
#
|
||||
# $DIST/solvecraft-<version>-freebsd-x86_64.tar.gz a /usr/local-style tree:
|
||||
# solvecraft-<version>-freebsd-x86_64/{bin, share/applications, share/icons, share/mime,
|
||||
# share/metainfo, share/doc/solvecraft}
|
||||
#
|
||||
# Install by copying the tree's contents into /usr/local:
|
||||
# tar -xzf solvecraft-<version>-freebsd-x86_64.tar.gz --strip-components 1 -C /usr/local
|
||||
#
|
||||
# Usage: packaging/freebsd/package.sh [--skip-build]
|
||||
# Needs: bash, cargo, and the packages installed by release.yml's FreeBSD job.
|
||||
# Adapted from PhotoCraft's packaging (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
|
||||
set -euo pipefail
|
||||
# shellcheck source=../env.sh
|
||||
. "$(dirname "${BASH_SOURCE[0]}")/../env.sh"
|
||||
LINUX="$ROOT/packaging/linux"
|
||||
|
||||
SKIP_BUILD=0
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--skip-build) SKIP_BUILD=1; shift ;;
|
||||
-h | --help) sed -n '2,13p' "$0"; exit 0 ;;
|
||||
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# FreeBSD's uname says amd64; release file names use x86_64 like the Linux ones.
|
||||
case "$(uname -m)" in
|
||||
amd64 | x86_64) ARCH=x86_64 ;;
|
||||
arm64 | aarch64) ARCH=aarch64 ;;
|
||||
*) echo "unsupported architecture $(uname -m)" >&2; exit 2 ;;
|
||||
esac
|
||||
BASENAME="solvecraft-$VERSION-freebsd-$ARCH"
|
||||
echo "==> SolveCraft $VERSION for FreeBSD $ARCH"
|
||||
|
||||
# The release VM has 12 GB; full parallelism on the biggest crates runs it out of memory.
|
||||
export CARGO_BUILD_JOBS="${CARGO_BUILD_JOBS:-4}"
|
||||
if [ "$SKIP_BUILD" = 0 ]; then
|
||||
(cd "$ROOT" && cargo build --release --locked -p solvecraft -p solvecraft-cli)
|
||||
fi
|
||||
WORK="$CARGO_TARGET_DIR/freebsd-package"
|
||||
STAGE="$WORK/$BASENAME"
|
||||
BIN="$CARGO_TARGET_DIR/release"
|
||||
rm -rf "$WORK"
|
||||
|
||||
# FreeBSD's install(1) has no -D: create the directories first.
|
||||
mkdir -p "$STAGE/bin" "$STAGE/share/applications" "$STAGE/share/mime/packages" \
|
||||
"$STAGE/share/metainfo" "$STAGE/share/icons" "$STAGE/share/doc/solvecraft"
|
||||
install -m 755 "$BIN/solvecraft" "$BIN/solvecraft-cli" "$STAGE/bin/"
|
||||
strip "$STAGE/bin/solvecraft" "$STAGE/bin/solvecraft-cli" 2>/dev/null || true
|
||||
# The desktop entry, MIME type, metainfo and icons are the freedesktop files Linux ships.
|
||||
install -m 644 "$LINUX/$APP_ID.desktop" "$STAGE/share/applications/$APP_ID.desktop"
|
||||
install -m 644 "$LINUX/$APP_ID.mime.xml" "$STAGE/share/mime/packages/$APP_ID.xml"
|
||||
sed -e "s/@VERSION@/$VERSION/g" -e "s/@DATE@/$SOLVECRAFT_BUILD_DATE/g" \
|
||||
"$LINUX/$APP_ID.metainfo.xml.in" >"$STAGE/share/metainfo/$APP_ID.metainfo.xml"
|
||||
make_icons "$STAGE/bin/solvecraft-cli" "$WORK/icons"
|
||||
cp -R "$WORK/icons/hicolor" "$STAGE/share/icons/"
|
||||
copy_docs "$STAGE/share/doc/solvecraft"
|
||||
|
||||
for f in bin/solvecraft bin/solvecraft-cli "share/applications/$APP_ID.desktop" \
|
||||
"share/icons/hicolor/256x256/apps/$APP_ID.png" share/doc/solvecraft/LICENSE-MIT \
|
||||
share/doc/solvecraft/LICENSE-APACHE share/doc/solvecraft/THIRD-PARTY-LICENSES.txt; do
|
||||
if [ ! -e "$STAGE/$f" ]; then echo "error: $f is missing from the package" >&2; exit 1; fi
|
||||
done
|
||||
|
||||
tar -C "$WORK" -czf "$DIST/$BASENAME.tar.gz" "$BASENAME"
|
||||
echo "wrote $DIST/$BASENAME.tar.gz"
|
||||
"$STAGE/bin/solvecraft-cli" --version
|
||||
echo "==> done"
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#!/bin/sh
|
||||
# SolveCraft AppImage entry point (packaging/linux/AppRun; installed by package.sh).
|
||||
# Adapted from PhotoCraft's AppRun (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
|
||||
#
|
||||
# On Wayland the compositor resolves a window's dock icon by taking the window's
|
||||
# app_id (ai.storyteller.solvecraft) and looking up an installed .desktop file.
|
||||
# An AppImage installs nothing, so every AppImage run would otherwise show the
|
||||
# generic "Wayland" icon. Best-effort desktop integration: on first run
|
||||
# (and whenever the AppImage's path changes) install the .desktop entry and the
|
||||
# hicolor icons into $XDG_DATA_HOME, then launch. Every failure here must leave
|
||||
# the launch untouched. Opt out with SOLVECRAFT_NO_DESKTOP_INTEGRATION=1.
|
||||
|
||||
set -u
|
||||
|
||||
APPDIR="$(dirname "$(readlink -f "$0")")"
|
||||
DESK_ID=ai.storyteller.solvecraft
|
||||
|
||||
integrate() {
|
||||
case "${SOLVECRAFT_NO_DESKTOP_INTEGRATION:-}" in
|
||||
1 | true | yes) return 0 ;;
|
||||
esac
|
||||
# The AppImage runtime exports $APPIMAGE; without it (an extracted AppDir) there
|
||||
# is no path to register and nothing to do.
|
||||
appimage="${APPIMAGE:-}"
|
||||
[ -n "$appimage" ] && [ -f "$appimage" ] || return 0
|
||||
appimage="$(readlink -f "$appimage")" || return 0
|
||||
# Exec= is parsed by the desktop: a path holding a quote, backquote, dollar, backslash,
|
||||
# percent or a line break would need escaping (or could add lines), so skip integration.
|
||||
case "$appimage" in
|
||||
*[\"\`\$\\%]* | *'
|
||||
'*) return 0 ;;
|
||||
esac
|
||||
home="${HOME:-}"
|
||||
[ -n "$home" ] || return 0
|
||||
data="${XDG_DATA_HOME:-$home/.local/share}"
|
||||
src_desktop="$APPDIR/usr/share/applications/$DESK_ID.desktop"
|
||||
[ -f "$src_desktop" ] || return 0
|
||||
|
||||
# Rewrite Exec to this AppImage (the binary is not on the host PATH) and drop
|
||||
# TryExec, which would hide the entry; everything else is shipped as packaged.
|
||||
apps="$data/applications"
|
||||
mkdir -p "$apps" 2>/dev/null || return 0
|
||||
tmp="$apps/.$DESK_ID.tmp.$$"
|
||||
esc="$(printf '%s' "$appimage" | sed 's/[\\&|]/\\&/g')"
|
||||
if sed -e "s|^Exec=.*|Exec=\"$esc\" %F|" -e '/^TryExec=/d' "$src_desktop" 2>/dev/null >"$tmp"; then
|
||||
if cmp -s "$tmp" "$apps/$DESK_ID.desktop"; then
|
||||
rm -f "$tmp"
|
||||
else
|
||||
if mv -f "$tmp" "$apps/$DESK_ID.desktop" 2>/dev/null; then
|
||||
command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$apps" >/dev/null 2>&1
|
||||
else
|
||||
rm -f "$tmp"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
rm -f "$tmp"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Icons: per-file compare so an unchanged install costs a handful of cmp calls.
|
||||
src_icons="$APPDIR/usr/share/icons/hicolor"
|
||||
[ -d "$src_icons" ] || return 0
|
||||
icons="$data/icons/hicolor"
|
||||
(cd "$src_icons" && find . -type f) | while IFS= read -r rel; do
|
||||
mkdir -p "$icons/$(dirname "$rel")" 2>/dev/null || continue
|
||||
if ! cmp -s "$src_icons/$rel" "$icons/$rel"; then
|
||||
cp "$src_icons/$rel" "$icons/$rel" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
integrate
|
||||
exec "$APPDIR/usr/bin/solvecraft" "$@"
|
||||
@@ -0,0 +1,14 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=SolveCraft
|
||||
GenericName=3D CAD
|
||||
Comment=Parametric 3D CAD: sketch, constrain, extrude, and change a parameter to rebuild
|
||||
Exec=solvecraft %f
|
||||
TryExec=solvecraft
|
||||
Icon=ai.storyteller.solvecraft
|
||||
Terminal=false
|
||||
StartupNotify=true
|
||||
StartupWMClass=solvecraft
|
||||
Categories=Graphics;3DGraphics;Engineering;
|
||||
Keywords=cad;3d;parametric;sketch;step;stl;modelling;modeling;
|
||||
MimeType=application/x-solvecraft;model/step;model/iges;model/stl;model/3mf;
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- AppStream metadata (software centres, Flathub). Template: package.sh substitutes @VERSION@
|
||||
and @DATE@ from Cargo.toml and the build date. -->
|
||||
<component type="desktop-application">
|
||||
<id>ai.storyteller.solvecraft</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>MIT OR Apache-2.0</project_license>
|
||||
<name>SolveCraft</name>
|
||||
<summary>Parametric 3D CAD in the style of Fusion</summary>
|
||||
<developer id="io.github.bherbruck">
|
||||
<name>SolveCraft contributors</name>
|
||||
</developer>
|
||||
<description>
|
||||
<p>
|
||||
SolveCraft is an open-source parametric 3D CAD application written in Rust. Sketch on a
|
||||
plane, constrain and dimension the sketch, turn profiles into solids on a feature
|
||||
timeline, and change a parameter to watch the timeline rebuild.
|
||||
</p>
|
||||
<ul>
|
||||
<li>Sketches with a constraint solver and parameter-driven dimensions</li>
|
||||
<li>Extrude, revolve, sweep, loft, fillet, chamfer, shell, holes and patterns</li>
|
||||
<li>Components and joints, sheet metal, and a plastic-enclosure subset</li>
|
||||
<li>STEP and IGES import and export; 3MF, STL and OBJ</li>
|
||||
<li>Every action is a command, scriptable from a CLI, a JSON control channel and an MCP server</li>
|
||||
</ul>
|
||||
</description>
|
||||
<launchable type="desktop-id">ai.storyteller.solvecraft.desktop</launchable>
|
||||
<icon type="stock">ai.storyteller.solvecraft</icon>
|
||||
<url type="homepage">https://bherbruck.github.io/solvecraft/</url>
|
||||
<url type="bugtracker">https://github.com/bherbruck/solvecraft/issues</url>
|
||||
<url type="help">https://bherbruck.github.io/solvecraft/</url>
|
||||
<categories>
|
||||
<category>Graphics</category>
|
||||
<category>3DGraphics</category>
|
||||
<category>Engineering</category>
|
||||
</categories>
|
||||
<keywords>
|
||||
<keyword>cad</keyword>
|
||||
<keyword>parametric</keyword>
|
||||
<keyword>step</keyword>
|
||||
</keywords>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<caption>A sample part with its sketches and feature timeline</caption>
|
||||
<image>https://raw.githubusercontent.com/bherbruck/solvecraft/main/docs/screenshots/sample-plate.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<requires>
|
||||
<display_length compare="ge">760</display_length>
|
||||
</requires>
|
||||
<supports>
|
||||
<control>keyboard</control>
|
||||
<control>pointing</control>
|
||||
</supports>
|
||||
<provides>
|
||||
<binary>solvecraft</binary>
|
||||
<binary>solvecraft-cli</binary>
|
||||
<mediatype>application/x-solvecraft</mediatype>
|
||||
</provides>
|
||||
<content_rating type="oars-1.1"/>
|
||||
<releases>
|
||||
<release version="@VERSION@" date="@DATE@"/>
|
||||
</releases>
|
||||
</component>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- shared-mime-info definitions for types the system database may not have.
|
||||
Installed to /usr/share/mime/packages/ (update-mime-database runs on install). -->
|
||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||
<mime-type type="application/x-solvecraft">
|
||||
<comment>SolveCraft design</comment>
|
||||
<icon name="ai.storyteller.solvecraft"/>
|
||||
<sub-class-of type="application/json"/>
|
||||
<glob pattern="*.solvecraft"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
# Repackage a SolveCraft Linux tarball as a single-file Flatpak bundle:
|
||||
#
|
||||
# $DIST/solvecraft-<version>-linux-<arch>.flatpak
|
||||
#
|
||||
# Usage: packaging/linux/flatpak-bundle.sh [--no-test] [TARBALL]
|
||||
#
|
||||
# TARBALL defaults to $DIST/solvecraft-<version>-linux-<arch>.tar.gz from package.sh. The bundle
|
||||
# is built for the host architecture (x86_64 or aarch64), which must match the tarball's.
|
||||
# Needs flatpak, flatpak-builder and the SVG pixbuf loader (librsvg2-common) for the host's
|
||||
# `appstreamcli compose`; the freedesktop runtime and SDK named in the manifest are
|
||||
# installed per-user from Flathub. Unless --no-test, the bundle is then installed per-user and
|
||||
# `solvecraft-cli --version` is run inside the sandbox as a smoke test.
|
||||
# Manifest: packaging/linux/flatpak/ai.storyteller.solvecraft.bundle.yml.
|
||||
# Adapted from PhotoCraft's (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
|
||||
set -euo pipefail
|
||||
# shellcheck source=../env.sh
|
||||
. "$(dirname "${BASH_SOURCE[0]}")/../env.sh"
|
||||
HERE="$ROOT/packaging/linux"
|
||||
FLATHUB=https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||
|
||||
TEST=1
|
||||
TARBALL=""
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--no-test) TEST=0; shift ;;
|
||||
-h | --help) sed -n '2,14p' "$0"; exit 0 ;;
|
||||
-*) echo "unknown argument: $1" >&2; exit 2 ;;
|
||||
*) TARBALL="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
ARCH="$(uname -m)"
|
||||
case "$ARCH" in
|
||||
x86_64) ;;
|
||||
aarch64 | arm64) ARCH=aarch64 ;;
|
||||
*) echo "unsupported architecture $ARCH" >&2; exit 2 ;;
|
||||
esac
|
||||
BASENAME="solvecraft-$VERSION-linux-$ARCH"
|
||||
TARBALL="${TARBALL:-$DIST/$BASENAME.tar.gz}"
|
||||
[ -f "$TARBALL" ] || { echo "error: $TARBALL not found (run packaging/linux/package.sh --formats tar first)" >&2; exit 1; }
|
||||
for tool in flatpak flatpak-builder; do
|
||||
command -v "$tool" >/dev/null || { echo "error: $tool not found (apt install flatpak flatpak-builder librsvg2-common)" >&2; exit 1; }
|
||||
done
|
||||
|
||||
echo "==> SolveCraft $VERSION Flatpak bundle for $ARCH from $(basename "$TARBALL")"
|
||||
|
||||
WORK="$CARGO_TARGET_DIR/flatpak-bundle"
|
||||
rm -rf "$WORK"
|
||||
mkdir -p "$WORK/stage"
|
||||
tar -xzf "$TARBALL" -C "$WORK/stage" --strip-components=1
|
||||
[ -x "$WORK/stage/bin/solvecraft" ] || { echo "error: $TARBALL has no bin/solvecraft" >&2; exit 1; }
|
||||
cp "$HERE/flatpak/$APP_ID.bundle.yml" "$WORK/$APP_ID.yml"
|
||||
|
||||
flatpak remote-add --user --if-not-exists flathub "$FLATHUB"
|
||||
# --disable-rofiles-fuse: no FUSE in containers and CI runners.
|
||||
flatpak-builder --user --install-deps-from=flathub --disable-rofiles-fuse --force-clean \
|
||||
--default-branch=stable --repo="$WORK/repo" --state-dir="$WORK/state" "$WORK/build" "$WORK/$APP_ID.yml"
|
||||
|
||||
OUT="$DIST/$BASENAME.flatpak"
|
||||
# --runtime-repo lets `flatpak install` fetch the freedesktop runtime from Flathub if missing.
|
||||
flatpak build-bundle --runtime-repo="$FLATHUB" "$WORK/repo" "$OUT" "$APP_ID" stable
|
||||
echo "wrote $OUT"
|
||||
|
||||
if [ "$TEST" = 1 ]; then
|
||||
flatpak install --user -y --noninteractive --reinstall "$OUT"
|
||||
flatpak info --user "$APP_ID"
|
||||
flatpak run --command=solvecraft-cli "$APP_ID" --version
|
||||
flatpak run --command=sh "$APP_ID" -c 'ls /app/share/applications /app/share/metainfo /app/share/mime/packages /app/share/icons/hicolor/256x256/apps /app/share/doc/solvecraft'
|
||||
fi
|
||||
echo "==> done"
|
||||
ls -lh "$OUT"
|
||||
@@ -0,0 +1,43 @@
|
||||
# Flatpak manifest for the single-file bundle attached to every GitHub release
|
||||
# (solvecraft-<version>-linux-<arch>.flatpak). Build it with packaging/linux/flatpak-bundle.sh.
|
||||
# Adapted from PhotoCraft's (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
|
||||
#
|
||||
# It repackages the release's own Linux tarball (solvecraft-<version>-linux-<arch>.tar.gz, built
|
||||
# on Ubuntu 22.04 by package.sh) instead of compiling Rust inside flatpak-builder: no network
|
||||
# during the build, and the same binaries as the AppImage/deb/rpm. The binaries need glibc >= 2.35
|
||||
# and load X11, Wayland, xkbcommon and Vulkan at runtime; the freedesktop runtime has all of
|
||||
# them, and the GPU driver comes from its org.freedesktop.Platform.GL extension.
|
||||
id: ai.storyteller.solvecraft
|
||||
runtime: org.freedesktop.Platform
|
||||
runtime-version: '26.08'
|
||||
sdk: org.freedesktop.Sdk
|
||||
command: solvecraft
|
||||
finish-args:
|
||||
# Display: Wayland first; X11 only when no Wayland compositor is running.
|
||||
- --socket=wayland
|
||||
- --socket=fallback-x11
|
||||
# X11 shared memory (MIT-SHM). Without it the X11 fallback is slow or fails.
|
||||
- --share=ipc
|
||||
# GPU access for the wgpu viewport (Vulkan, or GL as a fallback).
|
||||
- --device=dri
|
||||
# Open/Save dialogs go through the file-chooser portal (rfd's xdg-portal backend), so picked
|
||||
# files work anywhere without broad filesystem access. Documents is granted so files passed by
|
||||
# path (the command line, recent designs) work in the usual place.
|
||||
- --filesystem=xdg-documents
|
||||
# Deliberately not granted: network (the optional --control server listens on 127.0.0.1 in
|
||||
# the sandbox; `flatpak override --user --share=network ai.storyteller.solvecraft` exposes it to
|
||||
# host agents) and home/host filesystem access.
|
||||
modules:
|
||||
- name: solvecraft
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- install -Dm755 bin/solvecraft /app/bin/solvecraft
|
||||
- install -Dm755 bin/solvecraft-cli /app/bin/solvecraft-cli
|
||||
- install -Dm644 share/applications/ai.storyteller.solvecraft.desktop /app/share/applications/ai.storyteller.solvecraft.desktop
|
||||
- install -Dm644 share/mime/packages/ai.storyteller.solvecraft.xml /app/share/mime/packages/ai.storyteller.solvecraft.xml
|
||||
# Already has the release's version and date substituted by package.sh.
|
||||
- install -Dm644 share/metainfo/ai.storyteller.solvecraft.metainfo.xml /app/share/metainfo/ai.storyteller.solvecraft.metainfo.xml
|
||||
- mkdir -p /app/share/icons /app/share/doc && cp -R share/icons/hicolor /app/share/icons/ && cp -R share/doc/solvecraft /app/share/doc/
|
||||
sources:
|
||||
- type: dir
|
||||
path: stage
|
||||
@@ -0,0 +1,74 @@
|
||||
# .deb and .rpm packages (nfpm: https://nfpm.goreleaser.com). Run by package.sh, which stages
|
||||
# the file tree, substitutes its path for ${STAGE} (nfpm doesn't expand env vars in `src`) and
|
||||
# exports VERSION and NFPM_ARCH. Adapted from PhotoCraft's (storytold/photocraft, Apache-2.0).
|
||||
name: solvecraft
|
||||
arch: ${NFPM_ARCH}
|
||||
platform: linux
|
||||
version: ${VERSION}
|
||||
# semver: 0.2.0-rc.1 becomes 0.2.0~rc.1 in deb/rpm, so pre-releases sort before the release.
|
||||
version_schema: semver
|
||||
release: 1
|
||||
section: graphics
|
||||
priority: optional
|
||||
maintainer: ${SOLVECRAFT_MAINTAINER}
|
||||
vendor: SolveCraft contributors
|
||||
homepage: https://bherbruck.github.io/solvecraft/
|
||||
license: MIT OR Apache-2.0
|
||||
description: |-
|
||||
Open-source parametric 3D CAD in the style of Fusion
|
||||
Sketches with a constraint solver, a parametric feature timeline, components and
|
||||
joints, sheet metal; STEP, IGES, 3MF, STL and OBJ. Includes solvecraft-cli, a
|
||||
headless command runner and MCP server for AI agents.
|
||||
|
||||
# The binaries link only glibc and libgcc_s directly. Windowing (X11/Wayland, xkbcommon) and the
|
||||
# GPU (Vulkan, EGL/GL) are loaded at runtime with dlopen, so they aren't ELF NEEDED entries and
|
||||
# the package tools can't find them: list them here.
|
||||
overrides:
|
||||
deb:
|
||||
depends:
|
||||
- libc6 (>= 2.35)
|
||||
- libgcc-s1
|
||||
- libxkbcommon0
|
||||
- libxkbcommon-x11-0
|
||||
- libwayland-client0
|
||||
- libx11-6
|
||||
- libx11-xcb1
|
||||
- libxcb1
|
||||
- libxcursor1
|
||||
- libxi6
|
||||
- libvulkan1 | libegl1
|
||||
recommends:
|
||||
- mesa-vulkan-drivers
|
||||
- libegl-mesa0
|
||||
- xdg-desktop-portal
|
||||
rpm:
|
||||
# Soname provides rather than package names, so the same requirements resolve on Fedora/RHEL
|
||||
# and openSUSE.
|
||||
depends:
|
||||
- libxkbcommon.so.0()(64bit)
|
||||
- libxkbcommon-x11.so.0()(64bit)
|
||||
- libwayland-client.so.0()(64bit)
|
||||
- libX11.so.6()(64bit)
|
||||
- libX11-xcb.so.1()(64bit)
|
||||
- libxcb.so.1()(64bit)
|
||||
- libXcursor.so.1()(64bit)
|
||||
- libXi.so.6()(64bit)
|
||||
- (libvulkan.so.1()(64bit) or libEGL.so.1()(64bit))
|
||||
recommends:
|
||||
- mesa-vulkan-drivers
|
||||
- xdg-desktop-portal
|
||||
|
||||
contents:
|
||||
- src: ${STAGE}/usr/bin/solvecraft
|
||||
dst: /usr/bin/solvecraft
|
||||
file_info: { mode: 0755 }
|
||||
- src: ${STAGE}/usr/bin/solvecraft-cli
|
||||
dst: /usr/bin/solvecraft-cli
|
||||
file_info: { mode: 0755 }
|
||||
- src: ${STAGE}/usr/share/
|
||||
dst: /usr/share/
|
||||
type: tree
|
||||
|
||||
scripts:
|
||||
postinstall: packaging/linux/postinst.sh
|
||||
postremove: packaging/linux/postinst.sh
|
||||
Executable
+139
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build and package SolveCraft for Linux (<arch> is x86_64 or aarch64):
|
||||
#
|
||||
# $DIST/solvecraft-<version>-linux-<arch>.AppImage any distro with glibc >= the build host's
|
||||
# $DIST/solvecraft-<version>-linux-<arch>.AppImage.zsync delta updates (needs zsyncmake)
|
||||
# $DIST/solvecraft-<version>-linux-<arch>.deb Debian, Ubuntu, Mint, Pop!_OS, ...
|
||||
# $DIST/solvecraft-<version>-linux-<arch>.rpm Fedora, openSUSE, RHEL, ...
|
||||
# $DIST/solvecraft-<version>-linux-<arch>.tar.gz plain FHS-style tree (bin/, share/)
|
||||
#
|
||||
# Usage: packaging/linux/package.sh [--skip-build] [--formats "appimage deb rpm tar"]
|
||||
#
|
||||
# Needs: cargo; nfpm for deb/rpm (https://nfpm.goreleaser.com); appimagetool for the AppImage
|
||||
# (downloaded into $CARGO_TARGET_DIR if missing). Build on an old distro (CI: Ubuntu 22.04,
|
||||
# glibc 2.35) so the binaries run on newer ones. Optional: desktop-file-validate, appstreamcli,
|
||||
# zsyncmake (the zsync package) for the AppImage's .zsync.
|
||||
# Adapted from PhotoCraft's packaging (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
|
||||
set -euo pipefail
|
||||
# shellcheck source=../env.sh
|
||||
. "$(dirname "${BASH_SOURCE[0]}")/../env.sh"
|
||||
HERE="$ROOT/packaging/linux"
|
||||
|
||||
SKIP_BUILD=0
|
||||
FORMATS="appimage deb rpm tar"
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--skip-build) SKIP_BUILD=1; shift ;;
|
||||
--formats) FORMATS="$2"; shift 2 ;;
|
||||
-h | --help) sed -n '2,16p' "$0"; exit 0 ;;
|
||||
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
ARCH="$(uname -m)"
|
||||
case "$ARCH" in
|
||||
x86_64) DEB_ARCH=amd64 ;;
|
||||
aarch64 | arm64) ARCH=aarch64; DEB_ARCH=arm64 ;;
|
||||
*) echo "unsupported architecture $ARCH" >&2; exit 2 ;;
|
||||
esac
|
||||
export SOLVECRAFT_MAINTAINER="${SOLVECRAFT_MAINTAINER:-SolveCraft contributors <https://github.com/bherbruck/solvecraft>}"
|
||||
BASENAME="solvecraft-$VERSION-linux-$ARCH"
|
||||
|
||||
echo "==> SolveCraft $VERSION for Linux $ARCH ($FORMATS)"
|
||||
|
||||
if [ "$SKIP_BUILD" = 0 ]; then
|
||||
(cd "$ROOT" && cargo build --release --locked -p solvecraft -p solvecraft-cli)
|
||||
fi
|
||||
BIN="$CARGO_TARGET_DIR/release"
|
||||
WORK="$CARGO_TARGET_DIR/linux-package"
|
||||
STAGE="$WORK/root"
|
||||
rm -rf "$WORK"
|
||||
|
||||
# ---- stage an FHS tree (shared by every format) -------------------------------------------------
|
||||
install -Dm755 "$BIN/solvecraft" "$STAGE/usr/bin/solvecraft"
|
||||
install -Dm755 "$BIN/solvecraft-cli" "$STAGE/usr/bin/solvecraft-cli"
|
||||
strip "$STAGE/usr/bin/solvecraft" "$STAGE/usr/bin/solvecraft-cli" 2>/dev/null || true
|
||||
install -Dm644 "$HERE/$APP_ID.desktop" "$STAGE/usr/share/applications/$APP_ID.desktop"
|
||||
install -Dm644 "$HERE/$APP_ID.mime.xml" "$STAGE/usr/share/mime/packages/$APP_ID.xml"
|
||||
mkdir -p "$STAGE/usr/share/metainfo"
|
||||
sed -e "s/@VERSION@/$VERSION/g" -e "s/@DATE@/$SOLVECRAFT_BUILD_DATE/g" \
|
||||
"$HERE/$APP_ID.metainfo.xml.in" >"$STAGE/usr/share/metainfo/$APP_ID.metainfo.xml"
|
||||
make_icons "$STAGE/usr/bin/solvecraft-cli" "$WORK/icons"
|
||||
mkdir -p "$STAGE/usr/share/icons"
|
||||
cp -R "$WORK/icons/hicolor" "$STAGE/usr/share/icons/"
|
||||
mkdir -p "$STAGE/usr/share/doc/solvecraft"
|
||||
copy_docs "$STAGE/usr/share/doc/solvecraft"
|
||||
|
||||
if command -v desktop-file-validate >/dev/null; then
|
||||
desktop-file-validate "$STAGE/usr/share/applications/$APP_ID.desktop"
|
||||
fi
|
||||
if command -v appstreamcli >/dev/null; then
|
||||
appstreamcli validate --no-net --explain "$STAGE/usr/share/metainfo/$APP_ID.metainfo.xml"
|
||||
fi
|
||||
|
||||
has() { case " $FORMATS " in *" $1 "*) return 0 ;; *) return 1 ;; esac; }
|
||||
|
||||
# ---- .tar.gz ------------------------------------------------------------------------------------
|
||||
if has tar; then
|
||||
mkdir -p "$WORK/tar"
|
||||
cp -R "$STAGE/usr" "$WORK/tar/$BASENAME"
|
||||
tar -C "$WORK/tar" -czf "$DIST/$BASENAME.tar.gz" "$BASENAME"
|
||||
echo "wrote $DIST/$BASENAME.tar.gz"
|
||||
fi
|
||||
|
||||
# ---- .deb / .rpm --------------------------------------------------------------------------------
|
||||
if has deb || has rpm; then
|
||||
command -v nfpm >/dev/null || { echo "error: nfpm not found (https://nfpm.goreleaser.com/install/)" >&2; exit 1; }
|
||||
export VERSION
|
||||
export NFPM_ARCH="$DEB_ARCH"
|
||||
# nfpm expands env vars in fields like `version` and `arch`, but not in `contents[].src`.
|
||||
sed "s|\${STAGE}|$STAGE|g" "$HERE/nfpm.yaml" >"$WORK/nfpm.yaml"
|
||||
for fmt in deb rpm; do
|
||||
if has "$fmt"; then (cd "$ROOT" && nfpm package -f "$WORK/nfpm.yaml" -p "$fmt" -t "$DIST/$BASENAME.$fmt"); fi
|
||||
done
|
||||
fi
|
||||
|
||||
# ---- AppImage -----------------------------------------------------------------------------------
|
||||
if has appimage; then
|
||||
APPDIR="$WORK/SolveCraft.AppDir"
|
||||
cp -R "$STAGE" "$APPDIR"
|
||||
mv "$APPDIR/usr/share/doc" "$WORK/doc-unused"
|
||||
# AppRun is a script, not a symlink: it installs the .desktop entry and icons into
|
||||
# $XDG_DATA_HOME before launching, so Wayland compositors can resolve the window's app_id to
|
||||
# our icon.
|
||||
install -Dm755 "$HERE/AppRun" "$APPDIR/AppRun"
|
||||
cp "$HERE/$APP_ID.desktop" "$APPDIR/$APP_ID.desktop"
|
||||
cp "$WORK/icons/hicolor/256x256/apps/$APP_ID.png" "$APPDIR/$APP_ID.png"
|
||||
ln -s "$APP_ID.png" "$APPDIR/.DirIcon"
|
||||
|
||||
TOOL="${APPIMAGETOOL:-$(command -v appimagetool || true)}"
|
||||
if [ -z "$TOOL" ]; then
|
||||
TOOL="$CARGO_TARGET_DIR/appimagetool-$ARCH.AppImage"
|
||||
if [ ! -x "$TOOL" ]; then
|
||||
curl -fsSL -o "$TOOL" "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$ARCH.AppImage"
|
||||
chmod +x "$TOOL"
|
||||
fi
|
||||
fi
|
||||
OUT="$DIST/$BASENAME.AppImage"
|
||||
APPDIR="$(cd "$APPDIR" && pwd)"
|
||||
TOOL="$(cd "$(dirname "$TOOL")" && pwd)/$(basename "$TOOL")"
|
||||
rm -f "$OUT.zsync"
|
||||
# Update information: AppImageUpdate and the like fetch only the changed blocks of a newer
|
||||
# release through the .zsync published next to each AppImage. `latest` is the newest published
|
||||
# release that is not a pre-release.
|
||||
REPO="${GITHUB_REPOSITORY:-bherbruck/solvecraft}"
|
||||
UPDATE_INFO="gh-releases-zsync|${REPO%%/*}|${REPO#*/}|latest|solvecraft-*-linux-$ARCH.AppImage.zsync"
|
||||
# Extract-and-run: works without FUSE (containers, CI). With zsyncmake on the host appimagetool
|
||||
# also writes the .zsync, into its working directory, hence the cd.
|
||||
(cd "$DIST" && ARCH="$ARCH" APPIMAGE_EXTRACT_AND_RUN=1 "$TOOL" --no-appstream -u "$UPDATE_INFO" "$APPDIR" "$OUT")
|
||||
echo "wrote $OUT"
|
||||
if [ -s "$OUT.zsync" ]; then
|
||||
echo "wrote $OUT.zsync"
|
||||
else
|
||||
warn "zsyncmake not found, so $OUT.zsync was not written; AppImage delta updates need it"
|
||||
fi
|
||||
fi
|
||||
|
||||
"$STAGE/usr/bin/solvecraft-cli" --version
|
||||
echo "==> done"
|
||||
ls -lh "$DIST"
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
# deb/rpm post-install and post-remove: refresh the desktop, MIME and icon caches.
|
||||
# Every tool is optional; missing ones are skipped.
|
||||
if command -v update-desktop-database >/dev/null 2>&1; then
|
||||
update-desktop-database -q /usr/share/applications || true
|
||||
fi
|
||||
if command -v update-mime-database >/dev/null 2>&1; then
|
||||
update-mime-database /usr/share/mime || true
|
||||
fi
|
||||
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
|
||||
gtk-update-icon-cache -q -t -f /usr/share/icons/hicolor || true
|
||||
fi
|
||||
exit 0
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<!-- Template: packaging/macos/package.sh substitutes @VERSION@ (full, e.g. 0.2.0-rc.1) and
|
||||
@SHORT_VERSION@ (numeric X.Y.Z). Adapted from PhotoCraft's (storytold/photocraft, Apache-2.0). -->
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>SolveCraft</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>SolveCraft</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>SolveCraft</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>ai.storyteller.solvecraft</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>@SHORT_VERSION@</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>@SHORT_VERSION@</string>
|
||||
<key>SolveCraftVersion</key>
|
||||
<string>@VERSION@</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>SolveCraft</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.graphics-design</string>
|
||||
<!-- Must match MACOSX_DEPLOYMENT_TARGET in package.sh. -->
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>11.0</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>SolveCraft design</string>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array><string>solvecraft</string></array>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>STEP, IGES and mesh files</string>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array><string>step</string><string>stp</string><string>igs</string><string>iges</string><string>3mf</string><string>stl</string></array>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
Executable
+116
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build and package SolveCraft for macOS:
|
||||
#
|
||||
# $DIST/solvecraft-<version>-macos-<arch>.dmg SolveCraft.app on a drag-to-Applications DMG
|
||||
# $DIST/solvecraft-cli-<version>-macos-<arch>.zip the headless CLI with the licence files
|
||||
#
|
||||
# Usage: packaging/macos/package.sh [--arch universal|aarch64|x86_64] [--skip-build]
|
||||
#
|
||||
# The app is ad-hoc signed (not notarized): the first launch needs right-click > Open.
|
||||
# Adapted from PhotoCraft's packaging (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
|
||||
set -euo pipefail
|
||||
# shellcheck source=../env.sh
|
||||
. "$(dirname "${BASH_SOURCE[0]}")/../env.sh"
|
||||
HERE="$ROOT/packaging/macos"
|
||||
|
||||
ARCH=universal
|
||||
SKIP_BUILD=0
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--arch) ARCH="$2"; shift 2 ;;
|
||||
--skip-build) SKIP_BUILD=1; shift ;;
|
||||
-h | --help) sed -n '2,10p' "$0"; exit 0 ;;
|
||||
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
case "$ARCH" in
|
||||
universal) TARGETS=(aarch64-apple-darwin x86_64-apple-darwin) ;;
|
||||
aarch64) TARGETS=(aarch64-apple-darwin) ;;
|
||||
x86_64) TARGETS=(x86_64-apple-darwin) ;;
|
||||
*) echo "unknown --arch $ARCH" >&2; exit 2 ;;
|
||||
esac
|
||||
|
||||
# Keep in sync with LSMinimumSystemVersion in Info.plist.in.
|
||||
export MACOSX_DEPLOYMENT_TARGET=11.0
|
||||
SHORT_VERSION="${VERSION%%-*}"
|
||||
WORK="$CARGO_TARGET_DIR/macos-package"
|
||||
APP="$WORK/SolveCraft.app"
|
||||
DMG="$DIST/solvecraft-$VERSION-macos-$ARCH.dmg"
|
||||
CLI_ZIP="$DIST/solvecraft-cli-$VERSION-macos-$ARCH.zip"
|
||||
|
||||
echo "==> SolveCraft $VERSION for macOS ($ARCH)"
|
||||
if [ "$SKIP_BUILD" = 0 ]; then
|
||||
args=()
|
||||
for t in "${TARGETS[@]}"; do args+=(--target "$t"); done
|
||||
(cd "$ROOT" && cargo build --release --locked -p solvecraft -p solvecraft-cli "${args[@]}")
|
||||
fi
|
||||
|
||||
rm -rf "$WORK"
|
||||
mkdir -p "$WORK/bin"
|
||||
for bin in solvecraft solvecraft-cli; do
|
||||
inputs=()
|
||||
for t in "${TARGETS[@]}"; do inputs+=("$CARGO_TARGET_DIR/$t/release/$bin"); done
|
||||
lipo -create -output "$WORK/bin/$bin" "${inputs[@]}"
|
||||
lipo -info "$WORK/bin/$bin"
|
||||
done
|
||||
|
||||
# ---- SolveCraft.app ----------------------------------------------------------------------------
|
||||
echo "==> assembling $APP"
|
||||
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
|
||||
cp "$WORK/bin/solvecraft" "$APP/Contents/MacOS/SolveCraft"
|
||||
# The icon, drawn by the CLI (no image files are committed).
|
||||
SET="$WORK/SolveCraft.iconset"
|
||||
mkdir -p "$SET"
|
||||
for s in 16 32 128 256 512; do
|
||||
"$WORK/bin/solvecraft-cli" icon --size "$s" --out "$SET/icon_${s}x${s}.png" >/dev/null
|
||||
"$WORK/bin/solvecraft-cli" icon --size $((s * 2)) --out "$SET/icon_${s}x${s}@2x.png" >/dev/null
|
||||
done
|
||||
iconutil -c icns -o "$APP/Contents/Resources/SolveCraft.icns" "$SET"
|
||||
mkdir -p "$APP/Contents/Resources/Licenses"
|
||||
copy_docs "$APP/Contents/Resources/Licenses"
|
||||
sed -e "s/@VERSION@/$VERSION/g" -e "s/@SHORT_VERSION@/$SHORT_VERSION/g" "$HERE/Info.plist.in" >"$APP/Contents/Info.plist"
|
||||
plutil -lint "$APP/Contents/Info.plist"
|
||||
printf 'APPL????' >"$APP/Contents/PkgInfo"
|
||||
# Ad-hoc signature over the whole bundle (Apple silicon refuses unsigned code).
|
||||
codesign --force --sign - --timestamp=none "$APP"
|
||||
codesign --verify --strict --deep --verbose=2 "$APP"
|
||||
|
||||
# ---- DMG ---------------------------------------------------------------------------------------
|
||||
echo "==> building $DMG"
|
||||
STAGE="$WORK/dmg"
|
||||
mkdir -p "$STAGE"
|
||||
ditto "$APP" "$STAGE/SolveCraft.app"
|
||||
ln -s /Applications "$STAGE/Applications"
|
||||
rm -f "$DMG" "$WORK/raw.dmg"
|
||||
# makehybrid + convert builds the image without attaching a device, unlike `create -srcfolder`,
|
||||
# which is flaky on CI runners ("Resource busy").
|
||||
hdiutil makehybrid -hfs -hfs-volume-name "SolveCraft" -hfs-openfolder "$STAGE" -o "$WORK/raw.dmg" "$STAGE"
|
||||
hdiutil convert "$WORK/raw.dmg" -format UDZO -imagekey zlib-level=9 -o "$DMG"
|
||||
rm -f "$WORK/raw.dmg"
|
||||
|
||||
# Check the image as a user gets it: mount it and look inside.
|
||||
MNT="$(mktemp -d)"
|
||||
hdiutil attach -readonly -nobrowse -mountpoint "$MNT" "$DMG"
|
||||
ls -la "$MNT" "$MNT/SolveCraft.app/Contents" "$MNT/SolveCraft.app/Contents/MacOS" "$MNT/SolveCraft.app/Contents/Resources"
|
||||
test -L "$MNT/Applications" && test "$(readlink "$MNT/Applications")" = /Applications
|
||||
plutil -lint "$MNT/SolveCraft.app/Contents/Info.plist"
|
||||
case "$ARCH" in universal) WANT=(arm64 x86_64) ;; aarch64) WANT=(arm64) ;; *) WANT=(x86_64) ;; esac
|
||||
lipo "$MNT/SolveCraft.app/Contents/MacOS/SolveCraft" -verify_arch "${WANT[@]}"
|
||||
test -s "$MNT/SolveCraft.app/Contents/Resources/Licenses/THIRD-PARTY-LICENSES.txt"
|
||||
codesign --verify --strict --deep "$MNT/SolveCraft.app"
|
||||
"$MNT/SolveCraft.app/Contents/MacOS/SolveCraft" --version
|
||||
hdiutil detach "$MNT"
|
||||
|
||||
# ---- CLI ---------------------------------------------------------------------------------------
|
||||
echo "==> building $CLI_ZIP"
|
||||
CLI_DIR="$WORK/solvecraft-cli-$VERSION-macos-$ARCH"
|
||||
mkdir -p "$CLI_DIR"
|
||||
cp "$WORK/bin/solvecraft-cli" "$CLI_DIR/"
|
||||
codesign --force --sign - --timestamp=none --identifier ai.storyteller.solvecraft-cli "$CLI_DIR/solvecraft-cli"
|
||||
copy_docs "$CLI_DIR"
|
||||
rm -f "$CLI_ZIP"
|
||||
ditto -c -k --keepParent "$CLI_DIR" "$CLI_ZIP"
|
||||
|
||||
"$WORK/bin/solvecraft-cli" --version
|
||||
echo "==> done"
|
||||
ls -lh "$DMG" "$CLI_ZIP"
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build the browser version and zip it: $DIST/solvecraft-web-<version>.zip
|
||||
#
|
||||
# Usage: packaging/web/package.sh [--skip-build]
|
||||
#
|
||||
# Needs: trunk and the wasm32-unknown-unknown target. The zip holds a self-contained static site
|
||||
# in solvecraft-web-<version>/ that works from any URL path (public_url = "./" in Trunk.toml).
|
||||
# Saving designs needs HTTPS or localhost (the browser's private file storage).
|
||||
# Adapted from PhotoCraft's packaging (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
|
||||
set -euo pipefail
|
||||
# shellcheck source=../env.sh
|
||||
. "$(dirname "${BASH_SOURCE[0]}")/../env.sh"
|
||||
|
||||
if [ "${1:-}" != "--skip-build" ]; then
|
||||
command -v trunk >/dev/null || { echo "error: trunk not found (cargo install trunk --locked)" >&2; exit 1; }
|
||||
(cd "$ROOT/apps/solvecraft-web" && trunk build --release)
|
||||
fi
|
||||
|
||||
SITE="$ROOT/dist/web"
|
||||
[ -f "$SITE/index.html" ] || { echo "error: $SITE/index.html missing; run without --skip-build" >&2; exit 1; }
|
||||
# Paths must be relative so the site works under any prefix.
|
||||
if grep -Eq '(src|href)="/[^/]' "$SITE/index.html"; then
|
||||
echo "error: $SITE/index.html has root-absolute URLs; it would break when served from a sub-path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NAME="solvecraft-web-$VERSION"
|
||||
WORK="$CARGO_TARGET_DIR/web-package"
|
||||
rm -rf "$WORK"
|
||||
mkdir -p "$WORK/$NAME"
|
||||
cp -R "$SITE/." "$WORK/$NAME/"
|
||||
copy_docs "$WORK/$NAME"
|
||||
rm -f "$DIST/$NAME.zip"
|
||||
(cd "$WORK" && zip -qr9 "$DIST/$NAME.zip" "$NAME")
|
||||
echo "wrote $DIST/$NAME.zip"
|
||||
ls -lh "$DIST/$NAME.zip"
|
||||
@@ -0,0 +1,133 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Build and package SolveCraft for Windows.
|
||||
|
||||
.DESCRIPTION
|
||||
Produces, in $env:DIST (default: dist/release):
|
||||
solvecraft-<version>-windows-<arch>.msi per-machine installer (WiX v5)
|
||||
solvecraft-<version>-windows-<arch>-portable.zip solvecraft.exe + solvecraft-cli.exe + licences
|
||||
SolveCraft-<version>-windows-<arch>.exe the app alone (x64 only: a plain download)
|
||||
|
||||
The binaries link the C runtime statically (+crt-static), so nothing else needs installing.
|
||||
The icon comes from $env:SOLVECRAFT_ICONS (solvecraft.ico, made by `solvecraft-cli icon`), or
|
||||
is drawn by the CLI just built when this machine can run it. Nothing is signed.
|
||||
|
||||
Needs: Rust (MSVC toolchain + the target) and WiX v5:
|
||||
dotnet tool install --global wix --version 5.0.2
|
||||
wix extension add -g WixToolset.UI.wixext/5.0.2 WixToolset.Util.wixext/5.0.2
|
||||
|
||||
Adapted from PhotoCraft's packaging (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
|
||||
|
||||
.EXAMPLE
|
||||
pwsh packaging/windows/package.ps1 -Arch x64
|
||||
pwsh packaging/windows/package.ps1 -Arch arm64 # cross-compiled; needs the MSVC ARM64 build tools
|
||||
#>
|
||||
param(
|
||||
[ValidateSet('x64', 'x86', 'arm64')] [string] $Arch = 'x64',
|
||||
[switch] $SkipBuild
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$Root = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
|
||||
|
||||
function Invoke-Native([string] $What, [scriptblock] $Block) {
|
||||
Write-Output "==> $What"
|
||||
& $Block
|
||||
if ($LASTEXITCODE -ne 0) { throw "$What failed with exit code $LASTEXITCODE" }
|
||||
}
|
||||
|
||||
# The version lives in one place: [workspace.package] version in the root Cargo.toml.
|
||||
$Version = $env:SOLVECRAFT_VERSION
|
||||
if (-not $Version) {
|
||||
$inPkg = $false
|
||||
foreach ($line in Get-Content (Join-Path $Root 'Cargo.toml')) {
|
||||
if ($line -match '^\s*\[') { $inPkg = ($line.Trim() -eq '[workspace.package]'); continue }
|
||||
if ($inPkg -and $line -match '^\s*version\s*=\s*"([^"]+)"') { $Version = $Matches[1]; break }
|
||||
}
|
||||
}
|
||||
if (-not $Version) { throw 'could not read [workspace.package] version from Cargo.toml' }
|
||||
# MSI ProductVersion is numeric (major.minor.build); pre-release tags are dropped there.
|
||||
$MsiVersion = ($Version -split '-')[0]
|
||||
|
||||
$Target = switch ($Arch) { 'x64' { 'x86_64-pc-windows-msvc' } 'x86' { 'i686-pc-windows-msvc' } 'arm64' { 'aarch64-pc-windows-msvc' } }
|
||||
$Dist = if ($env:DIST) { $env:DIST } else { Join-Path $Root 'dist\release' }
|
||||
$TargetDir = if ($env:CARGO_TARGET_DIR) { $env:CARGO_TARGET_DIR } else { Join-Path $Root 'target' }
|
||||
New-Item -ItemType Directory -Force -Path $Dist | Out-Null
|
||||
|
||||
Write-Output "SolveCraft $Version for Windows $Arch ($Target)"
|
||||
|
||||
if (-not $SkipBuild) {
|
||||
# Static CRT: no VC++ redistributable needed. Scoped to the target so host build scripts and
|
||||
# proc-macros are unaffected.
|
||||
$flagVar = 'CARGO_TARGET_' + ($Target.ToUpper() -replace '-', '_') + '_RUSTFLAGS'
|
||||
[Environment]::SetEnvironmentVariable($flagVar, '-C target-feature=+crt-static')
|
||||
Invoke-Native "cargo build ($Target)" { cargo build --release --locked -p solvecraft -p solvecraft-cli --target $Target }
|
||||
}
|
||||
$Bin = Join-Path $TargetDir "$Target\release"
|
||||
|
||||
# Check both binaries' PE headers: Machine must match -Arch; Subsystem 2 = GUI (the app opens no
|
||||
# console window), 3 = console (the CLI's output reaches the terminal).
|
||||
function Get-PeHeader([string] $Path) {
|
||||
$bytes = [System.IO.File]::ReadAllBytes($Path)
|
||||
$pe = [BitConverter]::ToInt32($bytes, 0x3C)
|
||||
return @{ Machine = [BitConverter]::ToUInt16($bytes, $pe + 4); Subsystem = [BitConverter]::ToUInt16($bytes, $pe + 0x5C) }
|
||||
}
|
||||
$Machine = switch ($Arch) { 'x64' { 0x8664 } 'x86' { 0x14C } 'arm64' { 0xAA64 } }
|
||||
foreach ($check in @(@('solvecraft.exe', 2), @('solvecraft-cli.exe', 3))) {
|
||||
$h = Get-PeHeader (Join-Path $Bin $check[0])
|
||||
if ($h.Machine -ne $Machine) { throw "$($check[0]) is for machine 0x$('{0:X}' -f $h.Machine), expected 0x$('{0:X}' -f $Machine) ($Arch)" }
|
||||
if ($h.Subsystem -ne $check[1]) { throw "$($check[0]) has PE subsystem $($h.Subsystem), expected $($check[1])" }
|
||||
Write-Output "ok $($check[0]): $Arch, PE subsystem $($h.Subsystem)"
|
||||
}
|
||||
$Stage = Join-Path $TargetDir "windows-package\$Arch"
|
||||
Remove-Item -Recurse -Force $Stage -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Force -Path $Stage | Out-Null
|
||||
Copy-Item (Join-Path $Bin 'solvecraft.exe'), (Join-Path $Bin 'solvecraft-cli.exe') $Stage
|
||||
foreach ($f in 'LICENSE-MIT', 'LICENSE-APACHE', 'NOTICE', 'THIRD-PARTY-LICENSES.txt') { Copy-Item (Join-Path $Root $f) $Stage }
|
||||
|
||||
$HostArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant()
|
||||
$CanRun = ($Arch -ne 'arm64' -or $HostArch -eq 'arm64')
|
||||
$Icon = if ($env:SOLVECRAFT_ICONS) { Join-Path $env:SOLVECRAFT_ICONS 'solvecraft.ico' } else { Join-Path $Stage 'solvecraft.ico' }
|
||||
if (-not (Test-Path $Icon)) {
|
||||
if (-not $CanRun) { throw "no icon: set SOLVECRAFT_ICONS to a folder with solvecraft.ico (an $Arch CLI can't run on this $HostArch machine)" }
|
||||
Invoke-Native 'draw the icon' { & (Join-Path $Stage 'solvecraft-cli.exe') icon --out $Icon }
|
||||
}
|
||||
|
||||
# ---- MSI ---------------------------------------------------------------------------------------
|
||||
$Msi = Join-Path $Dist "solvecraft-$Version-windows-$Arch.msi"
|
||||
Invoke-Native 'wix build' {
|
||||
wix build (Join-Path $PSScriptRoot 'solvecraft.wxs') -arch $Arch `
|
||||
-ext WixToolset.UI.wixext -ext WixToolset.Util.wixext `
|
||||
-culture en-US -loc (Join-Path $PSScriptRoot 'solvecraft.en-us.wxl') `
|
||||
-d "Version=$MsiVersion" -d "DisplayVersion=$Version" -d "BinDir=$Stage" -d "IconPath=$Icon" `
|
||||
-o $Msi
|
||||
}
|
||||
Invoke-Native 'MSI shortcut icon validation (ICE50)' {
|
||||
wix msi validate $Msi -ice ICE50 -intermediateFolder (Join-Path $Stage 'msi-validation')
|
||||
}
|
||||
# wix writes its debug symbols (.wixpdb) next to the MSI; keep them out of the release assets.
|
||||
Remove-Item -Force -ErrorAction SilentlyContinue ([IO.Path]::ChangeExtension($Msi, '.wixpdb'))
|
||||
|
||||
# ---- portable zip ------------------------------------------------------------------------------
|
||||
$Portable = Join-Path $TargetDir "windows-package\solvecraft-$Version-windows-$Arch-portable"
|
||||
Remove-Item -Recurse -Force $Portable -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Force -Path $Portable | Out-Null
|
||||
Copy-Item (Join-Path $Stage '*.exe') $Portable
|
||||
foreach ($f in 'README.md', 'LICENSE-MIT', 'LICENSE-APACHE', 'NOTICE', 'THIRD-PARTY-LICENSES.txt') { Copy-Item (Join-Path $Root $f) $Portable }
|
||||
$Zip = Join-Path $Dist "solvecraft-$Version-windows-$Arch-portable.zip"
|
||||
Remove-Item -Force $Zip -ErrorAction SilentlyContinue
|
||||
Compress-Archive -Path $Portable -DestinationPath $Zip
|
||||
|
||||
# ---- the app as a plain download (x64) ---------------------------------------------------------
|
||||
$Outputs = @($Msi, $Zip)
|
||||
if ($Arch -eq 'x64') {
|
||||
$Exe = Join-Path $Dist "SolveCraft-$Version-windows-$Arch.exe"
|
||||
Copy-Item (Join-Path $Stage 'solvecraft.exe') $Exe
|
||||
$Outputs += $Exe
|
||||
}
|
||||
|
||||
if ($CanRun) {
|
||||
Invoke-Native 'solvecraft-cli --version' { & (Join-Path $Stage 'solvecraft-cli.exe') --version }
|
||||
} else {
|
||||
Write-Output "skipping solvecraft-cli --version: an $Arch build doesn't run on this $HostArch machine"
|
||||
}
|
||||
Get-Item $Outputs | Format-Table Name, Length
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Overrides for the setup wizard's WixUI strings, so its pages name the version being installed
|
||||
([SolvecraftVersion] is the full version, pre-release tag included, set in solvecraft.wxs).
|
||||
Adapted from PhotoCraft's (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
|
||||
-->
|
||||
<WixLocalization Culture="en-US" xmlns="http://wixtoolset.org/schemas/v4/wxl">
|
||||
<String Id="WelcomeDlgTitle" Value="{\WixUI_Font_Bigger}Welcome to the [ProductName] [SolvecraftVersion] Setup Wizard" />
|
||||
<String Id="WelcomeDlgDescription" Value="The Setup Wizard will install [ProductName] version [SolvecraftVersion] on your computer. Click Next to continue or Cancel to exit the Setup Wizard." />
|
||||
<String Id="VerifyReadyDlgInstallTitle" Value="{\WixUI_Font_Title}Ready to install [ProductName] [SolvecraftVersion]" />
|
||||
<String Id="ExitDialogTitle" Value="{\WixUI_Font_Bigger}Completed the [ProductName] [SolvecraftVersion] Setup Wizard" />
|
||||
</WixLocalization>
|
||||
@@ -0,0 +1,121 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
SolveCraft MSI (WiX Toolset v5, plus the UI and Util extensions). Built by package.ps1:
|
||||
|
||||
wix build solvecraft.wxs -arch x64 -ext WixToolset.UI.wixext -ext WixToolset.Util.wixext
|
||||
-culture en-US -loc solvecraft.en-us.wxl -d Version=1.2.3 -d DisplayVersion=1.2.3-rc.1
|
||||
-d BinDir=... -d IconPath=... -o out.msi
|
||||
|
||||
Per-machine install with a setup wizard: Welcome, install folder (Program Files\SolveCraft by
|
||||
default, remembered for upgrades), Ready, progress, and a Finish page with a ticked "Launch
|
||||
SolveCraft" box. Installs a Start Menu shortcut, solvecraft-cli, the licence files (including
|
||||
THIRD-PARTY-LICENSES.txt), makes SolveCraft the owner of `.solvecraft` and offers it under
|
||||
"Open with" for STEP, IGES, 3MF and STL. `msiexec /qn` installs silently.
|
||||
Adapted from PhotoCraft's (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
|
||||
-->
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
|
||||
xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
|
||||
<Package
|
||||
Name="SolveCraft"
|
||||
Manufacturer="SolveCraft contributors"
|
||||
Version="$(var.Version)"
|
||||
UpgradeCode="6A145FD6-D285-428D-AE84-A69570CDF4DB"
|
||||
Scope="perMachine"
|
||||
Compressed="yes"
|
||||
Language="1033">
|
||||
<SummaryInformation Description="SolveCraft parametric 3D CAD" Manufacturer="SolveCraft contributors" />
|
||||
<!-- Same-version upgrades let successive pre-releases of one X.Y.Z replace each other. -->
|
||||
<MajorUpgrade AllowSameVersionUpgrades="yes"
|
||||
DowngradeErrorMessage="A newer version of SolveCraft is already installed." />
|
||||
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
|
||||
|
||||
<!-- Advertised shortcuts to an .exe need an .exe icon identifier (ICE50). -->
|
||||
<Icon Id="SolvecraftIcon.exe" SourceFile="$(var.IconPath)" />
|
||||
<Property Id="ARPPRODUCTICON" Value="SolvecraftIcon.exe" />
|
||||
<Property Id="ARPURLINFOABOUT" Value="https://bherbruck.github.io/solvecraft/" />
|
||||
<Property Id="ARPURLUPDATEINFO" Value="https://github.com/bherbruck/solvecraft/releases" />
|
||||
<Property Id="ARPHELPLINK" Value="https://github.com/bherbruck/solvecraft/issues" />
|
||||
|
||||
<StandardDirectory Id="ProgramFiles6432Folder">
|
||||
<Directory Id="INSTALLFOLDER" Name="SolveCraft" />
|
||||
</StandardDirectory>
|
||||
<StandardDirectory Id="ProgramMenuFolder" />
|
||||
|
||||
<!-- An upgrade installs where the user put the previous version (written by SolvecraftApp). -->
|
||||
<Property Id="INSTALLFOLDER">
|
||||
<RegistrySearch Id="PreviousInstallFolder" Root="HKLM" Key="Software\SolveCraft" Name="InstallDir" Type="raw" />
|
||||
</Property>
|
||||
|
||||
<Feature Id="Main" Title="SolveCraft" Level="1">
|
||||
<ComponentGroupRef Id="SolvecraftFiles" />
|
||||
</Feature>
|
||||
|
||||
<!-- WixUI_InstallDir without its licence page (MIT OR Apache-2.0: nothing to accept). -->
|
||||
<ui:WixUI Id="WixUI_InstallDir" InstallDirectory="INSTALLFOLDER" />
|
||||
<UI>
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" Order="2" Condition="NOT Installed" />
|
||||
<Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2" />
|
||||
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchSolvecraft"
|
||||
Condition="WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 AND NOT Installed" />
|
||||
</UI>
|
||||
|
||||
<Property Id="SolvecraftVersion" Value="$(var.DisplayVersion)" />
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALTEXT" Value="SolveCraft $(var.DisplayVersion) is installed. You can start it any time from the Start menu." />
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch SolveCraft" />
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1" />
|
||||
<Property Id="WixShellExecTarget" Value="[#SolvecraftExe]" />
|
||||
<!-- Runs in the wizard's own (unelevated) process, so the app starts as the user. -->
|
||||
<CustomAction Id="LaunchSolvecraft" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixShellExec" Impersonate="yes" Return="ignore" />
|
||||
</Package>
|
||||
|
||||
<Fragment>
|
||||
<ComponentGroup Id="SolvecraftFiles" Directory="INSTALLFOLDER">
|
||||
<Component Id="SolvecraftApp" Guid="290BEF27-C200-4069-AA43-7DE6A39CDB0A">
|
||||
<File Id="SolvecraftExe" Source="$(var.BinDir)\solvecraft.exe" KeyPath="yes">
|
||||
<Shortcut Id="StartMenuShortcut" Directory="ProgramMenuFolder" Name="SolveCraft"
|
||||
Description="SolveCraft parametric 3D CAD" WorkingDirectory="INSTALLFOLDER" Advertise="yes"
|
||||
Icon="SolvecraftIcon.exe" IconIndex="0" />
|
||||
</File>
|
||||
|
||||
<!-- .solvecraft: SolveCraft's own format; SolveCraft is its default app. -->
|
||||
<ProgId Id="Solvecraft.Design" Description="SolveCraft Design" Icon="SolvecraftExe" IconIndex="0">
|
||||
<Extension Id="solvecraft" ContentType="application/x-solvecraft">
|
||||
<Verb Id="open" Command="Open" TargetFile="SolvecraftExe" Argument=""%1"" />
|
||||
</Extension>
|
||||
</ProgId>
|
||||
<!-- STEP, IGES and meshes: offered under "Open with", never forced as the default. -->
|
||||
<ProgId Id="Solvecraft.Model" Description="3D model" Icon="SolvecraftExe" IconIndex="0" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\Solvecraft.Model\shell\open\command" Value=""[#SolvecraftExe]" "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\.step\OpenWithProgids" Name="Solvecraft.Model" Value="" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\.stp\OpenWithProgids" Name="Solvecraft.Model" Value="" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\.igs\OpenWithProgids" Name="Solvecraft.Model" Value="" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\.iges\OpenWithProgids" Name="Solvecraft.Model" Value="" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\.3mf\OpenWithProgids" Name="Solvecraft.Model" Value="" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\.stl\OpenWithProgids" Name="Solvecraft.Model" Value="" Type="string" />
|
||||
|
||||
<!-- "Open with" app list and the Run dialog (Win+R "solvecraft"). -->
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\solvecraft.exe" Name="FriendlyAppName" Value="SolveCraft" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\solvecraft.exe\shell\open\command" Value=""[#SolvecraftExe]" "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\solvecraft.exe\SupportedTypes" Name=".solvecraft" Value="" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\solvecraft.exe\SupportedTypes" Name=".step" Value="" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\solvecraft.exe\SupportedTypes" Name=".stp" Value="" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\solvecraft.exe\SupportedTypes" Name=".igs" Value="" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\solvecraft.exe\SupportedTypes" Name=".iges" Value="" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\solvecraft.exe\SupportedTypes" Name=".3mf" Value="" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\solvecraft.exe\SupportedTypes" Name=".stl" Value="" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\App Paths\solvecraft.exe" Value="[#SolvecraftExe]" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\SolveCraft" Name="InstallDir" Value="[INSTALLFOLDER]" Type="string" />
|
||||
</Component>
|
||||
|
||||
<Component Id="SolvecraftCli">
|
||||
<File Id="SolvecraftCliExe" Source="$(var.BinDir)\solvecraft-cli.exe" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="SolvecraftLicences">
|
||||
<File Id="LicenseMit" Source="$(var.BinDir)\LICENSE-MIT" KeyPath="yes" />
|
||||
<File Id="LicenseApache" Source="$(var.BinDir)\LICENSE-APACHE" />
|
||||
<File Id="Notice" Source="$(var.BinDir)\NOTICE" />
|
||||
<File Id="ThirdParty" Source="$(var.BinDir)\THIRD-PARTY-LICENSES.txt" />
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
Reference in new issue
Block a user