build_windows.ps1 10.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
#!powershell
#
# powershell -ExecutionPolicy Bypass -File .\scripts\build_windows.ps1
#
# gcloud auth application-default login

$ErrorActionPreference = "Stop"

function checkEnv() {
10
11
12
13
14
15
16
17
18
19
20
    if ($null -ne $env:ARCH ) {
        $script:ARCH = $env:ARCH
    } else {
        $arch=([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)
        if ($null -ne $arch) {
            $script:ARCH = ($arch.ToString().ToLower()).Replace("x64", "amd64")
        } else {
            write-host "WARNING: old powershell detected, assuming amd64 architecture - set `$env:ARCH to override"
            $script:ARCH="amd64"
        }
    }
21
    $script:TARGET_ARCH=$script:ARCH
22
    Write-host "Building for ${script:TARGET_ARCH}"
23
24
    write-host "Locating required tools and paths"
    $script:SRC_DIR=$PWD
25
    if ($null -eq $env:VCToolsRedistDir) {
26
27
28
        $MSVC_INSTALL=(Get-CimInstance MSFT_VSInstance -Namespace root/cimv2/vs)[0].InstallLocation
        $env:VCToolsRedistDir=(get-item "${MSVC_INSTALL}\VC\Redist\MSVC\*")[0]
    }
29
30
31
32
    # Locate CUDA versions
    # Note: this assumes every version found will be built
    $cudaList=(get-item "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v*\bin\" -ea 'silentlycontinue')
    if ($cudaList.length -eq 0) {
33
        $d=(get-command -ea 'silentlycontinue' nvcc).path
34
35
        if ($null -ne $d) {
            $script:CUDA_DIRS=@($d| split-path -parent)
36
37
        }
    } else {
38
        $script:CUDA_DIRS=$cudaList
39
40
    }
    
41
42
43
44
    $inoSetup=(get-item "C:\Program Files*\Inno Setup*\")
    if ($inoSetup.length -gt 0) {
        $script:INNO_SETUP_DIR=$inoSetup[0]
    }
45

46
    $script:DIST_DIR="${script:SRC_DIR}\dist\windows-${script:TARGET_ARCH}"
47
    $env:CGO_ENABLED="1"
48
    Write-Output "Checking version"
49
50
51
52
53
54
55
56
57
    if (!$env:VERSION) {
        $data=(git describe --tags --first-parent --abbrev=7 --long --dirty --always)
        $pattern="v(.+)"
        if ($data -match $pattern) {
            $script:VERSION=$matches[1]
        }
    } else {
        $script:VERSION=$env:VERSION
    }
58
    $pattern = "(\d+[.]\d+[.]\d+).*"
59
    if ($script:VERSION -match $pattern) {
60
        $script:PKG_VERSION=$matches[1]
61
    } else {
62
        $script:PKG_VERSION="0.0.0"
63
64
65
    }
    write-host "Building Ollama $script:VERSION with package version $script:PKG_VERSION"

66
67
68
69
70
71
    # Note: Windows Kits 10 signtool crashes with GCP's plugin
    if ($null -eq $env:SIGN_TOOL) {
        ${script:SignTool}="C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe"
    } else {
        ${script:SignTool}=${env:SIGN_TOOL}
    }
72
    if ("${env:KEY_CONTAINER}") {
73
        ${script:OLLAMA_CERT}=$(resolve-path "${script:SRC_DIR}\ollama_inc.crt")
74
75
76
77
78
79
80
81
        Write-host "Code signing enabled"
    } else {
        write-host "Code signing disabled - please set KEY_CONTAINERS to sign and copy ollama_inc.crt to the top of the source tree"
    }
}


function buildOllama() {
82
    if ($null -eq ${env:OLLAMA_SKIP_GENERATE}) {
83
        Remove-Item -ea 0 -recurse -force -path "${script:SRC_DIR}\dist\windows-${script:ARCH}"
Michael Yang's avatar
Michael Yang committed
84
85
86
87
88
89
90
        New-Item "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" -ItemType Directory -ea 0


        # Default first, then conditionall ROCm and cuda v11
        write-host "Building Default native backend libraries"
         $env:CMAKE_GENERATOR="ninja"
        & cmake --preset Default
91
        if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
Michael Yang's avatar
Michael Yang committed
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
        & cmake --build --preset Default -j 12
        if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
        & cmake --install build -j 12
        
        # TODO - add steps for v11 and ROCm
        #
        # if ("$script:CUDA_DIRS".Contains("v11") -and "$script:CUDA_DIRS".Contains("v12")) {
        #     # We assume the default is v12, so override for v11
        #     $origCUDA_PATH=$env:CUDA_PATH
        #     $hashEnv = @{}
        #     Get-ChildItem env: | foreach { $hashEnv[$_.Name] = $_.Value }
        #     $hashEnv.Keys | foreach { if ($_.Contains("CUDA_PATH_V11")) { $v11="$_" }}
        #     write-host "$v11"
        #     # $env:CUDA_PATH=$hashEnv[$v11]
        #     # $env:CUDACXX=$hashEnv[$v11]+"\bin\nvcc.exe"
        #     $env:CUDAToolkit_ROOT=$hashEnv[$v11]
        #     # ls env:
        #     write-host "Building CUDA v11 backend libraries"
        #     & cmake --preset "CUDA 11"
        #     $env:CUDA_PATH=$origCUDA_PATH
        #     exit(1)
        #     if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
        #     # & cmake --build --preset "CUDA 11" -j 12
        #     # if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
        # }

        # if ($env:HIP_PATH) {
        #     write-host "Building ROCm backend libraries"
        #     $env:HIPCXX="${env:HIP_PATH}\bin\clang++.exe"
        #     $env:HIP_PLATFORM="amd"
        #     $env:CMAKE_PREFIX_PATH="${env:HIP_PATH}"
        #     & cmake --preset "ROCm"
        #     $env:HIPCXX=""
        #     $env:HIP_PLATFORM=""
        #     $env:CMAKE_PREFIX_PATH=""
        #     if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
        #     & cmake --build --preset "ROCm" -j 12
        #     if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
        # }
131
132
133
    } else {
        write-host "Skipping generate step with OLLAMA_SKIP_GENERATE set"
    }
134
    write-host "Building ollama CLI"
135
    & go build -trimpath -ldflags "-s -w -X=github.com/ollama/ollama/version.Version=$script:VERSION -X=github.com/ollama/ollama/server.mode=release" .
136
    if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
Michael Yang's avatar
Michael Yang committed
137
    cp .\ollama.exe "${script:DIST_DIR}\"
138
139
140
141
142
}

function buildApp() {
    write-host "Building Ollama App"
    cd "${script:SRC_DIR}\app"
143
    & windres -l 0 -o ollama.syso ollama.rc
144
    & 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" .
145
146
147
148
    if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
}

function gatherDependencies() {
149
150
151
152
153
    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"
154
    cd "${script:SRC_DIR}"
155
    md "${script:DIST_DIR}\lib\ollama" -ea 0 > $null
156
157
158

    # TODO - this varies based on host build system and MSVC version - drive from dumpbin output
    # currently works for Win11 + MSVC 2019 + Cuda V11
159
160
161
162
163
    if ($script:TARGET_ARCH -eq "amd64") {
        $depArch="x64"
    } else {
        $depArch=$script:TARGET_ARCH
    }
164
    if ($depArch -eq "x64") {
165
        write-host "cp ${env:VCToolsRedistDir}\${depArch}\Microsoft.VC*.CRT\msvcp140*.dll ${script:DIST_DIR}\lib\ollama\"
166
        cp "${env:VCToolsRedistDir}\${depArch}\Microsoft.VC*.CRT\msvcp140*.dll" "${script:DIST_DIR}\lib\ollama\"
167
        write-host "cp ${env:VCToolsRedistDir}\${depArch}\Microsoft.VC*.CRT\vcruntime140.dll ${script:DIST_DIR}\lib\ollama\"
168
        cp "${env:VCToolsRedistDir}\${depArch}\Microsoft.VC*.CRT\vcruntime140.dll" "${script:DIST_DIR}\lib\ollama\"
169
        write-host "cp ${env:VCToolsRedistDir}\${depArch}\Microsoft.VC*.CRT\vcruntime140_1.dll ${script:DIST_DIR}\lib\ollama\"
170
171
172
173
174
175
176
177
178
        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
Daniel Hiltgen's avatar
Daniel Hiltgen committed
179
    }
180
181

    cp "${script:SRC_DIR}\app\ollama_welcome.ps1" "${script:SRC_DIR}\dist\"
182
183
184
}

function sign() {
185
    if ("${env:KEY_CONTAINER}") {
186
187
188
189
190
191
192
193
        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 {
        write-host "Signing not enabled"
194
195
196
197
    }
}

function buildInstaller() {
198
199
200
201
    if ($null -eq ${script:INNO_SETUP_DIR}) {
        write-host "Inno Setup not present, skipping installer build"
        return
    }
202
203
204
205
    write-host "Building Ollama Installer"
    cd "${script:SRC_DIR}\app"
    $env:PKG_VERSION=$script:PKG_VERSION
    if ("${env:KEY_CONTAINER}") {
206
        & "${script:INNO_SETUP_DIR}\ISCC.exe" /DARCH=$script:TARGET_ARCH /SMySignTool="${script:SignTool} sign /fd sha256 /t http://timestamp.digicert.com /f ${script:OLLAMA_CERT} /csp `$qGoogle Cloud KMS Provider`$q /kc ${env:KEY_CONTAINER} `$f" .\ollama.iss
207
    } else {
208
        & "${script:INNO_SETUP_DIR}\ISCC.exe" /DARCH=$script:TARGET_ARCH .\ollama.iss
209
210
211
212
    }
    if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
}

213
function distZip() {
Michael Yang's avatar
Michael Yang committed
214
215
216
217
218
219
220
221
222
    if (Test-Path -Path "${script:SRC_DIR}\dist\windows-amd64") {
        write-host "Generating stand-alone distribution zip file ${script:SRC_DIR}\dist\ollama-windows-amd64.zip"
        Compress-Archive -Path "${script:SRC_DIR}\dist\windows-amd64\*" -DestinationPath "${script:SRC_DIR}\dist\ollama-windows-amd64.zip" -Force
    }

    if (Test-Path -Path "${script:SRC_DIR}\dist\windows-arm64") {
        write-host "Generating stand-alone distribution zip file ${script:SRC_DIR}\dist\ollama-windows-arm64.zip"
        Compress-Archive -Path "${script:SRC_DIR}\dist\windows-arm64\*" -DestinationPath "${script:SRC_DIR}\dist\ollama-windows-arm64.zip" -Force
    }
223
224
}

225
checkEnv
226
try {
227
228
229
230
    if ($($args.count) -eq 0) {
        buildOllama
        buildApp
        gatherDependencies
231
        sign
232
233
234
235
236
237
238
239
        buildInstaller
        distZip
    } else {
        for ( $i = 0; $i -lt $args.count; $i++ ) {
            write-host "performing $($args[$i])"
            & $($args[$i])
        } 
    }
240
241
242
243
244
245
} catch {
    write-host "Build Failed"
    write-host $_
} finally {
    set-location $script:SRC_DIR
    $env:PKG_VERSION=""
246
}