Files
electron/script/lib/utils.js
David Sanders d212bc7170 build: create a symlink to src/buildtools during sync
Assisted-by: Claude Opus 4.6
2026-05-01 18:06:02 -07:00

214 lines
6.4 KiB
JavaScript

const chalk = require('chalk');
const childProcess = require('node:child_process');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const ELECTRON_DIR = path.resolve(__dirname, '..', '..');
const SRC_DIR = path.resolve(ELECTRON_DIR, '..');
const CHROMIUM_VERSION_DEPS_REGEX = /chromium_version':\n +'(.+?)',/m;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const pass = chalk.green('✓');
const fail = chalk.red('✗');
function getElectronExec() {
const OUT_DIR = getOutDir();
switch (process.platform) {
case 'darwin':
return `out/${OUT_DIR}/Electron.app/Contents/MacOS/Electron`;
case 'win32':
return `out/${OUT_DIR}/electron.exe`;
case 'linux':
return `out/${OUT_DIR}/electron`;
default:
throw new Error('Unknown platform');
}
}
function getOutDir(options = {}) {
const shouldLog = options.shouldLog || false;
const presetDirs = ['Testing', 'Release', 'Default', 'Debug'];
if (options.outDir || process.env.ELECTRON_OUT_DIR) {
const outDir = options.outDir || process.env.ELECTRON_OUT_DIR;
const outPath = path.resolve(SRC_DIR, 'out', outDir);
// Check that user-set variable is a valid/existing directory
if (fs.existsSync(outPath)) {
if (shouldLog) console.log(`OUT_DIR is: ${outDir}`);
return outDir;
}
// Throw error if user passed/set nonexistent directory.
throw new Error(`${outDir} directory not configured on your machine.`);
} else {
for (const buildType of presetDirs) {
const outPath = path.resolve(SRC_DIR, 'out', buildType);
if (fs.existsSync(outPath)) {
if (shouldLog) console.log(`OUT_DIR is: ${buildType}`);
return buildType;
}
}
}
// If we got here, it means process.env.ELECTRON_OUT_DIR was not
// set and none of the preset options could be found in /out, so throw
throw new Error(
`No valid out directory found; use one of ${presetDirs.join(',')} or set process.env.ELECTRON_OUT_DIR`
);
}
function getAbsoluteElectronExec() {
return path.resolve(SRC_DIR, getElectronExec());
}
function handleGitCall(args, gitDir) {
const result = childProcess.spawnSync('git', args, {
cwd: gitDir,
encoding: 'utf8',
stdio: ['inherit', 'pipe', 'pipe']
});
if (result.status === 0) {
return result.stdout.replace(/^\*|\s+|\s+$/, '');
} else {
console.log(`${fail} couldn't parse git process call: `, result.stderr);
process.exit(1);
}
}
async function getCurrentBranch(gitDir) {
const RELEASE_BRANCH_PATTERN = /^\d+-x-y$/;
const MAIN_BRANCH_PATTERN = /^main$/;
const ORIGIN_MAIN_BRANCH_PATTERN = /^origin\/main$/;
let branch = await handleGitCall(['rev-parse', '--abbrev-ref', 'HEAD'], gitDir);
if (!MAIN_BRANCH_PATTERN.test(branch) && !RELEASE_BRANCH_PATTERN.test(branch)) {
const lastCommit = await handleGitCall(['rev-parse', 'HEAD'], gitDir);
const branches = (await handleGitCall(['branch', '--contains', lastCommit, '--remote'], gitDir)).split('\n');
branch = branches.find(
(b) =>
MAIN_BRANCH_PATTERN.test(b.trim()) ||
ORIGIN_MAIN_BRANCH_PATTERN.test(b.trim()) ||
RELEASE_BRANCH_PATTERN.test(b.trim())
);
if (!branch) {
console.log(`${fail} no release branch exists for this ref`);
process.exit(1);
}
if (branch.startsWith('origin/')) branch = branch.substr('origin/'.length);
}
return branch.trim();
}
function chunkFilenames(filenames, offset = 0) {
// Windows has a max command line length of 2047 characters, so we can't
// provide too many filenames without going over that. To work around that,
// chunk up a list of filenames such that it won't go over that limit when
// used as args. Other platforms may have higher limits, but 4095 might be
// the limit on Linux systems according to `termios(3)`, so cap it there.
const MAX_FILENAME_ARGS_LENGTH = (os.platform() === 'win32' ? 2047 : 4095) - offset;
return filenames.reduce(
(chunkedFilenames, filename) => {
const currChunk = chunkedFilenames[chunkedFilenames.length - 1];
const currChunkLength = currChunk.reduce((totalLength, _filename) => totalLength + _filename.length + 1, 0);
if (currChunkLength + filename.length + 1 > MAX_FILENAME_ARGS_LENGTH) {
chunkedFilenames.push([filename]);
} else {
currChunk.push(filename);
}
return chunkedFilenames;
},
[[]]
);
}
/**
* @param {string} top
* @param {(filename: string) => boolean} test
* @returns {Promise<string[]>}
*/
async function findMatchingFiles(top, test) {
return fs.promises.readdir(top, { encoding: 'utf8', recursive: true }).then((files) => {
return files
.filter((name) => path.basename(name) !== '.bin')
.filter((name) => test(name))
.map((name) => path.join(top, name));
});
}
function compareVersions(v1, v2) {
const [split1, split2] = [v1.split('.'), v2.split('.')];
if (split1.length !== split2.length) {
throw new Error(`Expected version strings to have same number of sections: ${split1} and ${split2}`);
}
for (let i = 0; i < split1.length; i++) {
const p1 = parseInt(split1[i], 10);
const p2 = parseInt(split2[i], 10);
if (p1 > p2) return 1;
else if (p1 < p2) return -1;
// Continue checking the value if this portion is equal
}
return 0;
}
function getChromiumVersionFromDEPS(depsContent) {
return CHROMIUM_VERSION_DEPS_REGEX.exec(depsContent)?.[1] ?? null;
}
function getDepotToolsEnv() {
let depotToolsEnv;
const findDepotToolsOnPath = () => {
const result = childProcess.spawnSync(os.platform() === 'win32' ? 'where' : 'which', ['gclient']);
if (result.status === 0) {
return process.env;
}
};
const checkForBuildTools = () => {
const result = childProcess.spawnSync('electron-build-tools', ['show', 'env', '--json'], { shell: true });
if (result.status === 0) {
return {
...process.env,
...JSON.parse(result.stdout.toString().trim())
};
}
};
try {
depotToolsEnv = checkForBuildTools();
if (!depotToolsEnv) depotToolsEnv = findDepotToolsOnPath();
} catch {}
if (!depotToolsEnv) {
throw new Error("Couldn't find depot_tools, ensure it's on your PATH");
}
return depotToolsEnv;
}
module.exports = {
chunkFilenames,
compareVersions,
findMatchingFiles,
getChromiumVersionFromDEPS,
getCurrentBranch,
getDepotToolsEnv,
getElectronExec,
getOutDir,
getAbsoluteElectronExec,
handleGitCall,
ELECTRON_DIR,
SRC_DIR
};