diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 47db2af3051..3c6a7b3ee89 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -15,7 +15,7 @@ const util = require('./lib/util'); const task = require('./lib/task'); const packageJson = require('../package.json'); const product = require('../product.json'); -const rpmDependencies = require('../resources/linux/rpm/dependencies.json'); +const rpmDependenciesGenerator = require('./linux/rpm/dependencies-generator'); const path = require('path'); const root = path.dirname(__dirname); const commit = util.getVersion(root); @@ -176,6 +176,7 @@ function prepareRpmPackage(arch) { const code = gulp.src(binaryDir + '/**/*', { base: binaryDir }) .pipe(rename(function (p) { p.dirname = 'BUILD/usr/share/' + product.applicationName + '/' + p.dirname; })); + const dependencies = rpmDependenciesGenerator.getDependencies(binaryDir, product.applicationName); const spec = gulp.src('resources/linux/rpm/code.spec.template', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) .pipe(replace('@@NAME_LONG@@', product.nameLong)) @@ -186,7 +187,7 @@ function prepareRpmPackage(arch) { .pipe(replace('@@LICENSE@@', product.licenseName)) .pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@')) .pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@')) - .pipe(replace('@@DEPENDENCIES@@', rpmDependencies[rpmArch].join(', '))) + .pipe(replace('@@DEPENDENCIES@@', dependencies.join(', '))) .pipe(rename('SPECS/' + product.applicationName + '.spec')); const specIcon = gulp.src('resources/linux/rpm/code.xpm', { base: '.' }) diff --git a/build/linux/rpm/dep-lists.js b/build/linux/rpm/dep-lists.js new file mode 100644 index 00000000000..f72b442670a --- /dev/null +++ b/build/linux/rpm/dep-lists.js @@ -0,0 +1,31 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.bundledDeps = exports.additionalDeps = void 0; +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/additional_deps +// Additional dependencies not in the rpm find-requires output. +exports.additionalDeps = [ + 'ca-certificates', + 'libgtk-3.so.0()(64bit)', + 'libnss3.so(NSS_3.22)(64bit)', + 'libssl3.so(NSS_3.28)(64bit)', + 'rpmlib(FileDigests) <= 4.6.0-1', + 'libvulkan.so.1()(64bit)', + 'libcurl.so.4()(64bit)', + 'xdg-utils' // OS integration +]; +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/98.0.4758.109:chrome/installer/linux/BUILD.gn;l=64-80 +// and the Linux Archive build +// Shared library dependencies that we already bundle. +exports.bundledDeps = [ + 'libEGL.so', + 'libGLESv2.so', + 'libvulkan.so.1', + 'swiftshader_libEGL.so', + 'swiftshader_libGLESv2.so', + 'libvk_swiftshader.so', + 'libffmpeg.so' +]; diff --git a/build/linux/rpm/dep-lists.ts b/build/linux/rpm/dep-lists.ts new file mode 100644 index 00000000000..884f6825fbd --- /dev/null +++ b/build/linux/rpm/dep-lists.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/additional_deps +// Additional dependencies not in the rpm find-requires output. +export const additionalDeps = [ + 'ca-certificates', // Make sure users have SSL certificates. + 'libgtk-3.so.0()(64bit)', + 'libnss3.so(NSS_3.22)(64bit)', + 'libssl3.so(NSS_3.28)(64bit)', + 'rpmlib(FileDigests) <= 4.6.0-1', + 'libvulkan.so.1()(64bit)', + 'libcurl.so.4()(64bit)', + 'xdg-utils' // OS integration +]; + +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/98.0.4758.109:chrome/installer/linux/BUILD.gn;l=64-80 +// and the Linux Archive build +// Shared library dependencies that we already bundle. +export const bundledDeps = [ + 'libEGL.so', + 'libGLESv2.so', + 'libvulkan.so.1', + 'swiftshader_libEGL.so', + 'swiftshader_libGLESv2.so', + 'libvk_swiftshader.so', + 'libffmpeg.so' +]; diff --git a/build/linux/rpm/dependencies-generator.js b/build/linux/rpm/dependencies-generator.js new file mode 100644 index 00000000000..ddeefe3c28d --- /dev/null +++ b/build/linux/rpm/dependencies-generator.js @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getDependencies = void 0; +const child_process_1 = require("child_process"); +const fs_1 = require("fs"); +const path = require("path"); +const dep_lists_1 = require("./dep-lists"); +function getDependencies(buildDir, applicationName) { + // Get the files for which we want to find dependencies. + const nativeModulesPath = path.join(buildDir, 'resources', 'app', 'node_modules.asar.unpacked'); + const findResult = (0, child_process_1.spawnSync)('find', [nativeModulesPath, '-name', '*.node']); + if (findResult.status) { + console.error('Error finding files:'); + console.error(findResult.stderr.toString()); + return []; + } + const files = findResult.stdout.toString().trimEnd().split('\n'); + const appPath = path.join(buildDir, applicationName); + files.push(appPath); + // Add chrome sandbox and crashpad handler. + files.push(path.join(buildDir, 'chrome-sandbox')); + files.push(path.join(buildDir, 'chrome_crashpad_handler')); + // Generate the dependencies. + const dependencies = files.map((file) => calculatePackageDeps(file)); + // Add additional dependencies. + const additionalDepsSet = new Set(dep_lists_1.additionalDeps); + dependencies.push(additionalDepsSet); + // Merge all the dependencies. + const mergedDependencies = mergePackageDeps(dependencies); + let sortedDependencies = []; + for (const dependency of mergedDependencies) { + sortedDependencies.push(dependency); + } + sortedDependencies.sort(); + // Exclude bundled dependencies + sortedDependencies = sortedDependencies.filter(dependency => { + return !dep_lists_1.bundledDeps.some(bundledDep => dependency.startsWith(bundledDep)); + }); + return sortedDependencies; +} +exports.getDependencies = getDependencies; +function calculatePackageDeps(binaryPath) { + try { + if (!((0, fs_1.statSync)(binaryPath).mode & fs_1.constants.S_IXUSR)) { + throw new Error(`Binary ${binaryPath} needs to have an executable bit set.`); + } + } + catch (e) { + // The package might not exist. Don't re-throw the error here. + console.error('Tried to stat ' + binaryPath + ' but failed.'); + } + const findRequiresResult = (0, child_process_1.spawnSync)('/usr/lib/rpm/find-requires', { input: binaryPath + '\n' }); + if (findRequiresResult.status !== 0) { + throw new Error(`find-requires failed with exit code ${findRequiresResult.status}.\nstderr: ${findRequiresResult.stderr}`); + } + const requires = new Set(findRequiresResult.stdout.toString('utf-8').trimEnd().split('\n')); + // we only need to use provides to check for newer dependencies + // const provides = readFileSync('dist_package_provides.json'); + // const jsonProvides = JSON.parse(provides.toString('utf-8')); + return requires; +} +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/merge_package_deps.py +function mergePackageDeps(inputDeps) { + const requires = new Set(); + for (const depSet of inputDeps) { + for (const dep of depSet) { + const trimmedDependency = dep.trim(); + if (trimmedDependency.length && !trimmedDependency.startsWith('#')) { + requires.add(trimmedDependency); + } + } + } + return requires; +} diff --git a/build/linux/rpm/dependencies-generator.ts b/build/linux/rpm/dependencies-generator.ts new file mode 100644 index 00000000000..97d313fe570 --- /dev/null +++ b/build/linux/rpm/dependencies-generator.ts @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { spawnSync } from 'child_process'; +import { constants, statSync } from 'fs'; +import path = require('path'); +import { additionalDeps, bundledDeps } from './dep-lists'; + +export function getDependencies(buildDir: string, applicationName: string): string[] { + // Get the files for which we want to find dependencies. + const nativeModulesPath = path.join(buildDir, 'resources', 'app', 'node_modules.asar.unpacked'); + const findResult = spawnSync('find', [nativeModulesPath, '-name', '*.node']); + if (findResult.status) { + console.error('Error finding files:'); + console.error(findResult.stderr.toString()); + return []; + } + + const files = findResult.stdout.toString().trimEnd().split('\n'); + + const appPath = path.join(buildDir, applicationName); + files.push(appPath); + + // Add chrome sandbox and crashpad handler. + files.push(path.join(buildDir, 'chrome-sandbox')); + files.push(path.join(buildDir, 'chrome_crashpad_handler')); + + // Generate the dependencies. + const dependencies: Set[] = files.map((file) => calculatePackageDeps(file)); + + // Add additional dependencies. + const additionalDepsSet = new Set(additionalDeps); + dependencies.push(additionalDepsSet); + + // Merge all the dependencies. + const mergedDependencies = mergePackageDeps(dependencies); + let sortedDependencies: string[] = []; + for (const dependency of mergedDependencies) { + sortedDependencies.push(dependency); + } + sortedDependencies.sort(); + + // Exclude bundled dependencies + sortedDependencies = sortedDependencies.filter(dependency => { + return !bundledDeps.some(bundledDep => dependency.startsWith(bundledDep)); + }); + + return sortedDependencies; +} + +function calculatePackageDeps(binaryPath: string): Set { + try { + if (!(statSync(binaryPath).mode & constants.S_IXUSR)) { + throw new Error(`Binary ${binaryPath} needs to have an executable bit set.`); + } + } catch (e) { + // The package might not exist. Don't re-throw the error here. + console.error('Tried to stat ' + binaryPath + ' but failed.'); + } + + const findRequiresResult = spawnSync('/usr/lib/rpm/find-requires', { input: binaryPath + '\n' }); + if (findRequiresResult.status !== 0) { + throw new Error(`find-requires failed with exit code ${findRequiresResult.status}.\nstderr: ${findRequiresResult.stderr}`); + } + + const requires = new Set(findRequiresResult.stdout.toString('utf-8').trimEnd().split('\n')); + + // we only need to use provides to check for newer dependencies + // const provides = readFileSync('dist_package_provides.json'); + // const jsonProvides = JSON.parse(provides.toString('utf-8')); + + return requires; +} + +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/merge_package_deps.py +function mergePackageDeps(inputDeps: Set[]): Set { + const requires = new Set(); + for (const depSet of inputDeps) { + for (const dep of depSet) { + const trimmedDependency = dep.trim(); + if (trimmedDependency.length && !trimmedDependency.startsWith('#')) { + requires.add(trimmedDependency); + } + } + } + return requires; +} diff --git a/resources/linux/rpm/dependencies.json b/resources/linux/rpm/dependencies.json deleted file mode 100644 index 8a055565b64..00000000000 --- a/resources/linux/rpm/dependencies.json +++ /dev/null @@ -1,185 +0,0 @@ -{ - "x86_64": [ - "libpthread.so.0()(64bit)", - "libpthread.so.0(GLIBC_2.2.5)(64bit)", - "libpthread.so.0(GLIBC_2.3.2)(64bit)", - "libpthread.so.0(GLIBC_2.3.3)(64bit)", - "libgtk-3.so.0()(64bit)", - "libgdk-x11-2.0.so.0()(64bit)", - "libatk-1.0.so.0()(64bit)", - "libgio-2.0.so.0()(64bit)", - "libpangocairo-1.0.so.0()(64bit)", - "libgdk_pixbuf-2.0.so.0()(64bit)", - "libcairo.so.2()(64bit)", - "libpango-1.0.so.0()(64bit)", - "libfreetype.so.6()(64bit)", - "libfontconfig.so.1()(64bit)", - "libgobject-2.0.so.0()(64bit)", - "libdbus-1.so.3()(64bit)", - "libXi.so.6()(64bit)", - "libXcursor.so.1()(64bit)", - "libXdamage.so.1()(64bit)", - "libXrandr.so.2()(64bit)", - "libXcomposite.so.1()(64bit)", - "libXext.so.6()(64bit)", - "libXfixes.so.3()(64bit)", - "libXrender.so.1()(64bit)", - "libX11.so.6()(64bit)", - "libXss.so.1()(64bit)", - "libXtst.so.6()(64bit)", - "libgmodule-2.0.so.0()(64bit)", - "librt.so.1()(64bit)", - "libglib-2.0.so.0()(64bit)", - "libnss3.so()(64bit)", - "libnssutil3.so()(64bit)", - "libsmime3.so()(64bit)", - "libnspr4.so()(64bit)", - "libasound.so.2()(64bit)", - "libcups.so.2()(64bit)", - "libdl.so.2()(64bit)", - "libexpat.so.1()(64bit)", - "libstdc++.so.6()(64bit)", - "libstdc++.so.6(GLIBCXX_3.4)(64bit)", - "libstdc++.so.6(GLIBCXX_3.4.10)(64bit)", - "libstdc++.so.6(GLIBCXX_3.4.11)(64bit)", - "libstdc++.so.6(GLIBCXX_3.4.14)(64bit)", - "libstdc++.so.6(GLIBCXX_3.4.15)(64bit)", - "libstdc++.so.6(GLIBCXX_3.4.9)(64bit)", - "libm.so.6()(64bit)", - "libm.so.6(GLIBC_2.2.5)(64bit)", - "libgcc_s.so.1()(64bit)", - "libgcc_s.so.1(GCC_3.0)(64bit)", - "libgcc_s.so.1(GCC_4.0.0)(64bit)", - "libc.so.6()(64bit)", - "libc.so.6(GLIBC_2.11)(64bit)", - "libc.so.6(GLIBC_2.2.5)(64bit)", - "libc.so.6(GLIBC_2.3)(64bit)", - "libc.so.6(GLIBC_2.3.2)(64bit)", - "libc.so.6(GLIBC_2.3.4)(64bit)", - "libc.so.6(GLIBC_2.4)(64bit)", - "libc.so.6(GLIBC_2.6)(64bit)", - "libc.so.6(GLIBC_2.7)(64bit)", - "libc.so.6(GLIBC_2.9)(64bit)", - "libxcb.so.1()(64bit)", - "libxkbfile.so.1()(64bit)", - "libsecret-1.so.0()(64bit)", - "libgbm.so.1()(64bit)" - ], - "aarch64": [ - "libpthread.so.0()(64bit)", - "libpthread.so.0(GLIBC_2.17)(64bit)", - "libgtk-3.so.0()(64bit)", - "libgdk-x11-2.0.so.0()(64bit)", - "libatk-1.0.so.0()(64bit)", - "libgio-2.0.so.0()(64bit)", - "libpangocairo-1.0.so.0()(64bit)", - "libgdk_pixbuf-2.0.so.0()(64bit)", - "libcairo.so.2()(64bit)", - "libpango-1.0.so.0()(64bit)", - "libfreetype.so.6()(64bit)", - "libfontconfig.so.1()(64bit)", - "libgobject-2.0.so.0()(64bit)", - "libdbus-1.so.3()(64bit)", - "libXi.so.6()(64bit)", - "libXcursor.so.1()(64bit)", - "libXdamage.so.1()(64bit)", - "libXrandr.so.2()(64bit)", - "libXcomposite.so.1()(64bit)", - "libXext.so.6()(64bit)", - "libXfixes.so.3()(64bit)", - "libXrender.so.1()(64bit)", - "libX11.so.6()(64bit)", - "libXss.so.1()(64bit)", - "libXtst.so.6()(64bit)", - "libgmodule-2.0.so.0()(64bit)", - "librt.so.1()(64bit)", - "libglib-2.0.so.0()(64bit)", - "libnss3.so()(64bit)", - "libnssutil3.so()(64bit)", - "libsmime3.so()(64bit)", - "libnspr4.so()(64bit)", - "libasound.so.2()(64bit)", - "libcups.so.2()(64bit)", - "libdl.so.2()(64bit)", - "libexpat.so.1()(64bit)", - "libstdc++.so.6()(64bit)", - "libstdc++.so.6(GLIBCXX_3.4)(64bit)", - "libstdc++.so.6(GLIBCXX_3.4.10)(64bit)", - "libstdc++.so.6(GLIBCXX_3.4.11)(64bit)", - "libstdc++.so.6(GLIBCXX_3.4.14)(64bit)", - "libstdc++.so.6(GLIBCXX_3.4.15)(64bit)", - "libstdc++.so.6(GLIBCXX_3.4.9)(64bit)", - "libm.so.6()(64bit)", - "libm.so.6(GLIBC_2.17)(64bit)", - "libgcc_s.so.1()(64bit)", - "libgcc_s.so.1(GCC_3.0)(64bit)", - "libgcc_s.so.1(GCC_4.0.0)(64bit)", - "libc.so.6()(64bit)", - "libc.so.6(GLIBC_2.17)(64bit)", - "libxcb.so.1()(64bit)", - "libxkbfile.so.1()(64bit)", - "libsecret-1.so.0()(64bit)" - ], - "armv7hl": [ - "libpthread.so.0()", - "libpthread.so.0(GLIBC_2.4)", - "libpthread.so.0(GLIBC_2.11)", - "libpthread.so.0(GLIBC_2.12)", - "libgtk-3.so.0()", - "libgdk-x11-2.0.so.0()", - "libatk-1.0.so.0()", - "libgio-2.0.so.0()", - "libpangocairo-1.0.so.0()", - "libgdk_pixbuf-2.0.so.0()", - "libcairo.so.2()", - "libpango-1.0.so.0()", - "libfreetype.so.6()", - "libfontconfig.so.1()", - "libgobject-2.0.so.0()", - "libdbus-1.so.3()", - "libXi.so.6()", - "libXcursor.so.1()", - "libXdamage.so.1()", - "libXrandr.so.2()", - "libXcomposite.so.1()", - "libXext.so.6()", - "libXfixes.so.3()", - "libXrender.so.1()", - "libX11.so.6()", - "libXss.so.1()", - "libXtst.so.6()", - "libgmodule-2.0.so.0()", - "librt.so.1()", - "libglib-2.0.so.0()", - "libnss3.so()", - "libnssutil3.so()", - "libsmime3.so()", - "libnspr4.so()", - "libasound.so.2()", - "libcups.so.2()", - "libdl.so.2()", - "libexpat.so.1()", - "libstdc++.so.6()", - "libstdc++.so.6(GLIBCXX_3.4)", - "libstdc++.so.6(GLIBCXX_3.4.10)", - "libstdc++.so.6(GLIBCXX_3.4.11)", - "libstdc++.so.6(GLIBCXX_3.4.14)", - "libstdc++.so.6(GLIBCXX_3.4.15)", - "libstdc++.so.6(GLIBCXX_3.4.9)", - "libm.so.6()", - "libm.so.6(GLIBC_2.4)", - "libm.so.6(GLIBC_2.15)", - "libgcc_s.so.1()", - "libgcc_s.so.1(GCC_3.0)", - "libgcc_s.so.1(GCC_4.0.0)", - "libc.so.6()", - "libc.so.6(GLIBC_2.11)", - "libc.so.6(GLIBC_2.4)", - "libc.so.6(GLIBC_2.6)", - "libc.so.6(GLIBC_2.7)", - "libc.so.6(GLIBC_2.9)", - "libxcb.so.1()", - "libxkbfile.so.1()", - "libsecret-1.so.0()" - ] -}