@@ -62,6 +62,7 @@ const displayInspectionElements = [
6262
6363const runtimeProfile = `display-${ createHash ( 'sha256' )
6464 . update ( JSON . stringify ( {
65+ layout : 2 ,
6566 packages : [ ...macOSRuntimePackages ] . sort ( ) ,
6667 plugins : [ ...displayPluginNames ] . sort ( ) ,
6768 elements : [ ...displayInspectionElements ] . sort ( ) ,
@@ -92,7 +93,7 @@ function currentTarget() {
9293 if ( process . platform === 'darwin' ) {
9394 return 'darwin-universal'
9495 }
95- if ( process . platform === 'win32' && process . arch === 'x64' && process . env . GSTREAMER_ENABLE_WINDOWS_BUNDLE ) {
96+ if ( process . platform === 'win32' && process . arch === 'x64' ) {
9697 return 'win32-x64'
9798 }
9899 return null
@@ -347,6 +348,23 @@ function syncDarwinSymlinkClosure(libDir, neededLibraries) {
347348 }
348349}
349350
351+ function displayRuntimeSeedPaths ( targetDir , spec , pluginExtension ) {
352+ const pluginDir = resolve ( targetDir , 'lib' , 'gstreamer-1.0' )
353+ const seedPaths = [
354+ resolve ( targetDir , spec . binary ) ,
355+ resolve ( targetDir , spec . inspect ) ,
356+ resolve ( targetDir , spec . scanner ) ,
357+ ]
358+ if ( existsSync ( pluginDir ) ) {
359+ for ( const pluginName of readdirSync ( pluginDir ) ) {
360+ if ( pluginName . endsWith ( pluginExtension ) ) {
361+ seedPaths . push ( resolve ( pluginDir , pluginName ) )
362+ }
363+ }
364+ }
365+ return seedPaths
366+ }
367+
350368function collectDarwinLibraryDependencies ( targetDir , seedPaths ) {
351369 const libDir = resolve ( targetDir , 'lib' )
352370 const neededLibraries = new Set ( )
@@ -382,20 +400,7 @@ function pruneDarwinLibraries(targetDir, spec) {
382400 }
383401
384402 const libDir = resolve ( targetDir , 'lib' )
385- const pluginDir = resolve ( libDir , 'gstreamer-1.0' )
386- const seedPaths = [
387- resolve ( targetDir , spec . binary ) ,
388- resolve ( targetDir , spec . inspect ) ,
389- resolve ( targetDir , spec . scanner ) ,
390- ]
391- if ( existsSync ( pluginDir ) ) {
392- for ( const pluginName of readdirSync ( pluginDir ) ) {
393- if ( pluginName . endsWith ( '.dylib' ) ) {
394- seedPaths . push ( resolve ( pluginDir , pluginName ) )
395- }
396- }
397- }
398-
403+ const seedPaths = displayRuntimeSeedPaths ( targetDir , spec , '.dylib' )
399404 const neededLibraries = collectDarwinLibraryDependencies ( targetDir , seedPaths )
400405 for ( const entry of readdirSync ( libDir ) ) {
401406 if ( entry === 'gstreamer-1.0' ) {
@@ -412,6 +417,173 @@ function pruneDarwinLibraries(targetDir, spec) {
412417 }
413418}
414419
420+ function readCString ( buffer , offset ) {
421+ if ( offset < 0 || offset >= buffer . length ) {
422+ return null
423+ }
424+ let end = offset
425+ while ( end < buffer . length && buffer [ end ] !== 0 ) {
426+ end += 1
427+ }
428+ return buffer . subarray ( offset , end ) . toString ( 'ascii' )
429+ }
430+
431+ function parsePEImage ( buffer ) {
432+ if ( buffer . length < 0x40 || buffer . toString ( 'ascii' , 0 , 2 ) !== 'MZ' ) {
433+ return null
434+ }
435+ const peOffset = buffer . readUInt32LE ( 0x3c )
436+ if ( peOffset + 24 > buffer . length || buffer . readUInt32LE ( peOffset ) !== 0x00004550 ) {
437+ return null
438+ }
439+
440+ const sectionCount = buffer . readUInt16LE ( peOffset + 6 )
441+ const optionalHeaderSize = buffer . readUInt16LE ( peOffset + 20 )
442+ const optionalHeaderOffset = peOffset + 24
443+ if ( optionalHeaderOffset + optionalHeaderSize > buffer . length ) {
444+ return null
445+ }
446+
447+ const optionalMagic = buffer . readUInt16LE ( optionalHeaderOffset )
448+ const dataDirectoryOffset = optionalHeaderOffset + (
449+ optionalMagic === 0x20b ? 112 : optionalMagic === 0x10b ? 96 : 0
450+ )
451+ if ( dataDirectoryOffset === optionalHeaderOffset || dataDirectoryOffset + 14 * 8 > buffer . length ) {
452+ return null
453+ }
454+
455+ const sectionTableOffset = optionalHeaderOffset + optionalHeaderSize
456+ const sections = [ ]
457+ for ( let index = 0 ; index < sectionCount ; index += 1 ) {
458+ const sectionOffset = sectionTableOffset + index * 40
459+ if ( sectionOffset + 40 > buffer . length ) {
460+ break
461+ }
462+ sections . push ( {
463+ virtualSize : buffer . readUInt32LE ( sectionOffset + 8 ) ,
464+ virtualAddress : buffer . readUInt32LE ( sectionOffset + 12 ) ,
465+ rawSize : buffer . readUInt32LE ( sectionOffset + 16 ) ,
466+ rawOffset : buffer . readUInt32LE ( sectionOffset + 20 ) ,
467+ } )
468+ }
469+
470+ return {
471+ dataDirectoryOffset,
472+ rvaToOffset ( rva ) {
473+ for ( const section of sections ) {
474+ const size = Math . max ( section . virtualSize , section . rawSize )
475+ if ( rva >= section . virtualAddress && rva < section . virtualAddress + size ) {
476+ const offset = section . rawOffset + rva - section . virtualAddress
477+ return offset >= 0 && offset < buffer . length ? offset : null
478+ }
479+ }
480+ return rva >= 0 && rva < buffer . length ? rva : null
481+ } ,
482+ }
483+ }
484+
485+ function descriptorIsEmpty ( buffer , offset , size ) {
486+ for ( let index = 0 ; index < size ; index += 4 ) {
487+ if ( buffer . readUInt32LE ( offset + index ) !== 0 ) {
488+ return false
489+ }
490+ }
491+ return true
492+ }
493+
494+ function collectPEImportNames ( filePath ) {
495+ const buffer = readFileSync ( filePath )
496+ const image = parsePEImage ( buffer )
497+ if ( ! image ) {
498+ return [ ]
499+ }
500+
501+ const names = new Set ( )
502+ const readDirectory = ( directoryIndex , descriptorSize , nameRvaOffset ) => {
503+ const directoryOffset = image . dataDirectoryOffset + directoryIndex * 8
504+ const rva = buffer . readUInt32LE ( directoryOffset )
505+ const size = buffer . readUInt32LE ( directoryOffset + 4 )
506+ if ( ! rva || ! size ) {
507+ return
508+ }
509+
510+ let descriptorOffset = image . rvaToOffset ( rva )
511+ if ( descriptorOffset === null ) {
512+ return
513+ }
514+ const descriptorEnd = Math . min ( buffer . length , descriptorOffset + size )
515+ while ( descriptorOffset + descriptorSize <= descriptorEnd ) {
516+ if ( descriptorIsEmpty ( buffer , descriptorOffset , descriptorSize ) ) {
517+ break
518+ }
519+ const nameRva = buffer . readUInt32LE ( descriptorOffset + nameRvaOffset )
520+ const nameOffset = image . rvaToOffset ( nameRva )
521+ const name = nameOffset === null ? null : readCString ( buffer , nameOffset )
522+ if ( name ) {
523+ names . add ( name )
524+ }
525+ descriptorOffset += descriptorSize
526+ }
527+ }
528+
529+ readDirectory ( 1 , 20 , 12 )
530+ readDirectory ( 13 , 32 , 4 )
531+ return [ ...names ]
532+ }
533+
534+ function collectWindowsLibraryDependencies ( targetDir , seedPaths ) {
535+ const binDir = resolve ( targetDir , 'bin' )
536+ const availableLibraries = new Map ( )
537+ for ( const entry of readdirSync ( binDir ) ) {
538+ if ( entry . toLowerCase ( ) . endsWith ( '.dll' ) ) {
539+ availableLibraries . set ( entry . toLowerCase ( ) , entry )
540+ }
541+ }
542+
543+ const neededLibraries = new Set ( )
544+ const queue = seedPaths . filter ( existsSync )
545+ for ( let index = 0 ; index < queue . length ; index += 1 ) {
546+ const binaryPath = queue [ index ]
547+ let imports
548+ try {
549+ imports = collectPEImportNames ( binaryPath )
550+ } catch ( error ) {
551+ console . warn (
552+ `Could not inspect Windows GStreamer dependencies for ${ basename ( binaryPath ) } : ${ formatDownloadError ( error ) } ` ,
553+ )
554+ continue
555+ }
556+ for ( const dependency of imports ) {
557+ const libraryName = availableLibraries . get ( dependency . toLowerCase ( ) )
558+ if ( ! libraryName || neededLibraries . has ( libraryName ) ) {
559+ continue
560+ }
561+ neededLibraries . add ( libraryName )
562+ queue . push ( resolve ( binDir , libraryName ) )
563+ }
564+ }
565+ return neededLibraries
566+ }
567+
568+ function pruneWindowsLibraries ( targetDir , spec ) {
569+ if ( process . env . GSTREAMER_KEEP_ALL_LIBS || process . platform !== 'win32' ) {
570+ return
571+ }
572+
573+ const binDir = resolve ( targetDir , 'bin' )
574+ if ( ! existsSync ( binDir ) ) {
575+ return
576+ }
577+
578+ const seedPaths = displayRuntimeSeedPaths ( targetDir , spec , '.dll' )
579+ const neededLibraries = collectWindowsLibraryDependencies ( targetDir , seedPaths )
580+ for ( const entry of readdirSync ( binDir ) ) {
581+ if ( entry . toLowerCase ( ) . endsWith ( '.dll' ) && ! neededLibraries . has ( entry ) ) {
582+ rmSync ( resolve ( binDir , entry ) , { force : true } )
583+ }
584+ }
585+ }
586+
415587function pruneRuntimeLayout ( targetDir , spec ) {
416588 if ( process . env . GSTREAMER_KEEP_FULL_LAYOUT ) {
417589 return
@@ -479,6 +651,7 @@ function stripDarwinBinaries(targetDir) {
479651function pruneDisplayRuntime ( targetDir , spec ) {
480652 pruneDisplayPlugins ( targetDir )
481653 pruneDarwinLibraries ( targetDir , spec )
654+ pruneWindowsLibraries ( targetDir , spec )
482655 pruneRuntimeLayout ( targetDir , spec )
483656 stripDarwinBinaries ( targetDir )
484657}
0 commit comments