Skip to content

Commit

Permalink
Generate rpm dependencies (microsoft#143415)
Browse files Browse the repository at this point in the history
* Commit non-py files

* Finish porting calculate and merge scripts for rpm

* Switch back to ts, add binaryDir

* Pass in app path

* Trim string before splitting

* Move files, apply PR feedback

* Add deps to exclude and crashpad handler dep

* polish
  • Loading branch information
rzhao271 authored Mar 18, 2022
1 parent 3ffbbe7 commit d572d6e
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 187 deletions.
5 changes: 3 additions & 2 deletions build/gulpfile.vscode.linux.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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))
Expand All @@ -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: '.' })
Expand Down
31 changes: 31 additions & 0 deletions build/linux/rpm/dep-lists.js
Original file line number Diff line number Diff line change
@@ -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'
];
30 changes: 30 additions & 0 deletions build/linux/rpm/dep-lists.ts
Original file line number Diff line number Diff line change
@@ -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'
];
78 changes: 78 additions & 0 deletions build/linux/rpm/dependencies-generator.js
Original file line number Diff line number Diff line change
@@ -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;
}
91 changes: 91 additions & 0 deletions build/linux/rpm/dependencies-generator.ts
Original file line number Diff line number Diff line change
@@ -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<string>[] = 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<string> {
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<string>[]): Set<string> {
const requires = new Set<string>();
for (const depSet of inputDeps) {
for (const dep of depSet) {
const trimmedDependency = dep.trim();
if (trimmedDependency.length && !trimmedDependency.startsWith('#')) {
requires.add(trimmedDependency);
}
}
}
return requires;
}
Loading

0 comments on commit d572d6e

Please sign in to comment.