feat(core): able to import gradle project (#27645)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
This commit is contained in:
parent
5bbaffbda8
commit
61b3503619
175
e2e/gradle/src/gradle-import.test.ts
Normal file
175
e2e/gradle/src/gradle-import.test.ts
Normal file
@ -0,0 +1,175 @@
|
||||
import {
|
||||
checkFilesExist,
|
||||
cleanupProject,
|
||||
getSelectedPackageManager,
|
||||
newProject,
|
||||
runCLI,
|
||||
updateJson,
|
||||
updateFile,
|
||||
e2eCwd,
|
||||
readJson,
|
||||
} from '@nx/e2e/utils';
|
||||
import { mkdirSync, rmdirSync, writeFileSync } from 'fs';
|
||||
import { execSync } from 'node:child_process';
|
||||
import { join } from 'path';
|
||||
import { createGradleProject } from './utils/create-gradle-project';
|
||||
import { createFileSync } from 'fs-extra';
|
||||
|
||||
describe('Nx Import Gradle', () => {
|
||||
let proj: string;
|
||||
const tempImportE2ERoot = join(e2eCwd, 'nx-import');
|
||||
beforeAll(() => {
|
||||
proj = newProject({
|
||||
packages: ['@nx/js'],
|
||||
unsetProjectNameAndRootFormat: false,
|
||||
});
|
||||
|
||||
if (getSelectedPackageManager() === 'pnpm') {
|
||||
updateFile(
|
||||
'pnpm-workspace.yaml',
|
||||
`packages:
|
||||
- 'projects/*'
|
||||
`
|
||||
);
|
||||
} else {
|
||||
updateJson('package.json', (json) => {
|
||||
json.workspaces = ['projects/*'];
|
||||
return json;
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
rmdirSync(tempImportE2ERoot);
|
||||
} catch {}
|
||||
mkdirSync(tempImportE2ERoot, { recursive: true });
|
||||
});
|
||||
afterAll(() => cleanupProject());
|
||||
|
||||
it('should be able to import a kotlin gradle app', () => {
|
||||
const tempGradleProjectName = 'created-gradle-app-kotlin';
|
||||
const tempGraldeProjectPath = join(
|
||||
tempImportE2ERoot,
|
||||
tempGradleProjectName
|
||||
);
|
||||
try {
|
||||
rmdirSync(tempGraldeProjectPath);
|
||||
} catch {}
|
||||
mkdirSync(tempGraldeProjectPath, { recursive: true });
|
||||
createGradleProject(
|
||||
tempGradleProjectName,
|
||||
'kotlin',
|
||||
tempGraldeProjectPath,
|
||||
'gradleProjectKotlin',
|
||||
'kotlin-'
|
||||
);
|
||||
// Add project.json files to the gradle project to avoid duplicate project names
|
||||
createFileSync(join(tempGraldeProjectPath, 'project.json'));
|
||||
writeFileSync(
|
||||
join(tempGraldeProjectPath, 'project.json'),
|
||||
`{"name": "${tempGradleProjectName}"}`
|
||||
);
|
||||
|
||||
execSync(`git init`, {
|
||||
cwd: tempGraldeProjectPath,
|
||||
});
|
||||
execSync(`git add .`, {
|
||||
cwd: tempGraldeProjectPath,
|
||||
});
|
||||
execSync(`git commit -am "initial commit"`, {
|
||||
cwd: tempGraldeProjectPath,
|
||||
});
|
||||
execSync(`git checkout -b main`, {
|
||||
cwd: tempGraldeProjectPath,
|
||||
});
|
||||
|
||||
const remote = tempGraldeProjectPath;
|
||||
const ref = 'main';
|
||||
const source = '.';
|
||||
const directory = 'projects/gradle-app-kotlin';
|
||||
|
||||
runCLI(
|
||||
`import ${remote} ${directory} --ref ${ref} --source ${source} --no-interactive`,
|
||||
{
|
||||
verbose: true,
|
||||
}
|
||||
);
|
||||
|
||||
checkFilesExist(
|
||||
`${directory}/settings.gradle.kts`,
|
||||
`${directory}/build.gradle.kts`,
|
||||
`${directory}/gradlew`,
|
||||
`${directory}/gradlew.bat`
|
||||
);
|
||||
const nxJson = readJson('nx.json');
|
||||
const gradlePlugin = nxJson.plugins.find(
|
||||
(plugin) => plugin.plugin === '@nx/gradle'
|
||||
);
|
||||
expect(gradlePlugin).toBeDefined();
|
||||
expect(() => {
|
||||
runCLI(`show projects`);
|
||||
runCLI('build kotlin-app');
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should be able to import a groovy gradle app', () => {
|
||||
const tempGradleProjectName = 'created-gradle-app-groovy';
|
||||
const tempGraldeProjectPath = join(
|
||||
tempImportE2ERoot,
|
||||
tempGradleProjectName
|
||||
);
|
||||
try {
|
||||
rmdirSync(tempGraldeProjectPath);
|
||||
} catch {}
|
||||
mkdirSync(tempGraldeProjectPath, { recursive: true });
|
||||
createGradleProject(
|
||||
tempGradleProjectName,
|
||||
'groovy',
|
||||
tempGraldeProjectPath,
|
||||
'gradleProjectGroovy',
|
||||
'groovy-'
|
||||
);
|
||||
// Add project.json files to the gradle project to avoid duplicate project names
|
||||
createFileSync(join(tempGraldeProjectPath, 'project.json'));
|
||||
writeFileSync(
|
||||
join(tempGraldeProjectPath, 'project.json'),
|
||||
`{"name": "${tempGradleProjectName}"}`
|
||||
);
|
||||
|
||||
execSync(`git init`, {
|
||||
cwd: tempGraldeProjectPath,
|
||||
});
|
||||
execSync(`git add .`, {
|
||||
cwd: tempGraldeProjectPath,
|
||||
});
|
||||
execSync(`git commit -am "initial commit"`, {
|
||||
cwd: tempGraldeProjectPath,
|
||||
});
|
||||
execSync(`git checkout -b main`, {
|
||||
cwd: tempGraldeProjectPath,
|
||||
});
|
||||
|
||||
const remote = tempGraldeProjectPath;
|
||||
const ref = 'main';
|
||||
const source = '.';
|
||||
const directory = 'projects/gradle-app-groovy';
|
||||
|
||||
runCLI(
|
||||
`import ${remote} ${directory} --ref ${ref} --source ${source} --no-interactive`,
|
||||
{
|
||||
verbose: true,
|
||||
}
|
||||
);
|
||||
runCLI(`g @nx/gradle:init --no-interactive`);
|
||||
|
||||
checkFilesExist(
|
||||
`${directory}/build.gradle`,
|
||||
`${directory}/settings.gradle`,
|
||||
`${directory}/gradlew`,
|
||||
`${directory}/gradlew.bat`
|
||||
);
|
||||
expect(() => {
|
||||
runCLI(`show projects`);
|
||||
runCLI('build groovy-app');
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
@ -2,17 +2,13 @@ import {
|
||||
checkFilesExist,
|
||||
cleanupProject,
|
||||
createFile,
|
||||
e2eConsoleLogger,
|
||||
isWindows,
|
||||
newProject,
|
||||
runCLI,
|
||||
runCommand,
|
||||
tmpProjPath,
|
||||
uniq,
|
||||
updateFile,
|
||||
} from '@nx/e2e/utils';
|
||||
import { execSync } from 'child_process';
|
||||
import { resolve } from 'path';
|
||||
|
||||
import { createGradleProject } from './utils/create-gradle-project';
|
||||
|
||||
describe('Gradle', () => {
|
||||
describe.each([{ type: 'kotlin' }, { type: 'groovy' }])(
|
||||
@ -22,6 +18,7 @@ describe('Gradle', () => {
|
||||
beforeAll(() => {
|
||||
newProject();
|
||||
createGradleProject(gradleProjectName, type);
|
||||
runCLI(`add @nx/gradle`);
|
||||
});
|
||||
afterAll(() => cleanupProject());
|
||||
|
||||
@ -101,33 +98,3 @@ dependencies {
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
function createGradleProject(
|
||||
projectName: string,
|
||||
type: 'kotlin' | 'groovy' = 'kotlin'
|
||||
) {
|
||||
e2eConsoleLogger(`Using java version: ${execSync('java -version')}`);
|
||||
const gradleCommand = isWindows()
|
||||
? resolve(`${__dirname}/../gradlew.bat`)
|
||||
: resolve(`${__dirname}/../gradlew`);
|
||||
e2eConsoleLogger(
|
||||
'Using gradle version: ' +
|
||||
execSync(`${gradleCommand} --version`, {
|
||||
cwd: tmpProjPath(),
|
||||
})
|
||||
);
|
||||
e2eConsoleLogger(
|
||||
execSync(`${gradleCommand} help --task :init`, {
|
||||
cwd: tmpProjPath(),
|
||||
}).toString()
|
||||
);
|
||||
e2eConsoleLogger(
|
||||
runCommand(
|
||||
`${gradleCommand} init --type ${type}-application --dsl ${type} --project-name ${projectName} --package gradleProject --no-incubating --split-project`,
|
||||
{
|
||||
cwd: tmpProjPath(),
|
||||
}
|
||||
)
|
||||
);
|
||||
runCLI(`add @nx/gradle`);
|
||||
}
|
||||
|
||||
59
e2e/gradle/src/utils/create-gradle-project.ts
Normal file
59
e2e/gradle/src/utils/create-gradle-project.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import {
|
||||
e2eConsoleLogger,
|
||||
isWindows,
|
||||
runCommand,
|
||||
tmpProjPath,
|
||||
} from '@nx/e2e/utils';
|
||||
import { execSync } from 'child_process';
|
||||
import { createFileSync, writeFileSync } from 'fs-extra';
|
||||
import { join, resolve } from 'path';
|
||||
|
||||
export function createGradleProject(
|
||||
projectName: string,
|
||||
type: 'kotlin' | 'groovy' = 'kotlin',
|
||||
cwd: string = tmpProjPath(),
|
||||
packageName: string = 'gradleProject',
|
||||
addProjectJsonNamePrefix: string = ''
|
||||
) {
|
||||
e2eConsoleLogger(`Using java version: ${execSync('java -version')}`);
|
||||
const gradleCommand = isWindows()
|
||||
? resolve(`${__dirname}/../../gradlew.bat`)
|
||||
: resolve(`${__dirname}/../../gradlew`);
|
||||
e2eConsoleLogger(
|
||||
'Using gradle version: ' +
|
||||
execSync(`${gradleCommand} --version`, {
|
||||
cwd,
|
||||
})
|
||||
);
|
||||
e2eConsoleLogger(
|
||||
execSync(`${gradleCommand} help --task :init`, {
|
||||
cwd,
|
||||
}).toString()
|
||||
);
|
||||
e2eConsoleLogger(
|
||||
runCommand(
|
||||
`${gradleCommand} init --type ${type}-application --dsl ${type} --project-name ${projectName} --package ${packageName} --no-incubating --split-project`,
|
||||
{
|
||||
cwd,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if (addProjectJsonNamePrefix) {
|
||||
createFileSync(join(cwd, 'app/project.json'));
|
||||
writeFileSync(
|
||||
join(cwd, 'app/project.json'),
|
||||
`{"name": "${addProjectJsonNamePrefix}app"}`
|
||||
);
|
||||
createFileSync(join(cwd, 'list/project.json'));
|
||||
writeFileSync(
|
||||
join(cwd, 'list/project.json'),
|
||||
`{"name": "${addProjectJsonNamePrefix}list"}`
|
||||
);
|
||||
createFileSync(join(cwd, 'utilities/project.json'));
|
||||
writeFileSync(
|
||||
join(cwd, 'utilities/project.json'),
|
||||
`{"name": "${addProjectJsonNamePrefix}utilities"}`
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "e2e-nx",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "e2e/nx-misc",
|
||||
"sourceRoot": "e2e/nx",
|
||||
"projectType": "application",
|
||||
"implicitDependencies": ["nx", "js"],
|
||||
"// targets": "to see all targets run: nx show project e2e-nx --web",
|
||||
|
||||
@ -9,7 +9,6 @@ import {
|
||||
Tree,
|
||||
updateNxJson,
|
||||
} from '@nx/devkit';
|
||||
import { execSync } from 'child_process';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
import { InitGeneratorSchema } from './schema';
|
||||
import { hasGradlePlugin } from '../../utils/has-gradle-plugin';
|
||||
@ -18,13 +17,6 @@ import { dirname, join, basename } from 'path';
|
||||
export async function initGenerator(tree: Tree, options: InitGeneratorSchema) {
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
if (!tree.exists('settings.gradle') && !tree.exists('settings.gradle.kts')) {
|
||||
logger.warn(`Could not find 'settings.gradle' or 'settings.gradle.kts' file in your gradle workspace.
|
||||
A Gradle build should contain a 'settings.gradle' or 'settings.gradle.kts' file in its root directory. It may also contain a 'build.gradle' or 'build.gradle.kts' file.
|
||||
Running 'gradle init':`);
|
||||
execSync('gradle init', { stdio: 'inherit' });
|
||||
}
|
||||
|
||||
if (!options.skipPackageJson && tree.exists('package.json')) {
|
||||
tasks.push(
|
||||
addDependenciesToPackageJson(
|
||||
|
||||
@ -9,11 +9,9 @@ import {
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { basename, dirname } from 'node:path';
|
||||
|
||||
import {
|
||||
GRADLE_BUILD_FILES,
|
||||
getCurrentGradleReport,
|
||||
newLineSeparator,
|
||||
} from '../utils/get-gradle-report';
|
||||
import { getCurrentGradleReport } from '../utils/get-gradle-report';
|
||||
import { GRADLE_BUILD_FILES } from '../utils/split-config-files';
|
||||
import { newLineSeparator } from '../utils/get-project-report-lines';
|
||||
|
||||
export const createDependencies: CreateDependencies = async (
|
||||
_,
|
||||
|
||||
@ -116,6 +116,9 @@ describe('@nx/gradle/plugin', () => {
|
||||
"gradle",
|
||||
],
|
||||
},
|
||||
"options": {
|
||||
"cwd": ".",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -200,6 +203,9 @@ describe('@nx/gradle/plugin', () => {
|
||||
"gradle",
|
||||
],
|
||||
},
|
||||
"options": {
|
||||
"cwd": ".",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -310,6 +316,9 @@ describe('@nx/gradle/plugin', () => {
|
||||
"gradle",
|
||||
],
|
||||
},
|
||||
"options": {
|
||||
"cwd": ".",
|
||||
},
|
||||
},
|
||||
"test-ci": {
|
||||
"cache": true,
|
||||
@ -379,6 +388,9 @@ describe('@nx/gradle/plugin', () => {
|
||||
"gradle",
|
||||
],
|
||||
},
|
||||
"options": {
|
||||
"cwd": ".",
|
||||
},
|
||||
},
|
||||
"test-ci--bTest": {
|
||||
"cache": true,
|
||||
@ -406,6 +418,9 @@ describe('@nx/gradle/plugin', () => {
|
||||
"gradle",
|
||||
],
|
||||
},
|
||||
"options": {
|
||||
"cwd": ".",
|
||||
},
|
||||
},
|
||||
"test-ci--cTests": {
|
||||
"cache": true,
|
||||
@ -433,6 +448,9 @@ describe('@nx/gradle/plugin', () => {
|
||||
"gradle",
|
||||
],
|
||||
},
|
||||
"options": {
|
||||
"cwd": ".",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -16,16 +16,18 @@ import { basename, dirname, join } from 'node:path';
|
||||
import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
|
||||
import { findProjectForPath } from 'nx/src/devkit-internals';
|
||||
|
||||
import { getGradleExecFile } from '../utils/exec-gradle';
|
||||
import {
|
||||
populateGradleReport,
|
||||
getCurrentGradleReport,
|
||||
GradleReport,
|
||||
gradleConfigGlob,
|
||||
GRADLE_BUILD_FILES,
|
||||
gradleConfigAndTestGlob,
|
||||
} from '../utils/get-gradle-report';
|
||||
import { hashObject } from 'nx/src/hasher/file-hasher';
|
||||
import {
|
||||
gradleConfigAndTestGlob,
|
||||
gradleConfigGlob,
|
||||
splitConfigFiles,
|
||||
} from '../utils/split-config-files';
|
||||
import { getGradleExecFile, findGraldewFile } from '../utils/exec-gradle';
|
||||
|
||||
const cacheableTaskType = new Set(['Build', 'Verification']);
|
||||
const dependsOnMap = {
|
||||
@ -76,7 +78,8 @@ export function writeTargetsToCache(cachePath: string, results: GradleTargets) {
|
||||
export const createNodesV2: CreateNodesV2<GradlePluginOptions> = [
|
||||
gradleConfigAndTestGlob,
|
||||
async (files, options, context) => {
|
||||
const { configFiles, projectRoots, testFiles } = splitConfigFiles(files);
|
||||
const { buildFiles, projectRoots, gradlewFiles, testFiles } =
|
||||
splitConfigFiles(files);
|
||||
const optionsHash = hashObject(options);
|
||||
const cachePath = join(
|
||||
workspaceDataDirectory,
|
||||
@ -84,7 +87,10 @@ export const createNodesV2: CreateNodesV2<GradlePluginOptions> = [
|
||||
);
|
||||
const targetsCache = readTargetsCache(cachePath);
|
||||
|
||||
await populateGradleReport(context.workspaceRoot);
|
||||
await populateGradleReport(
|
||||
context.workspaceRoot,
|
||||
gradlewFiles.map((f) => join(context.workspaceRoot, f))
|
||||
);
|
||||
const gradleReport = getCurrentGradleReport();
|
||||
const gradleProjectRootToTestFilesMap = getGradleProjectRootToTestFilesMap(
|
||||
testFiles,
|
||||
@ -98,7 +104,7 @@ export const createNodesV2: CreateNodesV2<GradlePluginOptions> = [
|
||||
targetsCache,
|
||||
gradleProjectRootToTestFilesMap
|
||||
),
|
||||
configFiles,
|
||||
buildFiles,
|
||||
options,
|
||||
context
|
||||
);
|
||||
@ -151,15 +157,16 @@ export const makeCreateNodesForGradleConfigFile =
|
||||
*/
|
||||
export const createNodes: CreateNodes<GradlePluginOptions> = [
|
||||
gradleConfigGlob,
|
||||
async (configFile, options, context) => {
|
||||
async (buildFile, options, context) => {
|
||||
logger.warn(
|
||||
'`createNodes` is deprecated. Update your plugin to utilize createNodesV2 instead. In Nx 20, this will change to the createNodesV2 API.'
|
||||
);
|
||||
await populateGradleReport(context.workspaceRoot);
|
||||
const { gradlewFiles } = splitConfigFiles(context.configFiles);
|
||||
await populateGradleReport(context.workspaceRoot, gradlewFiles);
|
||||
const gradleReport = getCurrentGradleReport();
|
||||
const internalCreateNodes =
|
||||
makeCreateNodesForGradleConfigFile(gradleReport);
|
||||
return await internalCreateNodes(configFile, options, context);
|
||||
return await internalCreateNodes(buildFile, options, context);
|
||||
},
|
||||
];
|
||||
|
||||
@ -234,13 +241,16 @@ async function createGradleTargets(
|
||||
context: CreateNodesContext,
|
||||
outputDirs: Map<string, string>,
|
||||
gradleProject: string,
|
||||
gradleFilePath: string,
|
||||
gradleBuildFilePath: string,
|
||||
testFiles: string[] = []
|
||||
): Promise<{
|
||||
targetGroups: Record<string, string[]>;
|
||||
targets: Record<string, TargetConfiguration>;
|
||||
}> {
|
||||
const inputsMap = createInputsMap(context);
|
||||
const gradlewFileDirectory = dirname(
|
||||
findGraldewFile(gradleBuildFilePath, context.workspaceRoot)
|
||||
);
|
||||
|
||||
const targets: Record<string, TargetConfiguration> = {};
|
||||
const targetGroups: Record<string, string[]> = {};
|
||||
@ -262,15 +272,20 @@ async function createGradleTargets(
|
||||
outputs,
|
||||
task.type,
|
||||
targets,
|
||||
targetGroups
|
||||
targetGroups,
|
||||
gradlewFileDirectory
|
||||
);
|
||||
}
|
||||
|
||||
const taskCommandToRun = `${gradleProject ? gradleProject + ':' : ''}${
|
||||
task.name
|
||||
}`;
|
||||
|
||||
targets[targetName] = {
|
||||
command: `${getGradleExecFile()} ${taskCommandToRun}`,
|
||||
options: {
|
||||
cwd: gradlewFileDirectory,
|
||||
},
|
||||
cache: cacheableTaskType.has(task.type),
|
||||
inputs: inputsMap[task.name],
|
||||
dependsOn: dependsOnMap[task.name],
|
||||
@ -320,7 +335,8 @@ function getTestCiTargets(
|
||||
outputs: string[],
|
||||
targetGroupName: string,
|
||||
targets: Record<string, TargetConfiguration>,
|
||||
targetGroups: Record<string, string[]>
|
||||
targetGroups: Record<string, string[]>,
|
||||
gradlewFileDirectory: string
|
||||
): void {
|
||||
if (!testFiles || testFiles.length === 0 || !ciTargetName) {
|
||||
return;
|
||||
@ -338,6 +354,9 @@ function getTestCiTargets(
|
||||
|
||||
targets[targetName] = {
|
||||
command: `${getGradleExecFile()} ${taskCommandToRun} --tests ${testName}`,
|
||||
options: {
|
||||
cwd: gradlewFileDirectory,
|
||||
},
|
||||
cache: true,
|
||||
inputs,
|
||||
dependsOn: dependsOnMap['test'],
|
||||
@ -386,26 +405,6 @@ function getTestCiTargets(
|
||||
targetGroups[targetGroupName].push(ciTargetName);
|
||||
}
|
||||
|
||||
function splitConfigFiles(files: readonly string[]): {
|
||||
configFiles: string[];
|
||||
testFiles: string[];
|
||||
projectRoots: string[];
|
||||
} {
|
||||
const configFiles = [];
|
||||
const testFiles = [];
|
||||
const projectRoots = new Set<string>();
|
||||
files.forEach((file) => {
|
||||
if (GRADLE_BUILD_FILES.has(basename(file))) {
|
||||
configFiles.push(file);
|
||||
projectRoots.add(dirname(file));
|
||||
} else {
|
||||
testFiles.push(file);
|
||||
}
|
||||
});
|
||||
|
||||
return { configFiles, testFiles, projectRoots: Array.from(projectRoots) };
|
||||
}
|
||||
|
||||
function getGradleProjectRootToTestFilesMap(
|
||||
testFiles: string[],
|
||||
projectRoots: string[]
|
||||
|
||||
@ -0,0 +1,160 @@
|
||||
|
||||
------------------------------------------------------------
|
||||
Project ':app'
|
||||
------------------------------------------------------------
|
||||
|
||||
allprojects: [project ':app']
|
||||
ant: org.gradle.api.internal.project.DefaultAntBuilder@505598eb
|
||||
antBuilderFactory: org.gradle.api.internal.project.DefaultAntBuilderFactory@259d836f
|
||||
application: extension 'application'
|
||||
applicationDefaultJvmArgs: []
|
||||
applicationDistribution: org.gradle.api.internal.file.copy.DefaultCopySpec_Decorated@3978059a
|
||||
applicationName: app
|
||||
archivesBaseName: app
|
||||
artifacts: org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler_Decorated@32167ebb
|
||||
asDynamicObject: DynamicObject for project ':app'
|
||||
assemble: task ':app:assemble'
|
||||
assembleDist: task ':app:assembleDist'
|
||||
autoTargetJvmDisabled: false
|
||||
base: extension 'base'
|
||||
baseClassLoaderScope: org.gradle.api.internal.initialization.DefaultClassLoaderScope@3c4c27d8
|
||||
build: task ':app:build'
|
||||
buildDependents: task ':app:buildDependents'
|
||||
buildDir: /private/tmp/nx-e2e/nx/proj9912426/app/build
|
||||
buildEnvironment: task ':app:buildEnvironment'
|
||||
buildFile: /private/tmp/nx-e2e/nx/proj9912426/app/build.gradle
|
||||
buildNeeded: task ':app:buildNeeded'
|
||||
buildPath: :
|
||||
buildScriptSource: org.gradle.groovy.scripts.TextResourceScriptSource@4ef82ad2
|
||||
buildTreePath: :app
|
||||
buildscript: org.gradle.api.internal.initialization.DefaultScriptHandler_Decorated@7b75e99
|
||||
check: task ':app:check'
|
||||
childProjects: {}
|
||||
childProjectsUnchecked: {}
|
||||
class: class org.gradle.api.internal.project.DefaultProject_Decorated
|
||||
classLoaderScope: org.gradle.api.internal.initialization.DefaultClassLoaderScope@7e1acf20
|
||||
classes: task ':app:classes'
|
||||
clean: task ':app:clean'
|
||||
compileGroovy: task ':app:compileGroovy'
|
||||
compileJava: task ':app:compileJava'
|
||||
compileTestGroovy: task ':app:compileTestGroovy'
|
||||
compileTestJava: task ':app:compileTestJava'
|
||||
components: SoftwareComponent container
|
||||
configurationActions: org.gradle.configuration.project.DefaultProjectConfigurationActionContainer@7f7885fe
|
||||
configurationTargetIdentifier: org.gradle.configuration.ConfigurationTargetIdentifier$1@70598e79
|
||||
configurations: configuration container
|
||||
convention: org.gradle.internal.extensibility.DefaultConvention@4d27557d
|
||||
conventionMapping: org.gradle.internal.extensibility.ConventionAwareHelper@65ecf036
|
||||
crossProjectModelAccess: org.gradle.api.internal.project.DefaultCrossProjectModelAccess@7665f252
|
||||
defaultArtifacts: extension 'defaultArtifacts'
|
||||
defaultTasks: []
|
||||
deferredProjectConfiguration: org.gradle.api.internal.project.DeferredProjectConfiguration@5e80ce00
|
||||
dependencies: org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler_Decorated@6216b9ef
|
||||
dependencyFactory: org.gradle.api.internal.artifacts.DefaultDependencyFactory@15ed846d
|
||||
dependencyInsight: task ':app:dependencyInsight'
|
||||
dependencyLocking: org.gradle.internal.locking.DefaultDependencyLockingHandler_Decorated@1702aba6
|
||||
dependencyMetaDataProvider: org.gradle.internal.service.scopes.ProjectScopeServices$ProjectBackedModuleMetaDataProvider@4f2a8788
|
||||
dependencyReport: task ':app:dependencyReport'
|
||||
dependentComponents: task ':app:dependentComponents'
|
||||
depth: 1
|
||||
description: null
|
||||
detachedState: false
|
||||
displayName: project ':app'
|
||||
distTar: task ':app:distTar'
|
||||
distZip: task ':app:distZip'
|
||||
distributions: Distribution container
|
||||
distsDirName: distributions
|
||||
distsDirectory: extension 'base' property 'distsDirectory'
|
||||
docsDir: /private/tmp/nx-e2e/nx/proj9912426/app/build/docs
|
||||
docsDirName: docs
|
||||
executableDir: bin
|
||||
ext: org.gradle.internal.extensibility.DefaultExtraPropertiesExtension@18249643
|
||||
extensions: org.gradle.internal.extensibility.DefaultConvention@4d27557d
|
||||
fileOperations: org.gradle.api.internal.file.DefaultFileOperations@6193a7c6
|
||||
fileResolver: org.gradle.api.internal.file.BaseDirFileResolver@634e7fc9
|
||||
gradle: build 'my-gradle-project5165026'
|
||||
groovyRuntime: extension 'groovyRuntime'
|
||||
groovydoc: task ':app:groovydoc'
|
||||
group: my-gradle-project5165026
|
||||
help: task ':app:help'
|
||||
htmlDependencyReport: task ':app:htmlDependencyReport'
|
||||
identityPath: :app
|
||||
inheritedScope: org.gradle.internal.extensibility.ExtensibleDynamicObject$InheritedDynamicObject@7843a2ef
|
||||
installDist: task ':app:installDist'
|
||||
internalStatus: property(java.lang.Object, fixed(class java.lang.String, integration))
|
||||
jar: task ':app:jar'
|
||||
java: extension 'java'
|
||||
javaToolchains: extension 'javaToolchains'
|
||||
javadoc: task ':app:javadoc'
|
||||
layout: org.gradle.api.internal.file.DefaultProjectLayout@38b897f6
|
||||
libsDirName: libs
|
||||
libsDirectory: extension 'base' property 'libsDirectory'
|
||||
listenerBuildOperationDecorator: org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator@58c7b14b
|
||||
logger: org.gradle.internal.logging.slf4j.OutputEventListenerBackedLogger@2fb55781
|
||||
logging: org.gradle.internal.logging.services.DefaultLoggingManager@2818071f
|
||||
mainClassName: null
|
||||
model: project ':app'
|
||||
modelIdentityDisplayName: null
|
||||
modelRegistry: org.gradle.model.internal.registry.DefaultModelRegistry@4d299c29
|
||||
name: app
|
||||
normalization: org.gradle.normalization.internal.DefaultInputNormalizationHandler_Decorated@4460a043
|
||||
objects: org.gradle.api.internal.model.DefaultObjectFactory@381b881e
|
||||
outgoingVariants: task ':app:outgoingVariants'
|
||||
owner: project ':app'
|
||||
parent: root project 'my-gradle-project5165026'
|
||||
parentIdentifier: root project 'my-gradle-project5165026'
|
||||
path: :app
|
||||
pluginContext: false
|
||||
pluginManager: org.gradle.api.internal.plugins.DefaultPluginManager_Decorated@505350fd
|
||||
plugins: [org.gradle.api.plugins.ReportingBasePlugin$Inject@363d344c, org.gradle.api.plugins.ProjectReportsPlugin$Inject@4ecb0bf4, org.gradle.api.plugins.HelpTasksPlugin$Inject@55c1f1c, org.gradle.buildinit.plugins.BuildInitPlugin$Inject@5154243b, org.gradle.buildinit.plugins.WrapperPlugin$Inject@4186a206, org.gradle.language.base.plugins.LifecycleBasePlugin$Inject@63b97a85, org.gradle.api.plugins.BasePlugin$Inject@46c915ee, org.gradle.api.plugins.JvmEcosystemPlugin$Inject@26901290, org.gradle.api.plugins.JvmToolchainsPlugin$Inject@1047a16b, org.gradle.api.plugins.JavaBasePlugin$Inject@219fc174, org.gradle.api.plugins.GroovyBasePlugin$Inject@cc0c00e, org.gradle.testing.base.plugins.TestSuiteBasePlugin$Inject@afcdb52, org.gradle.api.plugins.JvmTestSuitePlugin$Inject@4f2802e1, org.gradle.api.plugins.JavaPlugin$Inject@457c77b1, org.gradle.api.plugins.GroovyPlugin$Inject@6bea0b65, BuildlogicGroovyCommonConventionsPlugin@340f0f12, org.gradle.api.distribution.plugins.DistributionPlugin$Inject@41c5c0a5, org.gradle.api.plugins.ApplicationPlugin$Inject@4e797fdb, BuildlogicGroovyApplicationConventionsPlugin@502595f2]
|
||||
processOperations: org.gradle.process.internal.DefaultExecActionFactory$DecoratingExecActionFactory@511a460a
|
||||
processResources: task ':app:processResources'
|
||||
processTestResources: task ':app:processTestResources'
|
||||
project: project ':app'
|
||||
projectConfigurator: org.gradle.api.internal.project.BuildOperationCrossProjectConfigurator@21331883
|
||||
projectDir: /private/tmp/nx-e2e/nx/proj9912426/app
|
||||
projectEvaluationBroadcaster: ProjectEvaluationListener broadcast
|
||||
projectEvaluator: org.gradle.configuration.project.LifecycleProjectEvaluator@f6c3829
|
||||
projectPath: :app
|
||||
projectReport: task ':app:projectReport'
|
||||
projectReportDir: /private/tmp/nx-e2e/nx/proj9912426/app/build/reports/project
|
||||
projectReportDirName: project
|
||||
projects: [project ':app']
|
||||
properties: {...}
|
||||
propertyReport: task ':app:propertyReport'
|
||||
providers: org.gradle.api.internal.provider.DefaultProviderFactory_Decorated@1eb7e486
|
||||
publicType: org.gradle.api.plugins.ProjectReportsPluginConvention
|
||||
reporting: extension 'reporting'
|
||||
repositories: repository container
|
||||
resolvableConfigurations: task ':app:resolvableConfigurations'
|
||||
resources: org.gradle.api.internal.resources.DefaultResourceHandler@103986d2
|
||||
rootDir: /private/tmp/nx-e2e/nx/proj9912426
|
||||
rootProject: root project 'my-gradle-project5165026'
|
||||
rootScript: false
|
||||
run: task ':app:run'
|
||||
script: false
|
||||
scriptHandlerFactory: org.gradle.api.internal.initialization.DefaultScriptHandlerFactory@191cd667
|
||||
scriptPluginFactory: org.gradle.configuration.ScriptPluginFactorySelector@775f081d
|
||||
serviceRegistryFactory: org.gradle.internal.service.scopes.BuildScopeServiceRegistryFactory@566d51a4
|
||||
services: Project services
|
||||
sourceCompatibility: 21
|
||||
sourceSets: SourceSet container
|
||||
standardOutputCapture: org.gradle.internal.logging.services.DefaultLoggingManager@2818071f
|
||||
startScripts: task ':app:startScripts'
|
||||
state: project state 'EXECUTED'
|
||||
status: integration
|
||||
subprojects: []
|
||||
targetCompatibility: 21
|
||||
taskDependencyFactory: org.gradle.api.internal.tasks.DefaultTaskDependencyFactory@7486b7b3
|
||||
taskReport: task ':app:taskReport'
|
||||
taskThatOwnsThisObject: null
|
||||
tasks: task set
|
||||
test: task ':app:test'
|
||||
testClasses: task ':app:testClasses'
|
||||
testReportDir: /private/tmp/nx-e2e/nx/proj9912426/app/build/reports/tests
|
||||
testReportDirName: tests
|
||||
testResultsDir: /private/tmp/nx-e2e/nx/proj9912426/app/build/test-results
|
||||
testResultsDirName: test-results
|
||||
testing: extension 'testing'
|
||||
version: unspecified
|
||||
versionCatalogs: extension 'versionCatalogs'
|
||||
87
packages/gradle/src/utils/exec-gradle.spec.ts
Normal file
87
packages/gradle/src/utils/exec-gradle.spec.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { TempFs } from 'nx/src/internal-testing-utils/temp-fs';
|
||||
import { findGraldewFile } from './exec-gradle';
|
||||
|
||||
describe('exec gradle', () => {
|
||||
describe('findGraldewFile', () => {
|
||||
let tempFs: TempFs;
|
||||
let cwd: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
tempFs = new TempFs('test');
|
||||
cwd = process.cwd();
|
||||
process.chdir(tempFs.tempDir);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetModules();
|
||||
process.chdir(cwd);
|
||||
});
|
||||
|
||||
it('should find gradlew with one gradlew file at root', async () => {
|
||||
await tempFs.createFiles({
|
||||
'proj/build.gradle': ``,
|
||||
gradlew: '',
|
||||
'nested/nested/proj/build.gradle': ``,
|
||||
'nested/nested/proj/settings.gradle': ``,
|
||||
'nested/nested/proj/src/test/java/test/rootTest.java': ``,
|
||||
'nested/nested/proj/src/test/java/test/aTest.java': ``,
|
||||
'nested/nested/proj/src/test/java/test/bTest.java': ``,
|
||||
});
|
||||
let gradlewFile = findGraldewFile('proj/build.gradle', tempFs.tempDir);
|
||||
expect(gradlewFile).toEqual('gradlew');
|
||||
gradlewFile = findGraldewFile(
|
||||
'nested/nested/proj/build.gradle',
|
||||
tempFs.tempDir
|
||||
);
|
||||
expect(gradlewFile).toEqual('gradlew');
|
||||
gradlewFile = findGraldewFile(
|
||||
'nested/nested/proj/settings.gradle',
|
||||
tempFs.tempDir
|
||||
);
|
||||
expect(gradlewFile).toEqual('gradlew');
|
||||
});
|
||||
|
||||
it('should find gradlew with multiple gradlew files with nested project structure', async () => {
|
||||
await tempFs.createFiles({
|
||||
'proj/build.gradle': ``,
|
||||
'proj/gradlew': '',
|
||||
'proj/settings.gradle': ``,
|
||||
'nested/nested/proj/gradlew': '',
|
||||
'nested/nested/proj/build.gradle': ``,
|
||||
'nested/nested/proj/settings.gradle': ``,
|
||||
'nested/nested/proj/src/test/java/test/rootTest.java': ``,
|
||||
'nested/nested/proj/src/test/java/test/aTest.java': ``,
|
||||
'nested/nested/proj/src/test/java/test/bTest.java': ``,
|
||||
});
|
||||
|
||||
let gradlewFile = findGraldewFile('proj/build.gradle', tempFs.tempDir);
|
||||
expect(gradlewFile).toEqual('proj/gradlew');
|
||||
gradlewFile = findGraldewFile('proj/settings.gradle', tempFs.tempDir);
|
||||
expect(gradlewFile).toEqual('proj/gradlew');
|
||||
gradlewFile = findGraldewFile(
|
||||
'nested/nested/proj/build.gradle',
|
||||
tempFs.tempDir
|
||||
);
|
||||
expect(gradlewFile).toEqual('nested/nested/proj/gradlew');
|
||||
gradlewFile = findGraldewFile(
|
||||
'nested/nested/proj/settings.gradle',
|
||||
tempFs.tempDir
|
||||
);
|
||||
expect(gradlewFile).toEqual('nested/nested/proj/gradlew');
|
||||
});
|
||||
|
||||
it('should throw an error if no gradlw in workspace', async () => {
|
||||
await tempFs.createFiles({
|
||||
'proj/build.gradle': ``,
|
||||
'nested/nested/proj/build.gradle': ``,
|
||||
'nested/nested/proj/settings.gradle': ``,
|
||||
'nested/nested/proj/src/test/java/test/rootTest.java': ``,
|
||||
'nested/nested/proj/src/test/java/test/aTest.java': ``,
|
||||
'nested/nested/proj/src/test/java/test/bTest.java': ``,
|
||||
});
|
||||
expect(() =>
|
||||
findGraldewFile('proj/build.gradle', tempFs.tempDir)
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,36 +1,35 @@
|
||||
import { workspaceRoot } from '@nx/devkit';
|
||||
import { AggregateCreateNodesError, workspaceRoot } from '@nx/devkit';
|
||||
import { ExecFileOptions, execFile } from 'node:child_process';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
||||
export function getGradleBinaryPath(): string {
|
||||
const gradleFile = process.platform.startsWith('win')
|
||||
? 'gradlew.bat'
|
||||
: 'gradlew';
|
||||
const gradleBinaryPath = join(workspaceRoot, gradleFile);
|
||||
if (!existsSync(gradleBinaryPath)) {
|
||||
throw new Error('Gradle is not setup. Run "gradle init"');
|
||||
}
|
||||
|
||||
return gradleBinaryPath;
|
||||
}
|
||||
import { dirname, join } from 'node:path';
|
||||
|
||||
/**
|
||||
* For gradle command, it needs to be run from the directory of the gradle binary
|
||||
* @returns gradle binary file name
|
||||
*/
|
||||
export function getGradleExecFile(): string {
|
||||
return process.platform.startsWith('win') ? '.\\gradlew.bat' : './gradlew';
|
||||
}
|
||||
|
||||
/**
|
||||
* This function executes gradle with the given arguments
|
||||
* @param gradleBinaryPath absolute path to gradle binary
|
||||
* @param args args passed to gradle
|
||||
* @param execOptions exec options
|
||||
* @returns promise with the stdout buffer
|
||||
*/
|
||||
export function execGradleAsync(
|
||||
gradleBinaryPath: string,
|
||||
args: ReadonlyArray<string>,
|
||||
execOptions: ExecFileOptions = {}
|
||||
): Promise<Buffer> {
|
||||
const gradleBinaryPath = getGradleBinaryPath();
|
||||
|
||||
return new Promise<Buffer>((res, rej) => {
|
||||
const cp = execFile(gradleBinaryPath, args, {
|
||||
...execOptions,
|
||||
cwd: dirname(gradleBinaryPath),
|
||||
shell: true,
|
||||
windowsHide: true,
|
||||
env: process.env,
|
||||
...execOptions,
|
||||
});
|
||||
|
||||
let stdout = Buffer.from('');
|
||||
@ -53,3 +52,46 @@ export function execGradleAsync(
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This function recursively finds the nearest gradlew file in the workspace
|
||||
* @param originalFileToSearch the original file to search for
|
||||
* @param wr workspace root
|
||||
* @param currentSearchPath the path to start searching for gradlew file
|
||||
* @returns the relative path of the gradlew file to workspace root, throws an error if gradlew file is not found
|
||||
* It will return gradlew.bat file on windows and gradlew file on other platforms
|
||||
*/
|
||||
export function findGraldewFile(
|
||||
originalFileToSearch: string,
|
||||
wr: string = workspaceRoot,
|
||||
currentSearchPath?: string
|
||||
): string {
|
||||
currentSearchPath ??= originalFileToSearch;
|
||||
const parent = dirname(currentSearchPath);
|
||||
if (currentSearchPath === parent) {
|
||||
throw new AggregateCreateNodesError(
|
||||
[
|
||||
[
|
||||
originalFileToSearch,
|
||||
new Error('No Gradlew file found. Run "gradle init"'),
|
||||
],
|
||||
],
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
const gradlewPath = join(parent, 'gradlew');
|
||||
const gradlewBatPath = join(parent, 'gradlew.bat');
|
||||
|
||||
if (process.platform.startsWith('win')) {
|
||||
if (existsSync(join(wr, gradlewBatPath))) {
|
||||
return gradlewBatPath;
|
||||
}
|
||||
} else {
|
||||
if (existsSync(join(wr, gradlewPath))) {
|
||||
return gradlewPath;
|
||||
}
|
||||
}
|
||||
|
||||
return findGraldewFile(originalFileToSearch, wr, parent);
|
||||
}
|
||||
|
||||
@ -36,4 +36,13 @@ describe('processProjectReports', () => {
|
||||
'mylibrary',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should process properties report for projects without child projects', () => {
|
||||
const report = processProjectReports([
|
||||
'> Task :propertyReport',
|
||||
`See the report at: file://${__dirname}/__mocks__/gradle-properties-report-no-child-projects.txt`,
|
||||
]);
|
||||
expect(report.gradleProjectToProjectName.get('')).toEqual('app');
|
||||
expect(report.gradleProjectToChildProjects.get('')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -3,23 +3,18 @@ import { join, relative } from 'node:path';
|
||||
|
||||
import {
|
||||
AggregateCreateNodesError,
|
||||
logger,
|
||||
normalizePath,
|
||||
workspaceRoot,
|
||||
} from '@nx/devkit';
|
||||
import { combineGlobPatterns } from 'nx/src/utils/globs';
|
||||
|
||||
import { execGradleAsync } from './exec-gradle';
|
||||
import { hashWithWorkspaceContext } from 'nx/src/utils/workspace-context';
|
||||
import { dirname } from 'path';
|
||||
|
||||
export const fileSeparator = process.platform.startsWith('win')
|
||||
? 'file:///'
|
||||
: 'file://';
|
||||
|
||||
export const newLineSeparator = process.platform.startsWith('win')
|
||||
? '\r\n'
|
||||
: '\n';
|
||||
import { gradleConfigAndTestGlob } from './split-config-files';
|
||||
import {
|
||||
getProjectReportLines,
|
||||
fileSeparator,
|
||||
newLineSeparator,
|
||||
} from './get-project-report-lines';
|
||||
|
||||
export interface GradleReport {
|
||||
gradleFileToGradleProjectMap: Map<string, string>;
|
||||
@ -34,37 +29,39 @@ export interface GradleReport {
|
||||
let gradleReportCache: GradleReport;
|
||||
let gradleCurrentConfigHash: string;
|
||||
|
||||
export const GRADLE_BUILD_FILES = new Set(['build.gradle', 'build.gradle.kts']);
|
||||
export const GRADLE_TEST_FILES = [
|
||||
'**/src/test/java/**/*Test.java',
|
||||
'**/src/test/kotlin/**/*Test.kt',
|
||||
'**/src/test/java/**/*Tests.java',
|
||||
'**/src/test/kotlin/**/*Tests.kt',
|
||||
];
|
||||
|
||||
export const gradleConfigGlob = combineGlobPatterns(
|
||||
...Array.from(GRADLE_BUILD_FILES).map((file) => `**/${file}`)
|
||||
);
|
||||
|
||||
export const gradleConfigAndTestGlob = combineGlobPatterns(
|
||||
...Array.from(GRADLE_BUILD_FILES).map((file) => `**/${file}`),
|
||||
...GRADLE_TEST_FILES
|
||||
);
|
||||
|
||||
export function getCurrentGradleReport() {
|
||||
if (!gradleReportCache) {
|
||||
throw new Error(
|
||||
'Expected cached gradle report. Please open an issue at https://github.com/nrwl/nx/issues/new/choose'
|
||||
throw new AggregateCreateNodesError(
|
||||
[
|
||||
[
|
||||
null,
|
||||
new Error(
|
||||
`Expected cached gradle report. Please open an issue at https://github.com/nrwl/nx/issues/new/choose`
|
||||
),
|
||||
],
|
||||
],
|
||||
[]
|
||||
);
|
||||
}
|
||||
return gradleReportCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function populates the gradle report cache.
|
||||
* For each gradlew file, it runs the `projectReportAll` task and processes the output.
|
||||
* If `projectReportAll` fails, it runs the `projectReport` task instead.
|
||||
* It will throw an error if both tasks fail.
|
||||
* It will accumulate the output of all gradlew files.
|
||||
* @param workspaceRoot
|
||||
* @param gradlewFiles absolute paths to all gradlew files in the workspace
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
export async function populateGradleReport(
|
||||
workspaceRoot: string
|
||||
workspaceRoot: string,
|
||||
gradlewFiles: string[]
|
||||
): Promise<void> {
|
||||
const gradleConfigHash = await hashWithWorkspaceContext(workspaceRoot, [
|
||||
gradleConfigGlob,
|
||||
gradleConfigAndTestGlob,
|
||||
]);
|
||||
if (gradleReportCache && gradleConfigHash === gradleCurrentConfigHash) {
|
||||
return;
|
||||
@ -73,37 +70,18 @@ export async function populateGradleReport(
|
||||
const gradleProjectReportStart = performance.mark(
|
||||
'gradleProjectReport:start'
|
||||
);
|
||||
let projectReportLines;
|
||||
try {
|
||||
projectReportLines = await execGradleAsync(['projectReportAll'], {
|
||||
cwd: workspaceRoot,
|
||||
});
|
||||
} catch (e) {
|
||||
try {
|
||||
projectReportLines = await execGradleAsync(['projectReport'], {
|
||||
cwd: workspaceRoot,
|
||||
});
|
||||
logger.warn(
|
||||
'Could not run `projectReportAll` task. Ran `projectReport` instead. Please run `nx generate @nx/gradle:init` to generate the necessary tasks.'
|
||||
);
|
||||
} catch (e) {
|
||||
throw new AggregateCreateNodesError(
|
||||
[
|
||||
[
|
||||
null,
|
||||
new Error(
|
||||
'Could not run `projectReportAll` or `projectReport` task. Please run `nx generate @nx/gradle:init` to generate the necessary tasks.'
|
||||
),
|
||||
],
|
||||
],
|
||||
[]
|
||||
);
|
||||
}
|
||||
}
|
||||
projectReportLines = projectReportLines
|
||||
.toString()
|
||||
.split(newLineSeparator)
|
||||
.filter((line) => line.trim() !== '');
|
||||
|
||||
const projectReportLines = await gradlewFiles.reduce(
|
||||
async (
|
||||
projectReportLines: Promise<string[]>,
|
||||
gradlewFile: string
|
||||
): Promise<string[]> => {
|
||||
const allLines = await projectReportLines;
|
||||
const currentLines = await getProjectReportLines(gradlewFile);
|
||||
return [...allLines, ...currentLines];
|
||||
},
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
const gradleProjectReportEnd = performance.mark('gradleProjectReport:end');
|
||||
performance.measure(
|
||||
@ -111,6 +89,7 @@ export async function populateGradleReport(
|
||||
gradleProjectReportStart.name,
|
||||
gradleProjectReportEnd.name
|
||||
);
|
||||
gradleCurrentConfigHash = gradleConfigHash;
|
||||
gradleReportCache = processProjectReports(projectReportLines);
|
||||
}
|
||||
|
||||
|
||||
65
packages/gradle/src/utils/get-project-report-lines.ts
Normal file
65
packages/gradle/src/utils/get-project-report-lines.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { AggregateCreateNodesError, logger } from '@nx/devkit';
|
||||
import { execGradleAsync } from './exec-gradle';
|
||||
import { existsSync } from 'fs';
|
||||
import { dirname, join } from 'path';
|
||||
|
||||
export const fileSeparator = process.platform.startsWith('win')
|
||||
? 'file:///'
|
||||
: 'file://';
|
||||
|
||||
export const newLineSeparator = process.platform.startsWith('win')
|
||||
? '\r\n'
|
||||
: '\n';
|
||||
|
||||
/**
|
||||
* This function executes the gradle projectReportAll task and returns the output as an array of lines.
|
||||
* @param gradlewFile the absolute path to the gradlew file
|
||||
* @returns project report lines
|
||||
*/
|
||||
export async function getProjectReportLines(
|
||||
gradlewFile: string
|
||||
): Promise<string[]> {
|
||||
let projectReportBuffer: Buffer;
|
||||
|
||||
// if there is no build.gradle or build.gradle.kts file, we cannot run the projectReport nor projectReportAll task
|
||||
if (
|
||||
!existsSync(join(dirname(gradlewFile), 'build.gradle')) &&
|
||||
!existsSync(join(dirname(gradlewFile), 'build.gradle.kts'))
|
||||
) {
|
||||
logger.warn(
|
||||
`Could not find build file near ${gradlewFile}. Please run 'nx generate @nx/gradle:init' to generate the necessary tasks.`
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
projectReportBuffer = await execGradleAsync(gradlewFile, [
|
||||
'projectReportAll',
|
||||
]);
|
||||
} catch (e) {
|
||||
try {
|
||||
projectReportBuffer = await execGradleAsync(gradlewFile, [
|
||||
'projectReport',
|
||||
]);
|
||||
logger.warn(
|
||||
`Could not run 'projectReportAll' task. Ran 'projectReport' instead. Please run 'nx generate @nx/gradle:init' to generate the necessary tasks.`
|
||||
);
|
||||
} catch (e) {
|
||||
throw new AggregateCreateNodesError(
|
||||
[
|
||||
[
|
||||
gradlewFile,
|
||||
new Error(
|
||||
`Could not run 'projectReportAll' or 'projectReport' task. Please run 'nx generate @nx/gradle:init' to generate the necessary tasks.`
|
||||
),
|
||||
],
|
||||
],
|
||||
[]
|
||||
);
|
||||
}
|
||||
}
|
||||
return projectReportBuffer
|
||||
.toString()
|
||||
.split(newLineSeparator)
|
||||
.filter((line) => line.trim() !== '');
|
||||
}
|
||||
88
packages/gradle/src/utils/split-config-files.spec.ts
Normal file
88
packages/gradle/src/utils/split-config-files.spec.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import { TempFs } from 'nx/src/internal-testing-utils/temp-fs';
|
||||
import { splitConfigFiles } from './split-config-files';
|
||||
|
||||
describe('split config files', () => {
|
||||
let tempFs: TempFs;
|
||||
let cwd: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
tempFs = new TempFs('test');
|
||||
cwd = process.cwd();
|
||||
process.chdir(tempFs.tempDir);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetModules();
|
||||
process.chdir(cwd);
|
||||
});
|
||||
|
||||
it('should split config files with one gradlew file', async () => {
|
||||
await tempFs.createFiles({
|
||||
'proj/build.gradle': ``,
|
||||
gradlew: '',
|
||||
'nested/nested/proj/build.gradle': ``,
|
||||
'nested/nested/proj/settings.gradle': ``,
|
||||
'nested/nested/proj/src/test/java/test/rootTest.java': ``,
|
||||
'nested/nested/proj/src/test/java/test/aTest.java': ``,
|
||||
'nested/nested/proj/src/test/java/test/bTest.java': ``,
|
||||
});
|
||||
const { buildFiles, gradlewFiles, testFiles, projectRoots } =
|
||||
splitConfigFiles([
|
||||
'proj/build.gradle',
|
||||
'gradlew',
|
||||
'nested/nested/proj/build.gradle',
|
||||
'nested/nested/proj/src/test/java/test/rootTest.java',
|
||||
'nested/nested/proj/src/test/java/test/aTest.java',
|
||||
'nested/nested/proj/src/test/java/test/bTest.java',
|
||||
]);
|
||||
expect(buildFiles).toEqual([
|
||||
'proj/build.gradle',
|
||||
'nested/nested/proj/build.gradle',
|
||||
]);
|
||||
expect(gradlewFiles).toEqual(['gradlew']);
|
||||
expect(testFiles).toEqual([
|
||||
'nested/nested/proj/src/test/java/test/rootTest.java',
|
||||
'nested/nested/proj/src/test/java/test/aTest.java',
|
||||
'nested/nested/proj/src/test/java/test/bTest.java',
|
||||
]);
|
||||
expect(projectRoots).toEqual(['proj', 'nested/nested/proj']);
|
||||
});
|
||||
|
||||
it('should split config files with multiple gradlew files', async () => {
|
||||
await tempFs.createFiles({
|
||||
'proj/build.gradle': ``,
|
||||
'proj/gradlew': '',
|
||||
'proj/settings.gradle': ``,
|
||||
'nested/nested/proj/gradlew': '',
|
||||
'nested/nested/proj/build.gradle': ``,
|
||||
'nested/nested/proj/settings.gradle': ``,
|
||||
'nested/nested/proj/src/test/java/test/rootTest.java': ``,
|
||||
'nested/nested/proj/src/test/java/test/aTest.java': ``,
|
||||
'nested/nested/proj/src/test/java/test/bTest.java': ``,
|
||||
});
|
||||
const { buildFiles, gradlewFiles, testFiles, projectRoots } =
|
||||
splitConfigFiles([
|
||||
'proj/build.gradle',
|
||||
'proj/gradlew',
|
||||
'nested/nested/proj/build.gradle',
|
||||
'nested/nested/proj/gradlew',
|
||||
'nested/nested/proj/src/test/java/test/rootTest.java',
|
||||
'nested/nested/proj/src/test/java/test/aTest.java',
|
||||
'nested/nested/proj/src/test/java/test/bTest.java',
|
||||
]);
|
||||
expect(buildFiles).toEqual([
|
||||
'proj/build.gradle',
|
||||
'nested/nested/proj/build.gradle',
|
||||
]);
|
||||
expect(gradlewFiles).toEqual([
|
||||
'proj/gradlew',
|
||||
'nested/nested/proj/gradlew',
|
||||
]);
|
||||
expect(testFiles).toEqual([
|
||||
'nested/nested/proj/src/test/java/test/rootTest.java',
|
||||
'nested/nested/proj/src/test/java/test/aTest.java',
|
||||
'nested/nested/proj/src/test/java/test/bTest.java',
|
||||
]);
|
||||
expect(projectRoots).toEqual(['proj', 'nested/nested/proj']);
|
||||
});
|
||||
});
|
||||
67
packages/gradle/src/utils/split-config-files.ts
Normal file
67
packages/gradle/src/utils/split-config-files.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { combineGlobPatterns } from 'nx/src/utils/globs';
|
||||
import { basename, dirname } from 'node:path';
|
||||
|
||||
export const GRADLE_BUILD_FILES = new Set(['build.gradle', 'build.gradle.kts']);
|
||||
export const GRALDEW_FILES = new Set(['gradlew', 'gradlew.bat']);
|
||||
export const GRADLE_TEST_FILES = [
|
||||
'**/src/test/java/**/*Test.java',
|
||||
'**/src/test/kotlin/**/*Test.kt',
|
||||
'**/src/test/java/**/*Tests.java',
|
||||
'**/src/test/kotlin/**/*Tests.kt',
|
||||
];
|
||||
|
||||
export const gradleConfigGlob = combineGlobPatterns(
|
||||
...Array.from(GRADLE_BUILD_FILES).map((file) => `**/${file}`)
|
||||
);
|
||||
|
||||
export const gradleConfigAndTestGlob = combineGlobPatterns(
|
||||
...Array.from(GRADLE_BUILD_FILES).map((file) => `**/${file}`),
|
||||
...Array.from(GRALDEW_FILES).map((file) => `**/${file}`),
|
||||
...GRADLE_TEST_FILES
|
||||
);
|
||||
|
||||
/**
|
||||
* This function split config files into build files, settings files, test files and project roots
|
||||
* @param files list of files to split
|
||||
* @returns object with buildFiles, gradlewFiles, testFiles and projectRoots
|
||||
* For gradlewFiles, it will start with settings files and find the nearest gradlew file in the workspace
|
||||
*/
|
||||
export function splitConfigFiles(files: readonly string[]): {
|
||||
buildFiles: string[];
|
||||
gradlewFiles: string[];
|
||||
testFiles: string[];
|
||||
projectRoots: string[];
|
||||
} {
|
||||
const buildFiles = [];
|
||||
const testFiles = [];
|
||||
const gradlewFiles = [];
|
||||
const projectRoots = new Set<string>();
|
||||
|
||||
files.forEach((file) => {
|
||||
const filename = basename(file);
|
||||
const fileDirectory = dirname(file);
|
||||
if (GRADLE_BUILD_FILES.has(filename)) {
|
||||
buildFiles.push(file);
|
||||
projectRoots.add(fileDirectory);
|
||||
} else if (GRALDEW_FILES.has(filename)) {
|
||||
if (process.platform.startsWith('win')) {
|
||||
if (filename === 'gradlew.bat') {
|
||||
gradlewFiles.push(file);
|
||||
}
|
||||
} else {
|
||||
if (filename === 'gradlew') {
|
||||
gradlewFiles.push(file);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
testFiles.push(file);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
buildFiles,
|
||||
testFiles,
|
||||
gradlewFiles,
|
||||
projectRoots: Array.from(projectRoots),
|
||||
};
|
||||
}
|
||||
@ -220,7 +220,14 @@ export async function detectPlugins(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (existsSync('gradlew') || existsSync('gradlew.bat')) {
|
||||
|
||||
let gradlewFiles = ['gradlew', 'gradlew.bat'].concat(
|
||||
await globWithWorkspaceContext(process.cwd(), [
|
||||
'**/gradlew',
|
||||
'**/gradlew.bat',
|
||||
])
|
||||
);
|
||||
if (gradlewFiles.some((f) => existsSync(f))) {
|
||||
detectedPlugins.add('@nx/gradle');
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user