78 lines
2.8 KiB
TypeScript
78 lines
2.8 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import {Bar} from 'cli-progress';
|
|
import {cpus} from 'os';
|
|
import {exec} from 'shelljs';
|
|
|
|
const AVAILABLE_THREADS = Math.max(cpus().length - 1, 1);
|
|
|
|
type CallbackFunction = (file: string, code?: number, stdout?: string, stderr?: string) => void;
|
|
|
|
/**
|
|
* Run the provided commands in parallel for each provided file.
|
|
*
|
|
* A promise is returned, completed when the command has completed running for each file.
|
|
*/
|
|
export function runInParallel(providedFiles: string[], cmd: string, callback: CallbackFunction) {
|
|
return new Promise<void>((resolve) => {
|
|
if (providedFiles.length === 0) {
|
|
return resolve();
|
|
}
|
|
// The progress bar instance to use for progress tracking.
|
|
const progressBar =
|
|
new Bar({format: `[{bar}] ETA: {eta}s | {value}/{total} files`, clearOnComplete: true});
|
|
// A local copy of the files to run the command on.
|
|
const files = providedFiles.slice();
|
|
// An array to represent the current usage state of each of the threads for parallelization.
|
|
const threads = new Array<boolean>(AVAILABLE_THREADS).fill(false);
|
|
|
|
// Recursively run the command on the next available file from the list using the provided
|
|
// thread.
|
|
function runCommandInThread(thread: number) {
|
|
// Get the next file.
|
|
const file = files.pop();
|
|
// If no file was pulled from the array, return as there are no more files to run against.
|
|
if (!file) {
|
|
return;
|
|
}
|
|
|
|
exec(
|
|
`${cmd} ${file}`,
|
|
{async: true, silent: true},
|
|
(code, stdout, stderr) => {
|
|
// Run the provided callback function.
|
|
callback(file, code, stdout, stderr);
|
|
// Note in the progress bar another file being completed.
|
|
progressBar.increment(1);
|
|
// If more files exist in the list, run again to work on the next file,
|
|
// using the same slot.
|
|
if (files.length) {
|
|
return runCommandInThread(thread);
|
|
}
|
|
// If not more files are available, mark the thread as unused.
|
|
threads[thread] = false;
|
|
// If all of the threads are false, as they are unused, mark the progress bar
|
|
// completed and resolve the promise.
|
|
if (threads.every(active => !active)) {
|
|
progressBar.stop();
|
|
resolve();
|
|
}
|
|
},
|
|
);
|
|
// Mark the thread as in use as the command execution has been started.
|
|
threads[thread] = true;
|
|
}
|
|
|
|
// Start the progress bar
|
|
progressBar.start(files.length, 0);
|
|
// Start running the command on files from the least in each available thread.
|
|
threads.forEach((_, idx) => runCommandInThread(idx));
|
|
});
|
|
}
|