From 0888977c1339f09052dd73a1d4fb4e27a728be28 Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Tue, 17 Dec 2024 09:45:19 -0800 Subject: [PATCH] fix(gradle): cache gradle report (#29381) ## Current Behavior `./gradlew projectReport` is run everytime Nx calculates the graph. ## Expected Behavior Results of `./gradlew projectReport` are cached until gradle files have been changed. This improves the performance of the gradle graph plugin. ## Related Issue(s) Fixes # --- .../gradle/src/utils/get-gradle-report.ts | 116 +++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/packages/gradle/src/utils/get-gradle-report.ts b/packages/gradle/src/utils/get-gradle-report.ts index 254e8b16c36bc..f9de425ba8701 100644 --- a/packages/gradle/src/utils/get-gradle-report.ts +++ b/packages/gradle/src/utils/get-gradle-report.ts @@ -4,7 +4,9 @@ import { join, relative } from 'node:path'; import { AggregateCreateNodesError, normalizePath, + readJsonFile, workspaceRoot, + writeJsonFile, } from '@nx/devkit'; import { hashWithWorkspaceContext } from 'nx/src/utils/workspace-context'; @@ -15,6 +17,7 @@ import { fileSeparator, newLineSeparator, } from './get-project-report-lines'; +import { workspaceDataDirectory } from 'nx/src/utils/cache-directory'; export interface GradleReport { gradleFileToGradleProjectMap: Map; @@ -27,8 +30,111 @@ export interface GradleReport { gradleProjectToChildProjects: Map; } +export interface GradleReportJSON { + hash: string; + gradleFileToGradleProjectMap: Record; + buildFileToDepsMap: Record; + gradleFileToOutputDirsMap: Record>; + gradleProjectToTasksTypeMap: Record>; + gradleProjectToTasksMap: Record>; + gradleProjectToProjectName: Record; + gradleProjectNameToProjectRootMap: Record; + gradleProjectToChildProjects: Record; +} + +function readGradleReportCache( + cachePath: string, + hash: string +): GradleReport | undefined { + const gradleReportJson: Partial = existsSync(cachePath) + ? readJsonFile(cachePath) + : undefined; + if (!gradleReportJson || gradleReportJson.hash !== hash) { + return; + } + let results: GradleReport = { + gradleFileToGradleProjectMap: new Map( + Object.entries(gradleReportJson['gradleFileToGradleProjectMap']) + ), + buildFileToDepsMap: new Map( + Object.entries(gradleReportJson['buildFileToDepsMap']) + ), + gradleFileToOutputDirsMap: new Map( + Object.entries(gradleReportJson['gradleFileToOutputDirsMap']).map( + ([key, value]) => [key, new Map(Object.entries(value))] + ) + ), + gradleProjectToTasksTypeMap: new Map( + Object.entries(gradleReportJson['gradleProjectToTasksTypeMap']).map( + ([key, value]) => [key, new Map(Object.entries(value))] + ) + ), + gradleProjectToTasksMap: new Map( + Object.entries(gradleReportJson['gradleProjectToTasksMap']).map( + ([key, value]) => [key, new Set(value)] + ) + ), + gradleProjectToProjectName: new Map( + Object.entries(gradleReportJson['gradleProjectToProjectName']) + ), + gradleProjectNameToProjectRootMap: new Map( + Object.entries(gradleReportJson['gradleProjectNameToProjectRootMap']) + ), + gradleProjectToChildProjects: new Map( + Object.entries(gradleReportJson['gradleProjectToChildProjects']) + ), + }; + return results; +} + +export function writeGradleReportToCache( + cachePath: string, + results: GradleReport +) { + let gradleReportJson: GradleReportJSON = { + hash: gradleCurrentConfigHash, + gradleFileToGradleProjectMap: Object.fromEntries( + results.gradleFileToGradleProjectMap + ), + buildFileToDepsMap: Object.fromEntries(results.buildFileToDepsMap), + gradleFileToOutputDirsMap: Object.fromEntries( + Array.from(results.gradleFileToOutputDirsMap).map(([key, value]) => [ + key, + Object.fromEntries(value), + ]) + ), + gradleProjectToTasksTypeMap: Object.fromEntries( + Array.from(results.gradleProjectToTasksTypeMap).map(([key, value]) => [ + key, + Object.fromEntries(value), + ]) + ), + gradleProjectToTasksMap: Object.fromEntries( + Array.from(results.gradleProjectToTasksMap).map(([key, value]) => [ + key, + Array.from(value), + ]) + ), + gradleProjectToProjectName: Object.fromEntries( + results.gradleProjectToProjectName + ), + gradleProjectNameToProjectRootMap: Object.fromEntries( + results.gradleProjectNameToProjectRootMap + ), + gradleProjectToChildProjects: Object.fromEntries( + results.gradleProjectToChildProjects + ), + }; + + writeJsonFile(cachePath, gradleReportJson); +} + let gradleReportCache: GradleReport; let gradleCurrentConfigHash: string; +let gradleReportCachePath: string = join( + workspaceDataDirectory, + 'gradle-report.hash' +); export function getCurrentGradleReport() { if (!gradleReportCache) { @@ -64,7 +170,14 @@ export async function populateGradleReport( const gradleConfigHash = await hashWithWorkspaceContext(workspaceRoot, [ gradleConfigAndTestGlob, ]); - if (gradleReportCache && gradleConfigHash === gradleCurrentConfigHash) { + gradleReportCache ??= readGradleReportCache( + gradleReportCachePath, + gradleConfigHash + ); + if ( + gradleReportCache && + (!gradleCurrentConfigHash || gradleConfigHash === gradleCurrentConfigHash) + ) { return; } @@ -92,6 +205,7 @@ export async function populateGradleReport( ); gradleCurrentConfigHash = gradleConfigHash; gradleReportCache = processProjectReports(projectReportLines); + writeGradleReportToCache(gradleReportCachePath, gradleReportCache); } export function processProjectReports(