blob: d759f5a6b76cba71310da8100fefea0d1ac0efba [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as fs from 'fs';
import * as path from 'path';
import {Chroot, CrosOut} from './common_util';
// Wraps functions in fs or fs.promises, adding prefix to given paths.
export class WrapFs<T extends string> {
constructor(readonly root: T) {}
realpath(p: string): string {
if (p.startsWith(this.root)) {
return p;
}
return path.join(this.root, p);
}
async copyFile(p: string, dest: string): Promise<void> {
return fs.promises.copyFile(this.realpath(p), dest);
}
async stat(p: string): Promise<fs.Stats> {
return fs.promises.stat(this.realpath(p));
}
existsSync(p: string): boolean {
return fs.existsSync(this.realpath(p));
}
async readdir(p: string): Promise<string[]> {
return fs.promises.readdir(this.realpath(p));
}
async rm(p: string, opts?: {force?: boolean}): Promise<void> {
return fs.promises.rm(this.realpath(p), opts);
}
watchSync(
p: string,
listener: (eventType: string, fileName: string | null) => void
): void {
fs.watch(this.realpath(p), listener);
}
}
/**
* @returns Boards that have been set up, ordered by access time (newest to
* oldest).
*/
export async function getSetupBoardsRecentFirst(
chroot: WrapFs<Chroot>,
out: WrapFs<CrosOut>
): Promise<string[]> {
return getSetupBoardsOrdered(
chroot,
out,
async (fs, dir) => fs.stat(dir),
(a, b) => b.atimeMs - a.atimeMs
);
}
/**
* @returns Boards that have been set up in alphabetic order.
*/
export async function getSetupBoardsAlphabetic(
chroot: WrapFs<Chroot>,
out: WrapFs<CrosOut>
): Promise<string[]> {
return getSetupBoardsOrdered(
chroot,
out,
async (_fs, dir) => dir,
(a, b) => a.localeCompare(b)
);
}
async function getSetupBoardsOrdered<T>(
chroot: WrapFs<Chroot>,
out: WrapFs<CrosOut>,
keyFn: (fs: WrapFs<Chroot | CrosOut>, dir: string) => Promise<T>,
compareFn: (a: T, b: T) => number
): Promise<string[]> {
const res = [];
for (const fs of [chroot, out]) {
res.push(...(await getSetupBoardsOrderedInner(fs, keyFn, compareFn)));
}
return res;
}
async function getSetupBoardsOrderedInner<T, F extends Chroot | CrosOut>(
fs: WrapFs<F>,
keyFn: (fs: WrapFs<F>, dir: string) => Promise<T>,
compareFn: (a: T, b: T) => number
): Promise<string[]> {
const build = '/build';
// /build does not exist outside chroot, which causes problems in tests.
if (!fs.existsSync(build)) {
return [];
}
const dirs = await fs.readdir(build);
const dirStat: Array<[string, T]> = [];
for (const dir of dirs) {
// README file exists in chroot/build if it's a directory on which out/build
// is mounted in chroot.
if (dir === 'bin' || dir === 'README') {
continue;
}
dirStat.push([dir, await keyFn(fs, path.join(build, dir))]);
}
dirStat.sort(([, a], [, b]) => compareFn(a, b));
return dirStat.map(([x]) => x);
}