UNPKG

@nx/gradle

Version:

The Nx Plugin for Gradle allows Gradle tasks to be run through Nx

178 lines (177 loc) 7.42 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.batchRunnerPath = void 0; exports.default = gradleBatch; exports.getGradlewTasksToRun = getGradlewTasksToRun; const devkit_1 = require("@nx/devkit"); const exec_gradle_1 = require("../../utils/exec-gradle"); const path_1 = require("path"); const child_process_1 = require("child_process"); const readline_1 = require("readline"); const get_exclude_task_1 = require("./get-exclude-task"); exports.batchRunnerPath = (0, path_1.join)(__dirname, '../../../batch-runner/build/libs/gradle-batch-runner-all.jar'); async function* gradleBatch(taskGraph, inputs, overrides, context) { const projectName = taskGraph.tasks[taskGraph.roots[0]]?.target?.project; const projectRoot = context.projectGraph.nodes[projectName]?.data?.root ?? ''; const customGradleExecutableDirectory = (0, exec_gradle_1.getCustomGradleExecutableDirectoryFromPlugin)(context.nxJsonConfiguration); let gradlewPath = (0, exec_gradle_1.findGradlewFile)((0, path_1.join)(projectRoot, 'project.json'), devkit_1.workspaceRoot, customGradleExecutableDirectory); gradlewPath = (0, path_1.join)(context.root, gradlewPath); const root = (0, path_1.dirname)(gradlewPath); // set args with passed in args and overrides in command line const input = inputs[taskGraph.roots[0]]; const args = typeof input.args === 'string' ? input.args.trim().split(' ') : Array.isArray(input.args) ? input.args : []; if (overrides.__overrides_unparsed__.length) { args.push(...overrides.__overrides_unparsed__); } const taskIds = Object.keys(taskGraph.tasks); const { gradlewTasksToRun, excludeTasks, excludeTestTasks } = getGradlewTasksToRun(taskIds, taskGraph, inputs, context.projectGraph.nodes, context.taskGraph ?? taskGraph); const yielded = new Set(); try { for await (const event of streamTasksInBatch(gradlewTasksToRun, excludeTasks, excludeTestTasks, args, root)) { yielded.add(event.task); yield event; } } catch (e) { devkit_1.output.error({ title: `Gradlew batch failed`, bodyLines: [e.toString()], }); for (const taskId of taskIds) { if (!yielded.has(taskId)) { yielded.add(taskId); yield { task: taskId, result: { success: false, terminalOutput: e.toString() }, }; } } } } function getGradlewTasksToRun(taskIds, taskGraph, inputs, nodes, fullTaskGraph = taskGraph) { const tasksWithExcludeIds = new Set(); const testTasksWithExcludeIds = new Set(); const tasksWithoutExcludeIds = new Set(); const gradlewTasksToRun = {}; const includeDependsOnTasks = new Set(); for (const taskId of taskIds) { const input = inputs[taskId]; gradlewTasksToRun[taskId] = input; if (input.includeDependsOnTasks) { for (const t of input.includeDependsOnTasks) { includeDependsOnTasks.add(t); } } if (input.excludeDependsOn) { if (input.testClassName) { testTasksWithExcludeIds.add(taskId); } else { tasksWithExcludeIds.add(taskId); } } else { tasksWithoutExcludeIds.add(taskId); } } const runningTaskIds = new Set(taskIds); for (const depId of (0, get_exclude_task_1.getAllDependsOnFromTaskGraph)(tasksWithoutExcludeIds, fullTaskGraph)) { runningTaskIds.add(depId); } const excludeTasks = (0, get_exclude_task_1.getExcludeTasksFromTaskGraph)(tasksWithExcludeIds, runningTaskIds, fullTaskGraph, nodes, includeDependsOnTasks); const excludeTestTasks = (0, get_exclude_task_1.getExcludeTasksFromTaskGraph)(testTasksWithExcludeIds, new Set(), fullTaskGraph, nodes); return { gradlewTasksToRun, excludeTasks, excludeTestTasks, }; } async function* streamTasksInBatch(gradlewTasksToRun, excludeTasks, excludeTestTasks, args, root) { const gradlewBatchStart = performance.mark(`gradlew-batch:start`); const debugOptions = (process.env.NX_GRADLE_BATCH_DEBUG ?? '').trim(); const spawnArgs = [ ...(debugOptions ? debugOptions.split(/\s+/) : []), '-jar', exports.batchRunnerPath, `--tasks=${JSON.stringify(gradlewTasksToRun)}`, `--workspaceRoot=${root}`, `--args=${args.join(' ').replaceAll("'", '"')}`, `--excludeTasks=${Array.from(excludeTasks).join(',')}`, `--excludeTestTasks=${Array.from(excludeTestTasks).join(',')}`, ...(process.env.NX_VERBOSE_LOGGING === 'true' ? [] : ['--quiet']), ]; // stderr is inherited so Gradle output (tee'd to System.err by TeeOutputStream) // and logger output flow to the terminal in real-time. // stdout is piped so we can read NX_RESULT lines emitted per task. const cp = (0, child_process_1.spawn)('java', spawnArgs, { cwd: devkit_1.workspaceRoot, windowsHide: true, env: process.env, stdio: ['pipe', 'pipe', 'inherit'], }); const exit = new Promise((resolve, reject) => { cp.on('error', reject); cp.on('close', (code) => resolve(code ?? 0)); }); const rl = (0, readline_1.createInterface)({ input: cp.stdout, crlfDelay: Infinity }); // Drain stdout into a queue eagerly. Yielding inside the readline loop creates // back-pressure: if the consumer is slow, `yield` blocks, readline pauses, the // OS pipe between Java and Node fills, and Java's NX_RESULT println blocks on // a full pipe — the JVM hangs even though all task work is done. const queue = []; let notifyAvailable = null; let readerClosed = false; rl.on('line', (line) => { if (!line.startsWith('NX_RESULT:')) return; try { const data = JSON.parse(line.slice('NX_RESULT:'.length)); queue.push({ task: data.task, result: { success: data.result.success ?? false, status: data.result.status, terminalOutput: data.result.terminalOutput ?? '', startTime: data.result.startTime, endTime: data.result.endTime, }, }); } catch (e) { console.error('[Gradle Batch] Failed to parse result line:', line, e); return; } notifyAvailable?.(); notifyAvailable = null; }); rl.on('close', () => { readerClosed = true; notifyAvailable?.(); notifyAvailable = null; }); try { while (!readerClosed || queue.length > 0) { if (queue.length > 0) { yield queue.shift(); } else { await new Promise((resolve) => { notifyAvailable = resolve; }); } } } finally { rl.close(); } const code = await exit; const gradlewBatchEnd = performance.mark(`gradlew-batch:end`); performance.measure(`gradlew-batch`, gradlewBatchStart.name, gradlewBatchEnd.name); if (code !== 0) { throw new Error(`Gradle batch runner exited with code ${code}`); } }