@@ -27,6 +27,11 @@ import {detectWindowsBuildTools} from "./detectBuildTools.js";
2727const __dirname = path . dirname ( fileURLToPath ( import . meta. url ) ) ;
2828const buildConfigType : "Release" | "RelWithDebInfo" | "Debug" = "Release" ;
2929
30+ const windowsMsvcOnlyBuildFlagsToTargets = new Map (
31+ [ "blas" , "cann" , "cuda" , "hip" , "kompute" , "metal" , "musa" , "sycl" , "vulkan" , "opencl" ]
32+ . map ( ( backend ) => [ "GGML_" + backend . toUpperCase ( ) , "ggml-" + backend . toLowerCase ( ) ] )
33+ ) ;
34+
3035export async function compileLlamaCpp ( buildOptions : BuildOptions , compileOptions : {
3136 nodeTarget ?: string ,
3237 updateLastBuildInfo ?: boolean ,
@@ -53,7 +58,11 @@ export async function compileLlamaCpp(buildOptions: BuildOptions, compileOptions
5358 const finalBuildFolderName = includeBuildOptionsInBinaryFolderName
5459 ? buildFolderName . withCustomCmakeOptions
5560 : buildFolderName . withoutCustomCmakeOptions ;
56- const useWindowsLlvm = ( platform === "win" && ! ignoreWorkarounds . includes ( "avoidWindowsLlvm" ) )
61+ const useWindowsLlvm = (
62+ platform === "win" &&
63+ ! ignoreWorkarounds . includes ( "avoidWindowsLlvm" ) &&
64+ ! buildOptions . customCmakeOptions . has ( "CMAKE_TOOLCHAIN_FILE" )
65+ )
5766 ? areWindowsBuildToolsCapableForLlvmBuild ( await detectWindowsBuildTools ( ) )
5867 : false ;
5968
@@ -84,6 +93,8 @@ export async function compileLlamaCpp(buildOptions: BuildOptions, compileOptions
8493 const toolchainFile = await getToolchainFileForArch ( buildOptions . arch , useWindowsLlvm ) ;
8594 const runtimeVersion = nodeTarget . startsWith ( "v" ) ? nodeTarget . slice ( "v" . length ) : nodeTarget ;
8695 const cmakeCustomOptions = new Map ( buildOptions . customCmakeOptions ) ;
96+ const cmakeToolchainOptions = new Map < string , string > ( ) ;
97+ const windowsSeparateMsvcCmakeOptions = new Map < string , string > ( ) ;
8798
8899 cmakeCustomOptions . set ( "CMAKE_CONFIGURATION_TYPES" , buildConfigType ) ;
89100 cmakeCustomOptions . set ( "NLC_CURRENT_PLATFORM" , platform + "-" + process . arch ) ;
@@ -104,7 +115,7 @@ export async function compileLlamaCpp(buildOptions: BuildOptions, compileOptions
104115 cmakeCustomOptions . set ( "GGML_CCACHE" , "OFF" ) ;
105116
106117 if ( toolchainFile != null && ! cmakeCustomOptions . has ( "CMAKE_TOOLCHAIN_FILE" ) )
107- cmakeCustomOptions . set ( "CMAKE_TOOLCHAIN_FILE" , toolchainFile ) ;
118+ cmakeToolchainOptions . set ( "CMAKE_TOOLCHAIN_FILE" , toolchainFile ) ;
108119
109120 if ( buildOptions . platform === "win" && buildOptions . arch === "arm64" && ! cmakeCustomOptions . has ( "GGML_OPENMP" ) )
110121 cmakeCustomOptions . set ( "GGML_OPENMP" , "OFF" ) ;
@@ -124,6 +135,16 @@ export async function compileLlamaCpp(buildOptions: BuildOptions, compileOptions
124135 }
125136 }
126137
138+ if ( useWindowsLlvm ) {
139+ for ( const [ customFlag , customFlagValue ] of [ ...cmakeCustomOptions . entries ( ) ] ) {
140+ if ( ! windowsMsvcOnlyBuildFlagsToTargets . has ( customFlag ) || isCmakeValueOff ( customFlagValue ) )
141+ continue ;
142+
143+ windowsSeparateMsvcCmakeOptions . set ( customFlag , customFlagValue ) ;
144+ cmakeCustomOptions . delete ( customFlag ) ;
145+ }
146+ }
147+
127148 await fs . remove ( outDirectory ) ;
128149
129150 await spawnCommand (
@@ -152,38 +173,62 @@ export async function compileLlamaCpp(buildOptions: BuildOptions, compileOptions
152173 ...cmakeGeneratorArgs ,
153174 ...cmakePathArgs ,
154175 ...(
155- [ ...cmakeCustomOptions ] . map ( ( [ key , value ] ) => "--CD" + key + "=" + value )
176+ [
177+ ...cmakeCustomOptions ,
178+ ...cmakeToolchainOptions
179+ ] . map ( ( [ key , value ] ) => "--CD" + key + "=" + value )
156180 )
157181 ] ,
158182 __dirname ,
159183 envVars ,
160184 buildOptions . progressLogs
161185 ) ;
162186
163- const binFilesDirPaths = [
164- path . join ( outDirectory , "bin" ) ,
165- path . join ( outDirectory , "llama.cpp" , "bin" )
166- ] ;
167- const compiledResultDirPath = path . join ( outDirectory , buildConfigType ) ;
168-
169- if ( ! await fs . pathExists ( compiledResultDirPath ) )
170- throw new Error ( `Could not find ${ buildConfigType } directory` ) ;
171-
172- for ( const binFilesDirPath of binFilesDirPaths ) {
173- if ( await fs . pathExists ( binFilesDirPath ) ) {
174- const itemNames = await fs . readdir ( binFilesDirPath ) ;
175-
176- await Promise . all (
177- itemNames . map ( ( itemName ) => (
178- fs . copy ( path . join ( binFilesDirPath , itemName ) , path . join ( compiledResultDirPath , itemName ) , {
179- overwrite : false
180- } )
181- ) )
187+ const compiledResultDirPath = await moveBuildFilesToResultDir ( outDirectory ) ;
188+
189+ // perform a separate MSVC build and combine the compiled backends into the final build
190+ if ( useWindowsLlvm && windowsSeparateMsvcCmakeOptions . size > 0 ) {
191+ const llvmResultDir = path . join ( outDirectory , "_llvm" + buildConfigType ) ;
192+ await fs . move ( compiledResultDirPath , llvmResultDir ) ;
193+
194+ for ( const [ targetFlag , targetValue ] of windowsSeparateMsvcCmakeOptions ) {
195+ const targetName = windowsMsvcOnlyBuildFlagsToTargets . get ( targetFlag ) ;
196+ if ( targetName == null )
197+ continue ;
198+
199+ console . info ( getConsoleLogPrefix ( true , false ) , "Building specialized GPU backends using MSVC: " + targetName ) ;
200+
201+ await fs . remove ( compiledResultDirPath ) ;
202+ await spawnCommand (
203+ "npm" ,
204+ [
205+ "run" , "-s" , "cmake-js-llama" , "--" , "compile" ,
206+ "--log-level" , "warn" ,
207+ "--config" , buildConfigType ,
208+ "--arch=" + buildOptions . arch ,
209+ "--out" , path . relative ( llamaDirectory , outDirectory ) ,
210+ "--runtime-version=" + runtimeVersion ,
211+ "--parallel=" + parallelBuildThreads ,
212+ "--target" , targetName ,
213+ ...cmakePathArgs ,
214+ ...(
215+ [
216+ ...cmakeCustomOptions ,
217+ [ targetFlag , targetValue ]
218+ ] . map ( ( [ key , value ] ) => "--CD" + key + "=" + value )
219+ )
220+ ] ,
221+ __dirname ,
222+ envVars ,
223+ buildOptions . progressLogs
182224 ) ;
225+ const targetCompileResultDir = await moveBuildFilesToResultDir ( outDirectory ) ;
226+ await mergeDirWithoutOverrides ( targetCompileResultDir , compiledResultDirPath ) ;
183227 }
184- }
185228
186- await applyResultDirFixes ( compiledResultDirPath , path . join ( outDirectory , "_temp" ) ) ;
229+ await fs . remove ( compiledResultDirPath ) ;
230+ await fs . move ( llvmResultDir , compiledResultDirPath ) ;
231+ }
187232
188233 await fs . writeFile ( path . join ( compiledResultDirPath , buildMetadataFileName ) , JSON . stringify ( {
189234 buildOptions : convertBuildOptionsToBuildOptionsJSON ( buildOptions )
@@ -399,6 +444,35 @@ export async function getPrebuiltBinaryBuildMetadata(folderPath: string, folderN
399444 return buildMetadata ;
400445}
401446
447+ async function moveBuildFilesToResultDir ( outDirectory : string ) {
448+ const binFilesDirPaths = [
449+ path . join ( outDirectory , "bin" ) ,
450+ path . join ( outDirectory , "llama.cpp" , "bin" )
451+ ] ;
452+ const compiledResultDirPath = path . join ( outDirectory , buildConfigType ) ;
453+
454+ if ( ! await fs . pathExists ( compiledResultDirPath ) )
455+ throw new Error ( `Could not find ${ buildConfigType } directory` ) ;
456+
457+ for ( const binFilesDirPath of binFilesDirPaths ) {
458+ if ( await fs . pathExists ( binFilesDirPath ) ) {
459+ const itemNames = await fs . readdir ( binFilesDirPath ) ;
460+
461+ await Promise . all (
462+ itemNames . map ( ( itemName ) => (
463+ fs . copy ( path . join ( binFilesDirPath , itemName ) , path . join ( compiledResultDirPath , itemName ) , {
464+ overwrite : false
465+ } )
466+ ) )
467+ ) ;
468+ }
469+ }
470+
471+ await applyResultDirFixes ( compiledResultDirPath , path . join ( outDirectory , "_temp" ) ) ;
472+
473+ return compiledResultDirPath ;
474+ }
475+
402476async function applyResultDirFixes ( resultDirPath : string , tempDirPath : string ) {
403477 const releaseDirPath = path . join ( resultDirPath , buildConfigType ) ;
404478
@@ -420,6 +494,18 @@ async function applyResultDirFixes(resultDirPath: string, tempDirPath: string) {
420494 }
421495}
422496
497+ async function mergeDirWithoutOverrides ( sourceDirPath : string , targetDirPath : string ) {
498+ const itemNames = await fs . readdir ( sourceDirPath ) ;
499+
500+ await Promise . all (
501+ itemNames . map ( ( itemName ) => (
502+ fs . move ( path . join ( sourceDirPath , itemName ) , path . join ( targetDirPath , itemName ) , {
503+ overwrite : false
504+ } )
505+ ) )
506+ ) ;
507+ }
508+
423509async function resolvePrebuiltBinaryPath ( prebuiltBinaryDirectoryPath : string ) {
424510 const binaryPath = path . join ( prebuiltBinaryDirectoryPath , "llama-addon.node" ) ;
425511 const buildMetadataFilePath = path . join ( prebuiltBinaryDirectoryPath , buildMetadataFileName ) ;
0 commit comments