app: add code for macOS and Windows apps under 'app' (#12933)

* app: add code for macOS and Windows apps under 'app'

* app: add readme

* app: windows and linux only for now

* ci: fix ui CI validation

---------

Co-authored-by: jmorganca <jmorganca@gmail.com>
This commit is contained in:
Daniel Hiltgen
2025-11-04 11:40:17 -08:00
committed by GitHub
parent a4770107a6
commit d3b4b9970a
212 changed files with 102976 additions and 1482 deletions

View File

@@ -0,0 +1,2 @@
This is just a dummy file so create-dmg can tell whether it's being run from
inside the Git repo or from an installed location.

View File

@@ -1,16 +1,33 @@
#!/bin/sh
# Note:
# While testing, if you double-click on the Ollama.app
# some state is left on MacOS and subsequent attempts
# to build again will fail with:
#
# hdiutil: create failed - Operation not permitted
#
# To work around, specify another volume name with:
#
# VOL_NAME="$(date)" ./scripts/build_darwin.sh
#
VOL_NAME=${VOL_NAME:-"Ollama"}
export VERSION=${VERSION:-$(git describe --tags --first-parent --abbrev=7 --long --dirty --always | sed -e "s/^v//g")}
export GOFLAGS="'-ldflags=-w -s \"-X=github.com/ollama/ollama/version.Version=${VERSION#v}\" \"-X=github.com/ollama/ollama/server.mode=release\"'"
export CGO_CFLAGS="-mmacosx-version-min=14.0"
export CGO_CXXFLAGS="-mmacosx-version-min=14.0"
export CGO_LDFLAGS="-mmacosx-version-min=14.0"
set -e
status() { echo >&2 ">>> $@"; }
usage() {
echo "usage: $(basename $0) [build [sign]]"
echo "usage: $(basename $0) [build app [sign]]"
exit 1
}
export VERSION=${VERSION:-$(git describe --tags --first-parent --abbrev=7 --long --dirty --always | sed -e "s/^v//g")}
export GOFLAGS="'-ldflags=-w -s \"-X=github.com/ollama/ollama/version.Version=${VERSION#v}\" \"-X=github.com/ollama/ollama/server.mode=release\"'"
export CGO_CPPFLAGS='-mmacosx-version-min=11.3'
mkdir -p dist
ARCHS="arm64 amd64"
while getopts "a:h" OPTION; do
@@ -65,15 +82,106 @@ _sign_darwin() {
}
_build_macapp() {
# build and optionally sign the mac app
npm install --prefix macapp
if [ -n "$APPLE_IDENTITY" ]; then
npm run --prefix macapp make:sign
else
npm run --prefix macapp make
if ! command -v npm &> /dev/null; then
echo "npm is not installed. Please install Node.js and npm first:"
echo " Visit: https://nodejs.org/"
exit 1
fi
mv ./macapp/out/make/zip/darwin/universal/Ollama-darwin-universal-$VERSION.zip dist/Ollama-darwin.zip
if ! command -v tsc &> /dev/null; then
echo "Installing TypeScript compiler..."
npm install -g typescript
fi
echo "Installing required Go tools..."
cd app/ui/app
npm install
npm run build
cd ../../..
# Build the Ollama.app bundle
rm -rf dist/Ollama.app
cp -a ./app/darwin/Ollama.app dist/Ollama.app
# update the modified date of the app bundle to now
touch dist/Ollama.app
go clean -cache
GOARCH=amd64 CGO_ENABLED=1 GOOS=darwin go build -o dist/darwin-app-amd64 -ldflags="-s -w -X=github.com/ollama/ollama/app/version.Version=${VERSION}" ./app/cmd/app
GOARCH=arm64 CGO_ENABLED=1 GOOS=darwin go build -o dist/darwin-app-arm64 -ldflags="-s -w -X=github.com/ollama/ollama/app/version.Version=${VERSION}" ./app/cmd/app
mkdir -p dist/Ollama.app/Contents/MacOS
lipo -create -output dist/Ollama.app/Contents/MacOS/Ollama dist/darwin-app-amd64 dist/darwin-app-arm64
rm -f dist/darwin-app-amd64 dist/darwin-app-arm64
# Create a mock Squirrel.framework bundle
mkdir -p dist/Ollama.app/Contents/Frameworks/Squirrel.framework/Versions/A/Resources/
cp -a dist/Ollama.app/Contents/MacOS/Ollama dist/Ollama.app/Contents/Frameworks/Squirrel.framework/Versions/A/Squirrel
ln -s ../Squirrel dist/Ollama.app/Contents/Frameworks/Squirrel.framework/Versions/A/Resources/ShipIt
cp -a ./app/cmd/squirrel/Info.plist dist/Ollama.app/Contents/Frameworks/Squirrel.framework/Versions/A/Resources/Info.plist
ln -s A dist/Ollama.app/Contents/Frameworks/Squirrel.framework/Versions/Current
ln -s Versions/Current/Resources dist/Ollama.app/Contents/Frameworks/Squirrel.framework/Resources
ln -s Versions/Current/Squirrel dist/Ollama.app/Contents/Frameworks/Squirrel.framework/Squirrel
# Update the version in the Info.plist
plutil -replace CFBundleShortVersionString -string "$VERSION" dist/Ollama.app/Contents/Info.plist
plutil -replace CFBundleVersion -string "$VERSION" dist/Ollama.app/Contents/Info.plist
# Setup the ollama binaries
mkdir -p dist/Ollama.app/Contents/Resources
if [ -d dist/darwin-amd64 ]; then
lipo -create -output dist/Ollama.app/Contents/Resources/ollama dist/darwin-amd64/ollama dist/darwin-arm64/ollama
cp dist/darwin-amd64/lib/ollama/*.so dist/darwin-amd64/lib/ollama/*.dylib dist/Ollama.app/Contents/Resources/
else
cp -a dist/darwin/ollama dist/Ollama.app/Contents/Resources/ollama
cp dist/darwin/*.so dist/darwin/*.dylib dist/Ollama.app/Contents/Resources/
fi
chmod a+x dist/Ollama.app/Contents/Resources/ollama
# Sign
if [ -n "$APPLE_IDENTITY" ]; then
codesign -f --timestamp -s "$APPLE_IDENTITY" --identifier ai.ollama.ollama --options=runtime dist/Ollama.app/Contents/Resources/ollama
for lib in dist/Ollama.app/Contents/Resources/*.so dist/Ollama.app/Contents/Resources/*.dylib ; do
codesign -f --timestamp -s "$APPLE_IDENTITY" --identifier ai.ollama.ollama --options=runtime ${lib}
done
codesign -f --timestamp -s "$APPLE_IDENTITY" --identifier com.electron.ollama --deep --options=runtime dist/Ollama.app
fi
rm -f dist/Ollama-darwin.zip
ditto -c -k --keepParent dist/Ollama.app dist/Ollama-darwin.zip
(cd dist/Ollama.app/Contents/Resources/; tar -cf - ollama *.so *.dylib) | gzip -9vc > dist/ollama-darwin.tgz
# Notarize and Staple
if [ -n "$APPLE_IDENTITY" ]; then
$(xcrun -f notarytool) submit dist/Ollama-darwin.zip --wait --timeout 10m --apple-id "$APPLE_ID" --password "$APPLE_PASSWORD" --team-id "$APPLE_TEAM_ID"
rm -f dist/Ollama-darwin.zip
$(xcrun -f stapler) staple dist/Ollama.app
ditto -c -k --keepParent dist/Ollama.app dist/Ollama-darwin.zip
rm -f dist/Ollama.dmg
(cd dist && ../scripts/create-dmg.sh \
--volname "${VOL_NAME}" \
--volicon ../app/darwin/Ollama.app/Contents/Resources/icon.icns \
--background ../app/assets/background.png \
--window-pos 200 120 \
--window-size 800 400 \
--icon-size 128 \
--icon "Ollama.app" 200 190 \
--hide-extension "Ollama.app" \
--app-drop-link 600 190 \
--text-size 12 \
"Ollama.dmg" \
"Ollama.app" \
; )
rm -f dist/rw*.dmg
codesign -f --timestamp -s "$APPLE_IDENTITY" --identifier ai.ollama.ollama --options=runtime dist/Ollama.dmg
$(xcrun -f notarytool) submit dist/Ollama.dmg --wait --timeout 10m --apple-id "$APPLE_ID" --password "$APPLE_PASSWORD" --team-id "$APPLE_TEAM_ID"
$(xcrun -f stapler) staple dist/Ollama.dmg
else
echo "WARNING: Code signing disabled, this bundle will not work for upgrade testing"
fi
}
if [ "$#" -eq 0 ]; then
@@ -87,7 +195,7 @@ for CMD in "$@"; do
case $CMD in
build) _build_darwin ;;
sign) _sign_darwin ;;
macapp) _build_macapp ;;
app) _build_macapp ;;
*) usage ;;
esac
done

View File

@@ -6,7 +6,9 @@
$ErrorActionPreference = "Stop"
function checkEnv() {
mkdir -Force -path .\dist | Out-Null
function checkEnv {
if ($null -ne $env:ARCH ) {
$script:ARCH = $env:ARCH
} else {
@@ -22,10 +24,7 @@ function checkEnv() {
Write-host "Building for ${script:TARGET_ARCH}"
write-host "Locating required tools and paths"
$script:SRC_DIR=$PWD
if ($null -eq $env:VCToolsRedistDir) {
$MSVC_INSTALL=(Get-CimInstance MSFT_VSInstance -Namespace root/cimv2/vs)[0].InstallLocation
$env:VCToolsRedistDir=(get-item "${MSVC_INSTALL}\VC\Redist\MSVC\*")[0]
}
# Locate CUDA versions
$cudaList=(get-item "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v*\bin\" -ea 'silentlycontinue')
if ($cudaList.length -eq 0) {
@@ -34,7 +33,20 @@ function checkEnv() {
$script:CUDA_DIRS=@($d| split-path -parent)
}
} else {
$script:CUDA_DIRS=$cudaList
# Favor newer patch versions if available
$script:CUDA_DIRS=($cudaList | sort-object -Descending)
}
if ($script:CUDA_DIRS.length -gt 0) {
write-host "Available CUDA Versions: $script:CUDA_DIRS"
} else {
write-host "No CUDA versions detected"
}
# Locate ROCm version
if ($null -ne $env:HIP_PATH) {
$script:HIP_PATH=$env:HIP_PATH
} else {
$script:HIP_PATH=(get-item "C:\Program Files\AMD\ROCm\*\bin\" -ea 'silentlycontinue' | sort-object -Descending)
}
$inoSetup=(get-item "C:\Program Files*\Inno Setup*\")
@@ -74,12 +86,12 @@ function checkEnv() {
} else {
write-host "Code signing disabled - please set KEY_CONTAINERS to sign and copy ollama_inc.crt to the top of the source tree"
}
$script:JOBS=((Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors)
$script:JOBS=([Environment]::ProcessorCount)
}
function buildCPU() {
mkdir -Force -path "${script:DIST_DIR}\"
function cpu {
mkdir -Force -path "${script:DIST_DIR}\" | Out-Null
if ($script:ARCH -ne "arm64") {
Remove-Item -ea 0 -recurse -force -path "${script:SRC_DIR}\dist\windows-${script:ARCH}"
New-Item "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" -ItemType Directory -ea 0
@@ -93,79 +105,89 @@ function buildCPU() {
}
}
function buildCUDA11() {
function cuda11 {
# CUDA v11 claims to be compatible with MSVC 2022, but the latest updates are no longer compatible
# 19.40 is the last compiler version that works, but recent udpates are 19.43
# So this pins to MSVC 2019 for best compatibility
mkdir -Force -path "${script:DIST_DIR}\"
mkdir -Force -path "${script:DIST_DIR}\" | Out-Null
$cudaMajorVer="11"
if ($script:ARCH -ne "arm64") {
$hashEnv = @{}
Get-ChildItem env: | foreach { $hashEnv[$_.Name] = $_.Value }
if ("$script:CUDA_DIRS".Contains("v11")) {
$hashEnv.Keys | foreach { if ($_.Contains("CUDA_PATH_V11")) { $x=$hashEnv[$_]; if (test-path -literalpath "$x\bin\nvcc.exe" ) { $cuda=$x} }}
write-host "Building CUDA v11 backend libraries $cuda"
if ("$script:CUDA_DIRS".Contains("v$cudaMajorVer")) {
foreach ($d in $Script:CUDA_DIRS){
if ($d.FullName.Contains("v$cudaMajorVer")) {
if (test-path -literalpath (join-path -path $d -childpath "nvcc.exe" ) ) {
$cuda=($d.FullName|split-path -parent)
break
}
}
}
write-host "Building CUDA v$cudaMajorVer backend libraries $cuda"
$env:CUDAToolkit_ROOT=$cuda
& cmake -B build\cuda_v11 --preset "CUDA 11" -T cuda="$cuda" -DCMAKE_CUDA_COMPILER="$cuda\bin\nvcc.exe" -G "Visual Studio 16 2019" --install-prefix $script:DIST_DIR -DOLLAMA_RUNNER_DIR="cuda_v11"
& cmake -B build\cuda_v$cudaMajorVer --preset "CUDA $cudaMajorVer" -T cuda="$cuda" -DCMAKE_CUDA_COMPILER="$cuda\bin\nvcc.exe" -G "Visual Studio 16 2019" --install-prefix "$script:DIST_DIR"
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
& cmake --build build\cuda_v11 --target ggml-cuda --config Release --parallel $script:JOBS
& cmake --build build\cuda_v$cudaMajorVer --target ggml-cuda --config Release --parallel $script:JOBS
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
& cmake --install build\cuda_v11 --component "CUDA" --strip
& cmake --install build\cuda_v$cudaMajorVer --component "CUDA" --strip
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
} else {
write-host "CUDA v$cudaMajorVer not detected, skipping"
}
} else {
write-host "not arch we wanted"
}
write-host "done"
}
function cudaCommon {
param (
[string]$cudaMajorVer
)
mkdir -Force -path "${script:DIST_DIR}\" | Out-Null
if ($script:ARCH -ne "arm64") {
if ("$script:CUDA_DIRS".Contains("v$cudaMajorVer")) {
foreach ($d in $Script:CUDA_DIRS){
if ($d.FullName.Contains("v$cudaMajorVer")) {
if (test-path -literalpath (join-path -path $d -childpath "nvcc.exe" ) ) {
$cuda=($d.FullName|split-path -parent)
break
}
}
}
write-host "Building CUDA v$cudaMajorVer backend libraries $cuda"
$env:CUDAToolkit_ROOT=$cuda
& cmake -B build\cuda_v$cudaMajorVer --preset "CUDA $cudaMajorVer" -T cuda="$cuda" --install-prefix "$script:DIST_DIR"
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
& cmake --build build\cuda_v$cudaMajorVer --target ggml-cuda --config Release --parallel $script:JOBS
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
& cmake --install build\cuda_v$cudaMajorVer --component "CUDA" --strip
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
} else {
write-host "CUDA v$cudaMajorVer not detected, skipping"
}
}
}
function buildCUDA12() {
mkdir -Force -path "${script:DIST_DIR}\"
if ($script:ARCH -ne "arm64") {
$hashEnv = @{}
Get-ChildItem env: | foreach { $hashEnv[$_.Name] = $_.Value }
if ("$script:CUDA_DIRS".Contains("v12.8")) {
$hashEnv.Keys | foreach { if ($_.Contains("CUDA_PATH_V12_8")) { $x=$hashEnv[$_]; if (test-path -literalpath "$x\bin\nvcc.exe" ) { $cuda=$x} }}
write-host "Building CUDA v12 backend libraries $cuda"
$env:CUDAToolkit_ROOT=$cuda
& cmake -B build\cuda_v12 --preset "CUDA 12" -T cuda="$cuda" --install-prefix $script:DIST_DIR -DOLLAMA_RUNNER_DIR="cuda_v12"
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
& cmake --build build\cuda_v12 --target ggml-cuda --config Release --parallel $script:JOBS
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
& cmake --install build\cuda_v12 --component "CUDA" --strip
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
}
}
function cuda12 {
cudaCommon("12")
}
function buildCUDA13() {
mkdir -Force -path "${script:DIST_DIR}\"
if ($script:ARCH -ne "arm64") {
$hashEnv = @{}
Get-ChildItem env: | foreach { $hashEnv[$_.Name] = $_.Value }
if ("$script:CUDA_DIRS".Contains("v13")) {
$hashEnv.Keys | foreach { if ($_.Contains("CUDA_PATH_V13")) { $x=$hashEnv[$_]; if (test-path -literalpath "$x\bin\nvcc.exe" ) { $cuda=$x} }}
$env:CUDAToolkit_ROOT=$cuda
write-host "Building CUDA v13 backend libraries $cuda"
& cmake -B build\cuda_v13 --preset "CUDA 13" -T cuda="$cuda" --install-prefix $script:DIST_DIR -DOLLAMA_RUNNER_DIR="cuda_v13"
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
& cmake --build build\cuda_v13 --target ggml-cuda --config Release --parallel $script:JOBS
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
& cmake --install build\cuda_v13 --component "CUDA" --strip
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
}
}
function cuda13 {
cudaCommon("13")
}
function buildROCm() {
mkdir -Force -path "${script:DIST_DIR}\"
function rocm {
mkdir -Force -path "${script:DIST_DIR}\" | Out-Null
if ($script:ARCH -ne "arm64") {
if ($env:HIP_PATH) {
write-host "Building ROCm backend libraries"
if ($script:HIP_PATH) {
write-host "Building ROCm backend libraries $script:HIP_PATH"
if (-Not (get-command -ErrorAction silent ninja)) {
$NINJA_DIR=(gci -path (Get-CimInstance MSFT_VSInstance -Namespace root/cimv2/vs)[0].InstallLocation -r -fi ninja.exe).Directory.FullName
$env:PATH="$NINJA_DIR;$env:PATH"
}
$env:HIPCXX="${env:HIP_PATH}\bin\clang++.exe"
$env:HIPCXX="${script:HIP_PATH}\bin\clang++.exe"
$env:HIP_PLATFORM="amd"
$env:CMAKE_PREFIX_PATH="${env:HIP_PATH}"
& cmake --fresh -B build\rocm --preset "ROCm 6" -G Ninja -DOLLAMA_RUNNER_DIR="rocm" `
$env:CMAKE_PREFIX_PATH="${script:HIP_PATH}"
& cmake -B build\rocm --preset "ROCm 6" -G Ninja `
-DCMAKE_C_COMPILER=clang `
-DCMAKE_CXX_COMPILER=clang++ `
-DCMAKE_C_FLAGS="-parallel-jobs=4 -Wno-ignored-attributes -Wno-deprecated-pragma" `
@@ -180,80 +202,104 @@ function buildROCm() {
& cmake --install build\rocm --component "HIP" --strip
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
Remove-Item -Path $script:DIST_DIR\lib\ollama\rocm\rocblas\library\*gfx906* -ErrorAction SilentlyContinue
} else {
write-host "ROCm not detected, skipping"
}
}
}
function buildVulkan(){
function vulkan {
if ($env:VULKAN_SDK) {
write-host "Building Vulkan backend libraries"
& cmake -B build\vulkan --preset Vulkan --install-prefix $script:DIST_DIR -DOLLAMA_RUNNER_DIR="vulkan"
& cmake -B build\vulkan --preset Vulkan --install-prefix $script:DIST_DIR
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
& cmake --build build\vulkan --target ggml-vulkan --config Release --parallel $script:JOBS
& cmake --build build\vulkan --target ggml-vulkan --config Release --parallel $script:JOBS
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
& cmake --install build\vulkan --component Vulkan --strip
& cmake --install build\vulkan --component Vulkan --strip
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
} else {
write-host "Vulkan not detected, skipping"
}
}
function buildOllama() {
mkdir -Force -path "${script:DIST_DIR}\"
function ollama {
mkdir -Force -path "${script:DIST_DIR}\" | Out-Null
write-host "Building ollama CLI"
& go build -trimpath -ldflags "-s -w -X=github.com/ollama/ollama/version.Version=$script:VERSION -X=github.com/ollama/ollama/server.mode=release" .
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
cp .\ollama.exe "${script:DIST_DIR}\"
}
function buildApp() {
write-host "Building Ollama App"
cd "${script:SRC_DIR}\app"
& windres -l 0 -o ollama.syso ollama.rc
& go build -trimpath -ldflags "-s -w -H windowsgui -X=github.com/ollama/ollama/version.Version=$script:VERSION -X=github.com/ollama/ollama/server.mode=release" -o "${script:SRC_DIR}\dist\windows-${script:TARGET_ARCH}-app.exe" .
function app {
write-host "Building Ollama App $script:VERSION with package version $script:PKG_VERSION"
if (!(Get-Command npm -ErrorAction SilentlyContinue)) {
write-host "npm is not installed. Please install Node.js and npm first:"
write-host " Visit: https://nodejs.org/"
exit 1
}
if (!(Get-Command tsc -ErrorAction SilentlyContinue)) {
write-host "Installing TypeScript compiler..."
npm install -g typescript
}
if (!(Get-Command tscriptify -ErrorAction SilentlyContinue)) {
write-host "Installing tscriptify..."
go install github.com/tkrajina/typescriptify-golang-structs/tscriptify@latest
}
if (!(Get-Command tscriptify -ErrorAction SilentlyContinue)) {
$env:PATH="$env:PATH;$(go env GOPATH)\bin"
}
Push-Location app/ui/app
npm install
if ($LASTEXITCODE -ne 0) {
write-host "ERROR: npm install failed with exit code $LASTEXITCODE"
exit $LASTEXITCODE
}
write-host "Building React application..."
npm run build
if ($LASTEXITCODE -ne 0) {
write-host "ERROR: npm run build failed with exit code $LASTEXITCODE"
exit $LASTEXITCODE
}
# Check if dist directory exists and has content
if (!(Test-Path "dist")) {
write-host "ERROR: dist directory was not created by npm run build"
exit 1
}
$distFiles = Get-ChildItem "dist" -Recurse
if ($distFiles.Count -eq 0) {
write-host "ERROR: dist directory is empty after npm run build"
exit 1
}
Pop-Location
write-host "Running go generate"
& go generate ./...
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
& go build -trimpath -ldflags "-s -w -H windowsgui -X=github.com/ollama/ollama/app/version.Version=$script:VERSION" -o .\dist\windows-ollama-app-${script:ARCH}.exe ./app/cmd/app/
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
}
function gatherDependencies() {
if ($null -eq $env:VCToolsRedistDir) {
write-error "Unable to locate VC Install location - please use a Developer shell"
exit 1
}
write-host "Gathering runtime dependencies from $env:VCToolsRedistDir"
cd "${script:SRC_DIR}"
md "${script:DIST_DIR}\lib\ollama" -ea 0 > $null
# TODO - this varies based on host build system and MSVC version - drive from dumpbin output
# currently works for Win11 + MSVC 2019 + Cuda V11
if ($script:TARGET_ARCH -eq "amd64") {
$depArch="x64"
} else {
$depArch=$script:TARGET_ARCH
}
if ($depArch -eq "x64") {
write-host "cp ${env:VCToolsRedistDir}\${depArch}\Microsoft.VC*.CRT\msvcp140*.dll ${script:DIST_DIR}\lib\ollama\"
cp "${env:VCToolsRedistDir}\${depArch}\Microsoft.VC*.CRT\msvcp140*.dll" "${script:DIST_DIR}\lib\ollama\"
write-host "cp ${env:VCToolsRedistDir}\${depArch}\Microsoft.VC*.CRT\vcruntime140.dll ${script:DIST_DIR}\lib\ollama\"
cp "${env:VCToolsRedistDir}\${depArch}\Microsoft.VC*.CRT\vcruntime140.dll" "${script:DIST_DIR}\lib\ollama\"
write-host "cp ${env:VCToolsRedistDir}\${depArch}\Microsoft.VC*.CRT\vcruntime140_1.dll ${script:DIST_DIR}\lib\ollama\"
cp "${env:VCToolsRedistDir}\${depArch}\Microsoft.VC*.CRT\vcruntime140_1.dll" "${script:DIST_DIR}\lib\ollama\"
$llvmCrtDir="$env:VCToolsRedistDir\..\..\..\Tools\Llvm\${depArch}\bin"
foreach ($part in $("runtime", "stdio", "filesystem", "math", "convert", "heap", "string", "time", "locale", "environment")) {
write-host "cp ${llvmCrtDir}\api-ms-win-crt-${part}*.dll ${script:DIST_DIR}\lib\ollama\"
cp "${llvmCrtDir}\api-ms-win-crt-${part}*.dll" "${script:DIST_DIR}\lib\ollama\"
}
} else {
# Carying the dll's doesn't seem to work, so use the redist installer
copy-item -path "${env:VCToolsRedistDir}\vc_redist.arm64.exe" -destination "${script:DIST_DIR}" -verbose
}
cp "${script:SRC_DIR}\app\ollama_welcome.ps1" "${script:SRC_DIR}\dist\"
function deps {
write-host "Download MSVC Redistributables"
mkdir -Force -path "${script:SRC_DIR}\dist\\windows-arm64" | Out-Null
mkdir -Force -path "${script:SRC_DIR}\dist\\windows-amd64" | Out-Null
invoke-webrequest -Uri "https://aka.ms/vs/17/release/vc_redist.arm64.exe" -OutFile "${script:SRC_DIR}\dist\windows-arm64\vc_redist.arm64.exe"
invoke-webrequest -Uri "https://aka.ms/vs/17/release/vc_redist.x64.exe" -OutFile "${script:SRC_DIR}\dist\windows-amd64\vc_redist.x64.exe"
write-host "Done."
}
function sign() {
function sign {
if ("${env:KEY_CONTAINER}") {
write-host "Signing Ollama executables, scripts and libraries"
& "${script:SignTool}" sign /v /fd sha256 /t http://timestamp.digicert.com /f "${script:OLLAMA_CERT}" `
/csp "Google Cloud KMS Provider" /kc ${env:KEY_CONTAINER} `
$(get-childitem -path "${script:SRC_DIR}\dist" -r -include @('ollama_welcome.ps1')) `
$(get-childitem -path "${script:SRC_DIR}\dist\windows-*" -r -include @('*.exe', '*.dll'))
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
} else {
@@ -261,10 +307,10 @@ function sign() {
}
}
function buildInstaller() {
function installer {
if ($null -eq ${script:INNO_SETUP_DIR}) {
write-host "Inno Setup not present, skipping installer build"
return
write-host "ERROR: missing Inno Setup installation directory - install from https://jrsoftware.org/isdl.php"
exit 1
}
write-host "Building Ollama Installer"
cd "${script:SRC_DIR}\app"
@@ -277,7 +323,7 @@ function buildInstaller() {
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
}
function distZip() {
function zip {
if (Test-Path -Path "${script:SRC_DIR}\dist\windows-amd64") {
if (Test-Path -Path "${script:SRC_DIR}\dist\windows-amd64\lib\ollama\rocm") {
write-host "Generating stand-alone distribution zip file ${script:SRC_DIR}\dist\ollama-windows-amd64-rocm.zip"
@@ -302,23 +348,28 @@ function distZip() {
}
}
function clean {
Remove-Item -ea 0 -r "${script:SRC_DIR}\dist\"
Remove-Item -ea 0 -r "${script:SRC_DIR}\build\"
}
checkEnv
try {
if ($($args.count) -eq 0) {
buildCPU
buildCUDA12
buildCUDA13
buildROCm
buildVulkan
buildOllama
buildApp
gatherDependencies
cpu
cuda12
cuda13
rocm
vulkan
ollama
app
deps
sign
buildInstaller
distZip
installer
zip
} else {
for ( $i = 0; $i -lt $args.count; $i++ ) {
write-host "performing $($args[$i])"
write-host "running build step $($args[$i])"
& $($args[$i])
}
}

636
scripts/create-dmg.sh Executable file
View File

@@ -0,0 +1,636 @@
#!/usr/bin/env bash
# Vendored from https://github.com/create-dmg/create-dmg so our build can be self-contained
# Create a read-only disk image of the contents of a folder
# Bail out on any unhandled errors
set -e;
# Any command that exits with non-zero code will cause the pipeline to fail
set -o pipefail;
CDMG_VERSION='1.2.1'
# The full path to the "support/" directory this script is using
# (This will be set up by code later in the script.)
CDMG_SUPPORT_DIR=""
OS_FULL_VERSION="$(sw_vers | sed -n 2p | cut -d : -f 2 | tr -d '[:space:]' | cut -c1-)"
OS_MAJOR_VERSION="$(echo $OS_FULL_VERSION | cut -d . -f 1)"
OS_MINOR_VERSION="$(echo $OS_FULL_VERSION | cut -d . -f 2)"
WINX=10
WINY=60
WINW=500
WINH=350
ICON_SIZE=128
TEXT_SIZE=16
FORMAT="UDZO"
FILESYSTEM="HFS+"
ADD_FILE_SOURCES=()
ADD_FILE_TARGETS=()
IMAGEKEY=""
HDIUTIL_VERBOSITY=""
SANDBOX_SAFE=0
BLESS=0
SKIP_JENKINS=0
MAXIMUM_UNMOUNTING_ATTEMPTS=3
SIGNATURE=""
NOTARIZE=""
function pure_version() {
echo "$CDMG_VERSION"
}
function hdiutil_detach_retry() {
# Unmount
unmounting_attempts=0
until
echo "Unmounting disk image..."
(( unmounting_attempts++ ))
hdiutil detach "$1"
exit_code=$?
(( exit_code == 0 )) && break # nothing goes wrong
(( exit_code != 16 )) && exit $exit_code # exit with the original exit code
# The above statement returns 1 if test failed (exit_code == 16).
# It can make the code in the {do... done} block to be executed
do
(( unmounting_attempts == MAXIMUM_UNMOUNTING_ATTEMPTS )) && exit 16 # patience exhausted, exit with code EBUSY
echo "Wait a moment..."
sleep $(( 1 * (2 ** unmounting_attempts) ))
done
unset unmounting_attempts
}
function version() {
echo "create-dmg $(pure_version)"
}
function usage() {
version
cat <<EOHELP
Creates a fancy DMG file.
Usage: $(basename $0) [options] <output_name.dmg> <source_folder>
All contents of <source_folder> will be copied into the disk image.
Options:
--volname <name>
set volume name (displayed in the Finder sidebar and window title)
--volicon <icon.icns>
set volume icon
--background <pic.png>
set folder background image (provide png, gif, or jpg)
--window-pos <x> <y>
set position the folder window
--window-size <width> <height>
set size of the folder window
--text-size <text_size>
set window text size (10-16)
--icon-size <icon_size>
set window icons size (up to 128)
--icon file_name <x> <y>
set position of the file's icon
--hide-extension <file_name>
hide the extension of file
--app-drop-link <x> <y>
make a drop link to Applications, at location x,y
--ql-drop-link <x> <y>
make a drop link to user QuickLook install dir, at location x,y
--eula <eula_file>
attach a license file to the dmg (plain text or RTF)
--no-internet-enable
disable automatic mount & copy
--format <format>
specify the final disk image format (UDZO|UDBZ|ULFO|ULMO) (default is UDZO)
--filesystem <filesystem>
specify the disk image filesystem (HFS+|APFS) (default is HFS+, APFS supports macOS 10.13 or newer)
--encrypt
enable encryption for the resulting disk image (AES-256 - you will be prompted for password)
--encrypt-aes128
enable encryption for the resulting disk image (AES-128 - you will be prompted for password)
--add-file <target_name> <file>|<folder> <x> <y>
add additional file or folder (can be used multiple times)
--disk-image-size <x>
set the disk image size manually to x MB
--hdiutil-verbose
execute hdiutil in verbose mode
--hdiutil-quiet
execute hdiutil in quiet mode
--bless
bless the mount folder (deprecated, needs macOS 12.2.1 or older)
--codesign <signature>
codesign the disk image with the specified signature
--notarize <credentials>
notarize the disk image (waits and staples) with the keychain stored credentials
--sandbox-safe
execute hdiutil with sandbox compatibility and do not bless (not supported for APFS disk images)
--skip-jenkins
skip Finder-prettifying AppleScript, useful in Sandbox and non-GUI environments
--version
show create-dmg version number
-h, --help
display this help screen
EOHELP
exit 0
}
# factors can cause interstitial disk images to contain more than a single
# partition - expand the hunt for the temporary disk image by checking for
# the path of the volume, versus assuming its the first result (as in pr/152).
function find_mount_dir() {
local dev_name="${1}"
>&2 echo "Searching for mounted interstitial disk image using ${dev_name}... "
# enumerate up to 9 partitions
for i in {1..9}; do
# attempt to find the partition
local found_dir
found_dir=$(hdiutil info | grep -E --color=never "${dev_name}" | head -${i} | awk '{print $3}' | xargs)
if [[ -n "${found_dir}" ]]; then
echo "${found_dir}"
return 0
fi
done
}
# Argument parsing
while [[ "${1:0:1}" = "-" ]]; do
case $1 in
--volname)
VOLUME_NAME="$2"
shift; shift;;
--volicon)
VOLUME_ICON_FILE="$2"
shift; shift;;
--background)
BACKGROUND_FILE="$2"
BACKGROUND_FILE_NAME="$(basename "$BACKGROUND_FILE")"
BACKGROUND_CLAUSE="set background picture of opts to file \".background:$BACKGROUND_FILE_NAME\""
REPOSITION_HIDDEN_FILES_CLAUSE="set position of every item to {theBottomRightX + 100, 100}"
shift; shift;;
--icon-size)
ICON_SIZE="$2"
shift; shift;;
--text-size)
TEXT_SIZE="$2"
shift; shift;;
--window-pos)
WINX=$2; WINY=$3
shift; shift; shift;;
--window-size)
WINW=$2; WINH=$3
shift; shift; shift;;
--icon)
POSITION_CLAUSE="${POSITION_CLAUSE}set position of item \"$2\" to {$3, $4}
"
shift; shift; shift; shift;;
--hide-extension)
HIDING_CLAUSE="${HIDING_CLAUSE}set the extension hidden of item \"$2\" to true
"
shift; shift;;
-h | --help)
usage;;
--version)
version; exit 0;;
--pure-version)
pure_version; exit 0;;
--ql-drop-link)
QL_LINK=$2
QL_CLAUSE="set position of item \"QuickLook\" to {$2, $3}
"
shift; shift; shift;;
--app-drop-link)
APPLICATION_LINK=$2
APPLICATION_CLAUSE="set position of item \"Applications\" to {$2, $3}
"
shift; shift; shift;;
--eula)
EULA_RSRC=$2
shift; shift;;
--no-internet-enable)
NOINTERNET=1
shift;;
--format)
FORMAT="$2"
shift; shift;;
--filesystem)
FILESYSTEM="$2"
shift; shift;;
--encrypt)
ENABLE_ENCRYPTION=1
AESBITS=256
shift;;
--encrypt-aes128)
ENABLE_ENCRYPTION=1
AESBITS=128
shift;;
--add-file | --add-folder)
ADD_FILE_TARGETS+=("$2")
ADD_FILE_SOURCES+=("$3")
POSITION_CLAUSE="${POSITION_CLAUSE}
set position of item \"$2\" to {$4, $5}
"
shift; shift; shift; shift; shift;;
--disk-image-size)
DISK_IMAGE_SIZE="$2"
shift; shift;;
--hdiutil-verbose)
HDIUTIL_VERBOSITY='-verbose'
shift;;
--hdiutil-quiet)
HDIUTIL_VERBOSITY='-quiet'
shift;;
--codesign)
SIGNATURE="$2"
shift; shift;;
--notarize)
NOTARIZE="$2"
shift; shift;;
--sandbox-safe)
SANDBOX_SAFE=1
shift;;
--bless)
BLESS=1
shift;;
--rez)
echo "REZ is no more directly used. You can remove the --rez argument."
shift; shift;;
--skip-jenkins)
SKIP_JENKINS=1
shift;;
-*)
echo "Unknown option: $1. Run 'create-dmg --help' for help."
exit 1;;
esac
case $FORMAT in
UDZO)
IMAGEKEY="-imagekey zlib-level=9";;
UDBZ)
IMAGEKEY="-imagekey bzip2-level=9";;
ULFO)
;;
ULMO)
;;
*)
echo >&2 "Unknown disk image format: $FORMAT"
exit 1;;
esac
done
if [[ -z "$2" ]]; then
echo "Not enough arguments. Run 'create-dmg --help' for help."
exit 1
fi
DMG_PATH="$1"
SRC_FOLDER="$(cd "$2" > /dev/null; pwd)"
# Argument validation checks
if [[ "${DMG_PATH: -4}" != ".dmg" ]]; then
echo "Output file name must end with a .dmg extension. Run 'create-dmg --help' for help."
exit 1
fi
if [[ "${FILESYSTEM}" != "HFS+" ]] && [[ "${FILESYSTEM}" != "APFS" ]]; then
echo "Unknown disk image filesystem: ${FILESYSTEM}. Run 'create-dmg --help' for help."
exit 1
fi
if [[ "${FILESYSTEM}" == "APFS" ]] && [[ ${SANDBOX_SAFE} -eq 1 ]]; then
echo "Creating an APFS disk image that is sandbox safe is not supported."
exit 1
fi
# Main script logic
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
DMG_DIRNAME="$(dirname "$DMG_PATH")"
DMG_DIR="$(cd "$DMG_DIRNAME" > /dev/null; pwd)"
DMG_NAME="$(basename "$DMG_PATH")"
DMG_TEMP_NAME="$DMG_DIR/rw.$$.${DMG_NAME}"
# Detect where we're running from
sentinel_file="$SCRIPT_DIR/.this-is-the-create-dmg-repo"
if [[ -f "$sentinel_file" ]]; then
# We're running from inside a repo
CDMG_SUPPORT_DIR="$SCRIPT_DIR/support"
else
# We're running inside an installed location
bin_dir="$SCRIPT_DIR"
prefix_dir=$(dirname "$bin_dir")
CDMG_SUPPORT_DIR="$prefix_dir/share/create-dmg/support"
fi
if [[ -z "$VOLUME_NAME" ]]; then
VOLUME_NAME="$(basename "$DMG_PATH" .dmg)"
fi
if [[ ! -d "$CDMG_SUPPORT_DIR" ]]; then
echo >&2 "Cannot find support/ directory: expected at: $CDMG_SUPPORT_DIR"
exit 1
fi
if [[ -f "$SRC_FOLDER/.DS_Store" ]]; then
echo "Deleting .DS_Store found in source folder"
rm "$SRC_FOLDER/.DS_Store"
fi
# Create the image
echo "Creating disk image..."
if [[ -f "${DMG_TEMP_NAME}" ]]; then
rm -f "${DMG_TEMP_NAME}"
fi
# Use Megabytes since hdiutil fails with very large byte numbers
function blocks_to_megabytes() {
# Add 1 extra MB, since there's no decimal retention here
MB_SIZE=$((($1 * 512 / 1000 / 1000) + 1))
echo $MB_SIZE
}
function get_size() {
# Get block size in disk
if [[ $OS_MAJOR_VERSION -ge 12 ]]; then
bytes_size=$(du -B 512 -s "$1")
else
bytes_size=$(du -s "$1")
fi
bytes_size=$(echo $bytes_size | sed -e 's/ .*//g')
echo $(blocks_to_megabytes $bytes_size)
}
# Create the DMG with the specified size or the hdiutil estimation
CUSTOM_SIZE=''
if [[ -n "$DISK_IMAGE_SIZE" ]]; then
CUSTOM_SIZE="-size ${DISK_IMAGE_SIZE}m"
fi
if [[ $SANDBOX_SAFE -eq 0 ]]; then
if [[ "$FILESYSTEM" == "APFS" ]]; then
FILESYSTEM_ARGUMENTS=""
else
FILESYSTEM_ARGUMENTS="-c c=64,a=16,e=16"
fi
hdiutil create ${HDIUTIL_VERBOSITY} -srcfolder "$SRC_FOLDER" -volname "${VOLUME_NAME}" \
-fs "${FILESYSTEM}" -fsargs "${FILESYSTEM_ARGUMENTS}" -format UDRW ${CUSTOM_SIZE} "${DMG_TEMP_NAME}"
else
hdiutil makehybrid ${HDIUTIL_VERBOSITY} -default-volume-name "${VOLUME_NAME}" -hfs -o "${DMG_TEMP_NAME}" "$SRC_FOLDER"
hdiutil convert -format UDRW -ov -o "${DMG_TEMP_NAME}" "${DMG_TEMP_NAME}"
DISK_IMAGE_SIZE_CUSTOM=$DISK_IMAGE_SIZE
fi
# Get the created DMG actual size
DISK_IMAGE_SIZE=$(get_size "${DMG_TEMP_NAME}")
# Use the custom size if bigger
if [[ $SANDBOX_SAFE -eq 1 ]] && [[ ! -z "$DISK_IMAGE_SIZE_CUSTOM" ]] && [[ $DISK_IMAGE_SIZE_CUSTOM -gt $DISK_IMAGE_SIZE ]]; then
DISK_IMAGE_SIZE=$DISK_IMAGE_SIZE_CUSTOM
fi
# Estimate the additional sources size
if [[ -n "$ADD_FILE_SOURCES" ]]; then
for i in "${!ADD_FILE_SOURCES[@]}"; do
SOURCE_SIZE=$(get_size "${ADD_FILE_SOURCES[$i]}")
DISK_IMAGE_SIZE=$(expr $DISK_IMAGE_SIZE + $SOURCE_SIZE)
done
fi
# Add extra space for additional resources
DISK_IMAGE_SIZE=$(expr $DISK_IMAGE_SIZE + 20)
# Make sure target image size is within limits
MIN_DISK_IMAGE_SIZE=$(hdiutil resize -limits "${DMG_TEMP_NAME}" | awk 'NR=1{print int($1/2048+1)}')
if [ $MIN_DISK_IMAGE_SIZE -gt $DISK_IMAGE_SIZE ]; then
DISK_IMAGE_SIZE=$MIN_DISK_IMAGE_SIZE
fi
# Resize the image for the extra stuff
hdiutil resize ${HDIUTIL_VERBOSITY} -size ${DISK_IMAGE_SIZE}m "${DMG_TEMP_NAME}"
# Mount the new DMG
echo "Mounting disk image..."
MOUNT_RANDOM_PATH="/Volumes"
if [[ $SANDBOX_SAFE -eq 1 ]]; then
MOUNT_RANDOM_PATH="/tmp"
fi
if [[ "$FILESYSTEM" == "APFS" ]]; then
HDIUTIL_FILTER="tail -n 1"
else
HDIUTIL_FILTER="sed 1q"
fi
DEV_NAME=$(hdiutil attach -mountrandom ${MOUNT_RANDOM_PATH} -readwrite -noverify -noautoopen -nobrowse "${DMG_TEMP_NAME}" | grep -E --color=never '^/dev/' | ${HDIUTIL_FILTER} | awk '{print $1}')
echo "Device name: $DEV_NAME"
if [[ "$FILESYSTEM" == "APFS" ]]; then
MOUNT_DIR=$(find_mount_dir "${DEV_NAME}")
else
MOUNT_DIR=$(find_mount_dir "${DEV_NAME}s")
fi
if [[ -z "${MOUNT_DIR}" ]]; then
>&2 echo "ERROR: unable to proceed with final disk image creation because the interstitial disk image was not found."
>&2 echo "The interstitial disk image will likely be mounted and will need to be cleaned up manually."
exit 1
fi
echo "Mount dir: $MOUNT_DIR"
if [[ -n "$BACKGROUND_FILE" ]]; then
echo "Copying background file '$BACKGROUND_FILE'..."
[[ -d "$MOUNT_DIR/.background" ]] || mkdir "$MOUNT_DIR/.background"
cp "$BACKGROUND_FILE" "$MOUNT_DIR/.background/$BACKGROUND_FILE_NAME"
fi
if [[ -n "$APPLICATION_LINK" ]]; then
echo "Making link to Applications dir..."
echo $MOUNT_DIR
ln -s /Applications "$MOUNT_DIR/Applications"
fi
if [[ -n "$QL_LINK" ]]; then
echo "Making link to QuickLook install dir..."
echo $MOUNT_DIR
ln -s "/Library/QuickLook" "$MOUNT_DIR/QuickLook"
fi
if [[ -n "$VOLUME_ICON_FILE" ]]; then
echo "Copying volume icon file '$VOLUME_ICON_FILE'..."
cp "$VOLUME_ICON_FILE" "$MOUNT_DIR/.VolumeIcon.icns"
SetFile -c icnC "$MOUNT_DIR/.VolumeIcon.icns"
fi
if [[ -n "$ADD_FILE_SOURCES" ]]; then
echo "Copying custom files..."
for i in "${!ADD_FILE_SOURCES[@]}"; do
echo "${ADD_FILE_SOURCES[$i]}"
cp -a "${ADD_FILE_SOURCES[$i]}" "$MOUNT_DIR/${ADD_FILE_TARGETS[$i]}"
done
fi
VOLUME_NAME=$(basename $MOUNT_DIR)
# Run AppleScript to do all the Finder cosmetic stuff
APPLESCRIPT_FILE=$(mktemp -t createdmg.tmp.XXXXXXXXXX)
if [[ $SANDBOX_SAFE -eq 1 ]]; then
echo "Skipping Finder-prettifying AppleScript because we are in Sandbox..."
else
if [[ $SKIP_JENKINS -eq 0 ]]; then
cat "$CDMG_SUPPORT_DIR/template.applescript" \
| sed -e "s/WINX/$WINX/g" -e "s/WINY/$WINY/g" -e "s/WINW/$WINW/g" \
-e "s/WINH/$WINH/g" -e "s/BACKGROUND_CLAUSE/$BACKGROUND_CLAUSE/g" \
-e "s/REPOSITION_HIDDEN_FILES_CLAUSE/$REPOSITION_HIDDEN_FILES_CLAUSE/g" \
-e "s/ICON_SIZE/$ICON_SIZE/g" -e "s/TEXT_SIZE/$TEXT_SIZE/g" \
| perl -pe "s/POSITION_CLAUSE/$POSITION_CLAUSE/g" \
| perl -pe "s/QL_CLAUSE/$QL_CLAUSE/g" \
| perl -pe "s/APPLICATION_CLAUSE/$APPLICATION_CLAUSE/g" \
| perl -pe "s/HIDING_CLAUSE/$HIDING_CLAUSE/" \
> "$APPLESCRIPT_FILE"
# pause to workaround occasional "Cant get disk" (-1728) issues
ERROR_1728_WORKAROUND_SLEEP_INTERVAL=2
echo "Will sleep for $ERROR_1728_WORKAROUND_SLEEP_INTERVAL seconds to workaround occasions \"Can't get disk (-1728)\" issues..."
sleep $ERROR_1728_WORKAROUND_SLEEP_INTERVAL
echo "Running AppleScript to make Finder stuff pretty: /usr/bin/osascript \"${APPLESCRIPT_FILE}\" \"${VOLUME_NAME}\""
if /usr/bin/osascript "${APPLESCRIPT_FILE}" "${VOLUME_NAME}"; then
# Okay, we're cool
true
else
echo >&2 "Failed running AppleScript"
hdiutil_detach_retry "${DEV_NAME}"
exit 64
fi
echo "Done running the AppleScript..."
sleep 4
rm "$APPLESCRIPT_FILE"
else
echo ''
echo "Will skip running AppleScript to configure DMG aesthetics because of --skip-jenkins option."
echo "This will result in a DMG without any custom background or icons positioning."
echo "More info at https://github.com/create-dmg/create-dmg/issues/72"
echo ''
fi
fi
# Make sure it's not world writeable
echo "Fixing permissions..."
chmod -Rf go-w "${MOUNT_DIR}" &> /dev/null || true
echo "Done fixing permissions"
# Make the top window open itself on mount:
if [[ $BLESS -eq 1 && $SANDBOX_SAFE -eq 0 ]]; then
echo "Blessing started"
if [ $(uname -m) == "arm64" ]; then
bless --folder "${MOUNT_DIR}"
else
bless --folder "${MOUNT_DIR}" --openfolder "${MOUNT_DIR}"
fi
echo "Blessing finished"
else
echo "Skipping blessing on sandbox"
fi
if [[ -n "$VOLUME_ICON_FILE" ]]; then
# Tell the volume that it has a special file attribute
SetFile -a C "$MOUNT_DIR"
fi
# Delete unnecessary file system events log if possible
echo "Deleting .fseventsd"
rm -rf "${MOUNT_DIR}/.fseventsd" || true
hdiutil_detach_retry "${DEV_NAME}"
# Compress image and optionally encrypt
if [[ $ENABLE_ENCRYPTION -eq 0 ]]; then
echo "Compressing disk image..."
hdiutil convert ${HDIUTIL_VERBOSITY} "${DMG_TEMP_NAME}" -format ${FORMAT} ${IMAGEKEY} -o "${DMG_DIR}/${DMG_NAME}"
else
echo "Compressing and encrypting disk image..."
echo "NOTE: hdiutil will only prompt a single time for a password - ensure entry is correct."
hdiutil convert ${HDIUTIL_VERBOSITY} "${DMG_TEMP_NAME}" -format ${FORMAT} ${IMAGEKEY} -encryption AES-${AESBITS} -stdinpass -o "${DMG_DIR}/${DMG_NAME}"
fi
rm -f "${DMG_TEMP_NAME}"
# Adding EULA resources
if [[ -n "${EULA_RSRC}" && "${EULA_RSRC}" != "-null-" ]]; then
echo "Adding EULA resources..."
#
# Use udifrez instead flatten/rez/unflatten
# https://github.com/create-dmg/create-dmg/issues/109
#
# Based on a thread from dawn2dusk & peterguy
# https://developer.apple.com/forums/thread/668084
#
EULA_RESOURCES_FILE=$(mktemp -t createdmg.tmp.XXXXXXXXXX)
EULA_FORMAT=$(file -b ${EULA_RSRC})
if [[ ${EULA_FORMAT} == 'Rich Text Format data'* ]] ; then
EULA_FORMAT='RTF '
else
EULA_FORMAT='TEXT'
fi
# Encode the EULA to base64
# Replace 'openssl base64' with 'base64' if Mac OS X 10.6 support is no more needed
# EULA_DATA="$(base64 -b 52 "${EULA_RSRC}" | sed s$'/^\(.*\)$/\t\t\t\\1/')"
EULA_DATA="$(openssl base64 -in "${EULA_RSRC}" | tr -d '\n' | awk '{gsub(/.{52}/,"&\n")}1' | sed s$'/^\(.*\)$/\t\t\t\\1/')"
# Fill the template with the custom EULA contents
eval "cat > \"${EULA_RESOURCES_FILE}\" <<EOF
$(<${CDMG_SUPPORT_DIR}/eula-resources-template.xml)
EOF
"
# Apply the resources
hdiutil udifrez -xml "${EULA_RESOURCES_FILE}" '' -quiet "${DMG_DIR}/${DMG_NAME}" || {
echo "Failed to add the EULA license"
exit 1
}
echo "Successfully added the EULA license"
fi
# Enable "internet", whatever that is
if [[ ! -z "${NOINTERNET}" && "${NOINTERNET}" == 1 ]]; then
echo "Not setting 'internet-enable' on the dmg, per caller request"
else
# Check if hdiutil supports internet-enable
# Support was removed in macOS 10.15. See https://github.com/andreyvit/create-dmg/issues/76
if hdiutil internet-enable -help >/dev/null 2>/dev/null; then
hdiutil internet-enable -yes "${DMG_DIR}/${DMG_NAME}"
else
echo "hdiutil does not support internet-enable. Note it was removed in macOS 10.15."
fi
fi
if [[ -n "${SIGNATURE}" && "${SIGNATURE}" != "-null-" ]]; then
echo "Codesign started"
codesign -s "${SIGNATURE}" "${DMG_DIR}/${DMG_NAME}"
dmgsignaturecheck="$(codesign --verify --deep --verbose=2 --strict "${DMG_DIR}/${DMG_NAME}" 2>&1 >/dev/null)"
if [ $? -eq 0 ]; then
echo "The disk image is now codesigned"
else
echo "The signature seems invalid${NC}"
exit 1
fi
fi
if [[ -n "${NOTARIZE}" && "${NOTARIZE}" != "-null-" ]]; then
echo "Notarization started"
xcrun notarytool submit "${DMG_DIR}/${DMG_NAME}" --keychain-profile "${NOTARIZE}" --wait
echo "Stapling the notarization ticket"
staple="$(xcrun stapler staple "${DMG_DIR}/${DMG_NAME}")"
if [ $? -eq 0 ]; then
echo "The disk image is now notarized"
else
echo "$staple"
echo "The notarization failed with error $?"
exit 1
fi
fi
# All done!
echo "Disk image done"
exit 0

View File

@@ -0,0 +1,105 @@
<?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">
<plist version="1.0">
<dict>
<key>LPic</key>
<array>
<dict>
<key>Attributes</key>
<string>0x0000</string>
<key>Data</key>
<data>
AAAAAgAAAAAAAAAAAAQAAA==
</data>
<key>ID</key>
<string>5000</string>
<key>Name</key>
<string></string>
</dict>
</array>
<key>STR#</key>
<array>
<dict>
<key>Attributes</key>
<string>0x0000</string>
<key>Data</key>
<data>
AAYNRW5nbGlzaCB0ZXN0MQVBZ3JlZQhEaXNhZ3JlZQVQcmludAdT
YXZlLi4ueklmIHlvdSBhZ3JlZSB3aXRoIHRoZSB0ZXJtcyBvZiB0
aGlzIGxpY2Vuc2UsIGNsaWNrICJBZ3JlZSIgdG8gYWNjZXNzIHRo
ZSBzb2Z0d2FyZS4gSWYgeW91IGRvIG5vdCBhZ3JlZSwgY2xpY2sg
IkRpc2FncmVlIi4=
</data>
<key>ID</key>
<string>5000</string>
<key>Name</key>
<string>English buttons</string>
</dict>
<dict>
<key>Attributes</key>
<string>0x0000</string>
<key>Data</key>
<data>
AAYHRW5nbGlzaAVBZ3JlZQhEaXNhZ3JlZQVQcmludAdTYXZlLi4u
e0lmIHlvdSBhZ3JlZSB3aXRoIHRoZSB0ZXJtcyBvZiB0aGlzIGxp
Y2Vuc2UsIHByZXNzICJBZ3JlZSIgdG8gaW5zdGFsbCB0aGUgc29m
dHdhcmUuIElmIHlvdSBkbyBub3QgYWdyZWUsIGNsaWNrICJEaXNh
Z3JlZSIu
</data>
<key>ID</key>
<string>5002</string>
<key>Name</key>
<string>English</string>
</dict>
</array>
<key>${EULA_FORMAT}</key>
<array>
<dict>
<key>Attributes</key>
<string>0x0000</string>
<key>Data</key>
<data>
${EULA_DATA}
</data>
<key>ID</key>
<string>5000</string>
<key>Name</key>
<string>English</string>
</dict>
</array>
<key>TMPL</key>
<array>
<dict>
<key>Attributes</key>
<string>0x0000</string>
<key>Data</key>
<data>
E0RlZmF1bHQgTGFuZ3VhZ2UgSUREV1JEBUNvdW50T0NOVAQqKioq
TFNUQwtzeXMgbGFuZyBJRERXUkQebG9jYWwgcmVzIElEIChvZmZz
ZXQgZnJvbSA1MDAwRFdSRBAyLWJ5dGUgbGFuZ3VhZ2U/RFdSRAQq
KioqTFNURQ==
</data>
<key>ID</key>
<string>128</string>
<key>Name</key>
<string>LPic</string>
</dict>
</array>
<key>styl</key>
<array>
<dict>
<key>Attributes</key>
<string>0x0000</string>
<key>Data</key>
<data>
AAMAAAAAAAwACQAUAAAAAAAAAAAAAAAAACcADAAJABQBAAAAAAAA
AAAAAAAAKgAMAAkAFAAAAAAAAAAAAAA=
</data>
<key>ID</key>
<string>5000</string>
<key>Name</key>
<string>English</string>
</dict>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,74 @@
on run (volumeName)
tell application "Finder"
tell disk (volumeName as string)
open
set theXOrigin to WINX
set theYOrigin to WINY
set theWidth to WINW
set theHeight to WINH
set theBottomRightX to (theXOrigin + theWidth)
set theBottomRightY to (theYOrigin + theHeight)
set dsStore to "\"" & "/Volumes/" & volumeName & "/" & ".DS_STORE\""
tell container window
set current view to icon view
set toolbar visible to false
set statusbar visible to false
set the bounds to {theXOrigin, theYOrigin, theBottomRightX, theBottomRightY}
set statusbar visible to false
REPOSITION_HIDDEN_FILES_CLAUSE
end tell
set opts to the icon view options of container window
tell opts
set icon size to ICON_SIZE
set text size to TEXT_SIZE
set arrangement to not arranged
end tell
BACKGROUND_CLAUSE
-- Positioning
POSITION_CLAUSE
-- Hiding
HIDING_CLAUSE
-- Application and QL Link Clauses
APPLICATION_CLAUSE
QL_CLAUSE
close
open
-- Force saving of the size
delay 1
tell container window
set statusbar visible to false
set the bounds to {theXOrigin, theYOrigin, theBottomRightX - 10, theBottomRightY - 10}
end tell
end tell
delay 1
tell disk (volumeName as string)
tell container window
set statusbar visible to false
set the bounds to {theXOrigin, theYOrigin, theBottomRightX, theBottomRightY}
end tell
end tell
--give the finder some time to write the .DS_Store file
delay 3
set waitTime to 0
set ejectMe to false
repeat while ejectMe is false
delay 1
set waitTime to waitTime + 1
if (do shell script "[ -f " & dsStore & " ]; echo $?") = "0" then set ejectMe to true
end repeat
log "waited " & waitTime & " seconds for .DS_STORE to be created."
end tell
end run