diff --git a/script/.prettierrc b/script/.prettierrc new file mode 100644 index 000000000..e97ea1bfa --- /dev/null +++ b/script/.prettierrc @@ -0,0 +1,5 @@ +{ + "tabWidth": 2, + "useTabs": false, + "singleQuote": true +} diff --git a/script/archive-standalone-nix.sh b/script/archive-standalone-nix.sh index 94a3f930b..ead14f11f 100755 --- a/script/archive-standalone-nix.sh +++ b/script/archive-standalone-nix.sh @@ -90,6 +90,15 @@ cp bin/locales/*.json $OUTDIR/locales cp bin/themes/*.json $OUTDIR/themes cp -rfp bin/share/terminfo/* $OUTDIR/share/terminfo/ +if [[ $CROSSCOMPILE == "rpi" ]]; then + printf "\n\n\n ***** CROSSCOMPILE DETECTED, **NOT** SCANNING DEPENDENCIES! *****\n\n\n" + sleep 1 +else + printf "\n\n\n ***** SCANNING DEPENDENCIES *****\n\n\n" + sleep 1 + node ./script/scan-standalone dist/$VERSION/$OUTNAME || exit $? +fi + strip $OUTDIR/musikcube strip $OUTDIR/musikcubed strip $OUTDIR/libmusikcore.${DLL_EXT} diff --git a/script/scan-standalone.js b/script/scan-standalone.js new file mode 100644 index 000000000..2ca36f896 --- /dev/null +++ b/script/scan-standalone.js @@ -0,0 +1,126 @@ +const { promisify } = require('util'); +const exec = promisify(require('child_process').exec); +const readdir = promisify(require('fs').readdir); + +const mac = process.platform === 'darwin'; + +const filetype = mac ? '.dylib' : '.so'; + +/* these libraries will always have dynamic dependencies, so ignore */ +const ignoredLibraries = mac ? new Set([]) : new Set([]); + +/* we assume these dependencies will always be available on the user's +target machine. */ +const validLibraries = mac + ? new Set([ + '/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit', + '/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices', + '/System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox', + '/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon', + '/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio', + '/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation', + '/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics', + '/System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia', + '/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo', + '/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation', + '/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration', + '/usr/lib/libc++.1.dylib', + '/usr/lib/libobjc.A.dylib', + '/usr/lib/libSystem.B.dylib', + '/usr/lib/libz.1.dylib', + ]) + : new Set([ + '/lib64/ld-linux-x86-64.so.2', + 'libc.so.6', + 'libdl.so.2', + 'libgcc_s.so.1', + 'libm.so.6', + 'libstdc++.so.6', + 'libz.so.1', + 'linux-vdso.so.1', + ]); + +let errors = 0; + +const path = process.argv[2]; + +if (!path) { + console.log('\n\nusage: node scan-standalone.js \n\n'); + process.exit(1); +} + +const ls = async (leaf) => { + const output = await readdir(`${path}/${leaf}`); + const files = output + .filter((fn) => fn.indexOf(filetype) !== -1) + .filter((fn) => !ignoredLibraries.has(fn)); + return files; +}; + +const lddLinux = async (fn) => { + const output = await exec(`ldd ${path}/${fn}`); + const problems = output.stdout + .split('\n') + .map((line) => line.trim()) + /* libraries resolved properly will exist in the current path; exclude */ + .filter((line) => !!line && line.indexOf(path) === -1) + .filter((line) => line !== 'statically linked') + /* libraries are formatted as " [=> ] <(offset)>". if there's + a fat arrow, just grab everything to the left of it. then, if there's + still an offset, strip that as well */ + .map((line) => line.split(' => ')[0].split(' ')[0].trim()) + .filter((line) => !validLibraries.has(line)); + return problems; +}; + +const lddMac = async (fn) => { + const output = await exec(`otool -L ${path}/${fn}`); + const problems = output.stdout + .split('\n') + .map((line) => line.trim()) + /* we want all of the libraries we compile ourself to be prefixed + with the value `@rpath/`. */ + .filter((line) => !!line && line.indexOf('@rpath/') === -1) + /* the first line of output is the path to the library, exclude it */ + .slice(1) + /* libraries are formatted as " (compatibility ...)", so split + and take the name */ + .map((line) => line.split(' (compatibility')[0].trim()) + .filter((line) => !validLibraries.has(line)); + return problems; +}; + +const ldd = async (fn) => { + const problems = mac ? await lddMac(fn) : await lddLinux(fn); + if (!problems.length) { + console.log(`${fn}: ok`); + } else { + console.error(`${fn}: potential problems found`); + problems.forEach((line) => console.error(` * ${line}`)); + ++errors; + } +}; + +const lddDir = async (leaf) => { + const files = await ls(leaf); + await Promise.allSettled(files.map((fn) => ldd(`${leaf}/${fn}`))); +}; + +const main = async () => { + await ldd('musikcube'); + await ldd('musikcubed'); + await ldd(`libmusikcore${filetype}`); + await lddDir('plugins'); + await lddDir('lib'); + + if (errors) { + console.log( + `\n\n\n ***** ${errors} problematic files detected! ***** \n\n\n` + ); + process.exit(1); + } + + console.log('\neverything looks good!\n'); +}; + +main();