The bundled binaries need LD_LIBRARY_PATH, PYTHONPATH, and QT_PLUGIN_PATH set to find their libraries. Replace symlinks with wrapper scripts that set up the environment before exec.
334 lines
12 KiB
Bash
Executable File
334 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
# Build a .deb package from the installed build directory
|
|
#
|
|
# This script creates a Debian binary package following Debian Policy Manual guidelines.
|
|
# See: https://www.debian.org/doc/debian-policy/
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
|
|
|
# Parse arguments
|
|
INSTALL_DIR="${1:-}"
|
|
OUTPUT_DIR="${2:-}"
|
|
VERSION="${3:-}"
|
|
|
|
if [ -z "$INSTALL_DIR" ] || [ -z "$OUTPUT_DIR" ]; then
|
|
echo "Usage: $0 <install-dir> <output-dir> [version]"
|
|
echo " install-dir: Directory containing installed build (e.g., build/release/install)"
|
|
echo " output-dir: Directory to write the .deb file"
|
|
echo " version: Package version (default: from git describe)"
|
|
exit 1
|
|
fi
|
|
|
|
# Validate install directory exists
|
|
if [ ! -d "$INSTALL_DIR" ]; then
|
|
echo "Error: Install directory does not exist: $INSTALL_DIR"
|
|
exit 1
|
|
fi
|
|
|
|
# Get version from git if not provided
|
|
if [ -z "$VERSION" ]; then
|
|
VERSION=$(cd "$PROJECT_ROOT" && git describe --tags --always 2>/dev/null || echo "0.1.0")
|
|
fi
|
|
|
|
# Convert version to Debian-compatible format
|
|
# Debian Policy Manual 5.6.12: version must start with a digit
|
|
# Format: [epoch:]upstream_version[-debian_revision]
|
|
convert_to_debian_version() {
|
|
local ver="$1"
|
|
|
|
# Remove leading 'v' if present (common in git tags)
|
|
ver="${ver#v}"
|
|
|
|
# Replace hyphens with dots (hyphens have special meaning in Debian versions)
|
|
ver="${ver//-/.}"
|
|
|
|
# If version starts with a letter (e.g., 'weekly'), prefix with '0~'
|
|
# The ~ character sorts before anything else, ensuring:
|
|
# 0~weekly.2025.01.01 < 0.1.0 < 1.0.0
|
|
if [[ "$ver" =~ ^[a-zA-Z] ]]; then
|
|
ver="0~${ver}"
|
|
fi
|
|
|
|
echo "$ver"
|
|
}
|
|
|
|
VERSION=$(convert_to_debian_version "$VERSION")
|
|
|
|
PACKAGE_NAME="kindred-create"
|
|
ARCH="amd64"
|
|
# Debian package naming: <name>_<version>_<arch>.deb
|
|
DEB_NAME="${PACKAGE_NAME}_${VERSION}_${ARCH}"
|
|
|
|
echo "========================================"
|
|
echo "Building Debian package"
|
|
echo "========================================"
|
|
echo " Package: ${PACKAGE_NAME}"
|
|
echo " Version: ${VERSION}"
|
|
echo " Arch: ${ARCH}"
|
|
echo " Source: ${INSTALL_DIR}"
|
|
echo " Output: ${OUTPUT_DIR}/${DEB_NAME}.deb"
|
|
echo "========================================"
|
|
|
|
# Create staging directory
|
|
STAGING_DIR=$(mktemp -d)
|
|
trap "rm -rf ${STAGING_DIR}" EXIT
|
|
|
|
# Create debian package structure following Filesystem Hierarchy Standard
|
|
mkdir -p "${STAGING_DIR}/DEBIAN"
|
|
mkdir -p "${STAGING_DIR}/opt/${PACKAGE_NAME}"
|
|
mkdir -p "${STAGING_DIR}/usr/bin"
|
|
mkdir -p "${STAGING_DIR}/usr/share/applications"
|
|
mkdir -p "${STAGING_DIR}/usr/share/icons/hicolor/scalable/apps"
|
|
mkdir -p "${STAGING_DIR}/usr/share/icons/hicolor/48x48/apps"
|
|
mkdir -p "${STAGING_DIR}/usr/share/icons/hicolor/256x256/apps"
|
|
mkdir -p "${STAGING_DIR}/usr/share/mime/packages"
|
|
mkdir -p "${STAGING_DIR}/usr/share/metainfo"
|
|
mkdir -p "${STAGING_DIR}/usr/share/doc/${PACKAGE_NAME}"
|
|
|
|
# Copy installed files
|
|
echo "Copying installed files..."
|
|
cp -a "${INSTALL_DIR}"/* "${STAGING_DIR}/opt/${PACKAGE_NAME}/"
|
|
|
|
# Create wrapper scripts in /usr/bin that set up the environment
|
|
# The binaries need LD_LIBRARY_PATH to find bundled libraries
|
|
cat > "${STAGING_DIR}/usr/bin/kindred-create" << 'WRAPPER'
|
|
#!/bin/bash
|
|
export KINDRED_CREATE_HOME="/opt/kindred-create"
|
|
export LD_LIBRARY_PATH="${KINDRED_CREATE_HOME}/lib:${LD_LIBRARY_PATH:-}"
|
|
export QT_PLUGIN_PATH="${KINDRED_CREATE_HOME}/lib/qt6/plugins:${QT_PLUGIN_PATH:-}"
|
|
export PYTHONHOME="${KINDRED_CREATE_HOME}"
|
|
export PYTHONPATH="${KINDRED_CREATE_HOME}/lib/python3.11:${KINDRED_CREATE_HOME}/lib/python3.11/site-packages:${PYTHONPATH:-}"
|
|
exec "${KINDRED_CREATE_HOME}/bin/FreeCAD" "$@"
|
|
WRAPPER
|
|
chmod 755 "${STAGING_DIR}/usr/bin/kindred-create"
|
|
|
|
cat > "${STAGING_DIR}/usr/bin/kindred-create-cmd" << 'WRAPPER'
|
|
#!/bin/bash
|
|
export KINDRED_CREATE_HOME="/opt/kindred-create"
|
|
export LD_LIBRARY_PATH="${KINDRED_CREATE_HOME}/lib:${LD_LIBRARY_PATH:-}"
|
|
export PYTHONHOME="${KINDRED_CREATE_HOME}"
|
|
export PYTHONPATH="${KINDRED_CREATE_HOME}/lib/python3.11:${KINDRED_CREATE_HOME}/lib/python3.11/site-packages:${PYTHONPATH:-}"
|
|
exec "${KINDRED_CREATE_HOME}/bin/FreeCADCmd" "$@"
|
|
WRAPPER
|
|
chmod 755 "${STAGING_DIR}/usr/bin/kindred-create-cmd"
|
|
|
|
# Create desktop entry following freedesktop.org specification
|
|
cat > "${STAGING_DIR}/usr/share/applications/kindred-create.desktop" << 'EOF'
|
|
[Desktop Entry]
|
|
Version=1.1
|
|
Type=Application
|
|
Name=Kindred Create
|
|
GenericName=CAD Application
|
|
Comment=Engineering-focused parametric 3D CAD platform
|
|
Exec=/usr/bin/kindred-create %F
|
|
Icon=kindred-create
|
|
Terminal=false
|
|
Categories=Graphics;Science;Engineering;
|
|
MimeType=application/x-extension-fcstd;application/x-freecad;
|
|
Keywords=CAD;3D;modeling;engineering;design;parametric;FreeCAD;
|
|
StartupNotify=true
|
|
StartupWMClass=FreeCAD
|
|
EOF
|
|
|
|
# Copy or create icon files
|
|
if [ -f "${INSTALL_DIR}/share/icons/hicolor/scalable/apps/org.freecad.FreeCAD.svg" ]; then
|
|
cp "${INSTALL_DIR}/share/icons/hicolor/scalable/apps/org.freecad.FreeCAD.svg" \
|
|
"${STAGING_DIR}/usr/share/icons/hicolor/scalable/apps/kindred-create.svg"
|
|
elif [ -f "${PROJECT_ROOT}/resources/branding/kindred-create.svg" ]; then
|
|
cp "${PROJECT_ROOT}/resources/branding/kindred-create.svg" \
|
|
"${STAGING_DIR}/usr/share/icons/hicolor/scalable/apps/kindred-create.svg"
|
|
fi
|
|
|
|
# Copy PNG icons if available
|
|
for size in 48 256; do
|
|
if [ -f "${INSTALL_DIR}/share/icons/hicolor/${size}x${size}/apps/org.freecad.FreeCAD.png" ]; then
|
|
cp "${INSTALL_DIR}/share/icons/hicolor/${size}x${size}/apps/org.freecad.FreeCAD.png" \
|
|
"${STAGING_DIR}/usr/share/icons/hicolor/${size}x${size}/apps/kindred-create.png"
|
|
fi
|
|
done
|
|
|
|
# Create MIME type definition for .fcstd files
|
|
cat > "${STAGING_DIR}/usr/share/mime/packages/kindred-create.xml" << 'EOF'
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
|
<mime-type type="application/x-freecad">
|
|
<comment>FreeCAD Document</comment>
|
|
<glob pattern="*.fcstd"/>
|
|
<glob pattern="*.FCStd"/>
|
|
<icon name="kindred-create"/>
|
|
</mime-type>
|
|
</mime-info>
|
|
EOF
|
|
|
|
# Create AppStream metainfo for software centers
|
|
cat > "${STAGING_DIR}/usr/share/metainfo/kindred-create.metainfo.xml" << EOF
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<component type="desktop-application">
|
|
<id>kindred-create</id>
|
|
<name>Kindred Create</name>
|
|
<summary>Engineering-focused parametric 3D CAD platform</summary>
|
|
<metadata_license>CC0-1.0</metadata_license>
|
|
<project_license>LGPL-2.1-or-later</project_license>
|
|
<description>
|
|
<p>
|
|
Kindred Create is an engineering-focused parametric 3D CAD platform
|
|
built on FreeCAD 1.0+. It provides a streamlined interface for
|
|
mechanical engineering and product design.
|
|
</p>
|
|
<p>Features include:</p>
|
|
<ul>
|
|
<li>Parametric modeling with constraint-based sketcher</li>
|
|
<li>Part Design workbench for solid modeling</li>
|
|
<li>Assembly workbench for multi-part designs</li>
|
|
<li>TechDraw workbench for 2D technical drawings</li>
|
|
<li>FEM workbench for finite element analysis</li>
|
|
<li>CAM workbench for CNC machining</li>
|
|
</ul>
|
|
</description>
|
|
<launchable type="desktop-id">kindred-create.desktop</launchable>
|
|
<url type="homepage">https://gitea.kindred.internal/kindred/create</url>
|
|
<url type="bugtracker">https://gitea.kindred.internal/kindred/create/issues</url>
|
|
<provides>
|
|
<binary>kindred-create</binary>
|
|
<binary>kindred-create-cmd</binary>
|
|
</provides>
|
|
<content_rating type="oars-1.1"/>
|
|
<releases>
|
|
<release version="${VERSION}" date="$(date +%Y-%m-%d)"/>
|
|
</releases>
|
|
</component>
|
|
EOF
|
|
|
|
# Create basic copyright file (required by Debian policy)
|
|
cat > "${STAGING_DIR}/usr/share/doc/${PACKAGE_NAME}/copyright" << 'EOF'
|
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
|
Upstream-Name: Kindred Create
|
|
Upstream-Contact: Kindred Team <team@kindred.internal>
|
|
Source: https://gitea.kindred.internal/kindred/create
|
|
|
|
Files: *
|
|
Copyright: 2024-2026 Kindred
|
|
2001-2024 FreeCAD Contributors
|
|
License: LGPL-2.1-or-later
|
|
|
|
License: LGPL-2.1-or-later
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
(at your option) any later version.
|
|
.
|
|
On Debian systems, the complete text of the GNU Lesser General Public
|
|
License can be found in `/usr/share/common-licenses/LGPL-2.1'.
|
|
EOF
|
|
|
|
# Generate control file with actual version
|
|
sed "s/\${VERSION}/${VERSION}/" "${SCRIPT_DIR}/control" > "${STAGING_DIR}/DEBIAN/control"
|
|
|
|
# Calculate and append installed size (in KiB, as required by Debian policy)
|
|
INSTALLED_SIZE=$(du -sk "${STAGING_DIR}" | cut -f1)
|
|
echo "Installed-Size: ${INSTALLED_SIZE}" >> "${STAGING_DIR}/DEBIAN/control"
|
|
|
|
# Create postinst script for post-installation setup
|
|
cat > "${STAGING_DIR}/DEBIAN/postinst" << 'EOF'
|
|
#!/bin/sh
|
|
set -e
|
|
|
|
case "$1" in
|
|
configure)
|
|
# Update desktop database
|
|
if command -v update-desktop-database > /dev/null 2>&1; then
|
|
update-desktop-database -q /usr/share/applications 2>/dev/null || true
|
|
fi
|
|
|
|
# Update icon cache
|
|
if command -v gtk-update-icon-cache > /dev/null 2>&1; then
|
|
gtk-update-icon-cache -q -t -f /usr/share/icons/hicolor 2>/dev/null || true
|
|
fi
|
|
|
|
# Update MIME database
|
|
if command -v update-mime-database > /dev/null 2>&1; then
|
|
update-mime-database /usr/share/mime 2>/dev/null || true
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
exit 0
|
|
EOF
|
|
chmod 755 "${STAGING_DIR}/DEBIAN/postinst"
|
|
|
|
# Create postrm script for post-removal cleanup
|
|
cat > "${STAGING_DIR}/DEBIAN/postrm" << 'EOF'
|
|
#!/bin/sh
|
|
set -e
|
|
|
|
case "$1" in
|
|
remove|purge)
|
|
# Update desktop database
|
|
if command -v update-desktop-database > /dev/null 2>&1; then
|
|
update-desktop-database -q /usr/share/applications 2>/dev/null || true
|
|
fi
|
|
|
|
# Update icon cache
|
|
if command -v gtk-update-icon-cache > /dev/null 2>&1; then
|
|
gtk-update-icon-cache -q -t -f /usr/share/icons/hicolor 2>/dev/null || true
|
|
fi
|
|
|
|
# Update MIME database
|
|
if command -v update-mime-database > /dev/null 2>&1; then
|
|
update-mime-database /usr/share/mime 2>/dev/null || true
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
exit 0
|
|
EOF
|
|
chmod 755 "${STAGING_DIR}/DEBIAN/postrm"
|
|
|
|
# Set proper permissions following Debian policy
|
|
# Directories: 755, Files: 644, Executables: 755
|
|
echo "Setting file permissions..."
|
|
find "${STAGING_DIR}" -type d -exec chmod 755 {} \;
|
|
find "${STAGING_DIR}/opt" -type f -exec chmod 644 {} \;
|
|
|
|
# Make binaries executable
|
|
if [ -d "${STAGING_DIR}/opt/${PACKAGE_NAME}/bin" ]; then
|
|
find "${STAGING_DIR}/opt/${PACKAGE_NAME}/bin" -type f -exec chmod 755 {} \;
|
|
fi
|
|
|
|
# Make shared libraries executable (required for proper loading)
|
|
find "${STAGING_DIR}/opt/${PACKAGE_NAME}" -name "*.so" -type f -exec chmod 755 {} \;
|
|
find "${STAGING_DIR}/opt/${PACKAGE_NAME}" -name "*.so.*" -type f -exec chmod 755 {} \;
|
|
|
|
# Ensure DEBIAN scripts have correct permissions (must be 0555 to 0775)
|
|
chmod 755 "${STAGING_DIR}/DEBIAN/postinst"
|
|
chmod 755 "${STAGING_DIR}/DEBIAN/postrm"
|
|
|
|
# Build the .deb package
|
|
# --root-owner-group ensures all files are owned by root:root (standard practice)
|
|
mkdir -p "${OUTPUT_DIR}"
|
|
echo "Building package..."
|
|
dpkg-deb --build --root-owner-group "${STAGING_DIR}" "${OUTPUT_DIR}/${DEB_NAME}.deb"
|
|
|
|
# Verify the package
|
|
echo "Verifying package..."
|
|
dpkg-deb --info "${OUTPUT_DIR}/${DEB_NAME}.deb"
|
|
|
|
# Generate checksums
|
|
cd "${OUTPUT_DIR}"
|
|
sha256sum "${DEB_NAME}.deb" > "${DEB_NAME}.deb.sha256"
|
|
|
|
echo ""
|
|
echo "========================================"
|
|
echo "Package built successfully!"
|
|
echo "========================================"
|
|
echo " File: ${OUTPUT_DIR}/${DEB_NAME}.deb"
|
|
echo " Size: $(ls -lh "${DEB_NAME}.deb" | awk '{print $5}')"
|
|
echo " SHA256: $(cat "${DEB_NAME}.deb.sha256" | cut -d' ' -f1)"
|
|
echo "========================================"
|
|
echo ""
|
|
echo "Install with: sudo dpkg -i ${DEB_NAME}.deb"
|
|
echo " sudo apt-get install -f # to resolve dependencies"
|