22
33import com .fasterxml .jackson .annotation .JsonInclude ;
44import com .fasterxml .jackson .core .type .TypeReference ;
5- import com .fasterxml .jackson .core .util .DefaultIndenter ;
6- import com .fasterxml .jackson .core .util .DefaultPrettyPrinter ;
75import com .fasterxml .jackson .databind .ObjectMapper ;
86import com .fasterxml .jackson .databind .SerializationFeature ;
97import org .graalvm .internal .tck .exceptions .ContributingException ;
1210import org .graalvm .internal .tck .utils .ConfigurationStringBuilder ;
1311import org .graalvm .internal .tck .utils .FilesUtils ;
1412import org .graalvm .internal .tck .utils .InteractiveTaskUtils ;
13+ import org .graalvm .internal .tck .utils .MetadataGenerationUtils ;
1514import org .gradle .api .DefaultTask ;
1615import org .gradle .api .file .FileSystemOperations ;
1716import org .gradle .api .file .ProjectLayout ;
1817import org .gradle .api .tasks .TaskAction ;
1918import org .gradle .process .ExecOperations ;
2019
2120import javax .inject .Inject ;
22- import java .io .ByteArrayOutputStream ;
2321import java .io .File ;
2422import java .io .IOException ;
25- import java .io .InputStream ;
26- import java .nio .charset .StandardCharsets ;
2723import java .nio .file .Files ;
2824import java .nio .file .Path ;
2925import java .nio .file .StandardOpenOption ;
3026import java .util .ArrayList ;
31- import java .util .Arrays ;
3227import java .util .Comparator ;
3328import java .util .HashMap ;
3429import java .util .HashSet ;
4035public abstract class ContributionTask extends DefaultTask {
4136 private static final String BRANCH_NAME_PREFIX = "add-support-for-" ;
4237 private static final String METADATA_INDEX = "metadata/index.json" ;
43- private static final String BUILD_FILE = "build.gradle" ;
44- private static final String USER_CODE_FILTER_FILE = "user-code-filter.json" ;
38+ private static final String GRADLEW = "gradlew" ;
4539 private static final String REQUIRED_DOCKER_IMAGES_FILE = "required-docker-images.txt" ;
4640
41+
4742 @ Inject
4843 protected abstract ProjectLayout getLayout ();
4944
@@ -55,32 +50,24 @@ public abstract class ContributionTask extends DefaultTask {
5550
5651 private final ObjectMapper objectMapper = new ObjectMapper ().enable (SerializationFeature .INDENT_OUTPUT ).setSerializationInclusion (JsonInclude .Include .NON_NULL );
5752
58- private Path gradlew ;
53+ private Path gradlewPath ;
5954 private Path testsDirectory ;
60- private Path metadataDirectory ;
61-
62- private Coordinates coordinates ;
6355
64- private record ContributingQuestion (String question , String help ) {}
65- private final Map <String , ContributingQuestion > questions = new HashMap <>();
56+ String coordinates ;
6657
67- private void initializeWorkingDirectories (){
68- testsDirectory = getPathFromProject (CoordinateUtils .replace ("tests/src/$group$/$artifact$/$version$" , coordinates ));
69- metadataDirectory = getPathFromProject (CoordinateUtils .replace ("metadata/$group$/$artifact$/$version$" , coordinates ));
70- gradlew = getPathFromProject ("gradlew" );
58+ private record ContributingQuestion (String question , String help ) {
7159 }
7260
73- private Path getPathFromProject (String fileName ) {
74- return Path .of (getProjectFile (fileName ).getAbsolutePath ());
75- }
61+ private final Map <String , ContributingQuestion > questions = new HashMap <>();
7662
7763 private File getProjectFile (String fileName ) {
7864 return getLayout ().getProjectDirectory ().file (fileName ).getAsFile ();
7965 }
8066
8167 private void loadQuestions () throws IOException {
8268 File questionsJson = getProjectFile ("tests/tck-build-logic/src/main/resources/contributing/questions.json" );
83- List <Question > contributingQuestions = objectMapper .readValue (questionsJson , new TypeReference <>() {});
69+ List <Question > contributingQuestions = objectMapper .readValue (questionsJson , new TypeReference <>() {
70+ });
8471 for (var question : contributingQuestions ) {
8572 questions .put (question .questionKey (), new ContributingQuestion (question .question (), question .help ()));
8673 }
@@ -96,7 +83,7 @@ void run() throws IOException {
9683 coordinates = getCoordinates ();
9784 InteractiveTaskUtils .closeSection ();
9885
99- Path coordinatesMetadataRoot = getPathFromProject (CoordinateUtils .replace ("metadata/$group$/$artifact$" , coordinates ));
86+ Path coordinatesMetadataRoot = MetadataGenerationUtils . getPathFromProject (getLayout (), CoordinateUtils .replace ("metadata/$group$/$artifact$" , CoordinateUtils . fromString ( coordinates ) ));
10087 boolean isExistingLibrary = Files .exists (coordinatesMetadataRoot );
10188
10289 Path testsLocation = getTestsLocation ();
@@ -111,29 +98,31 @@ void run() throws IOException {
11198 List <String > packages = getAllowedPackages ();
11299 InteractiveTaskUtils .closeSection ();
113100
114- List <Coordinates > additionalTestImplementationDependencies = getAdditionalDependencies ();
101+ List <String > additionalTestImplementationDependencies = getAdditionalDependencies ();
115102 InteractiveTaskUtils .closeSection ();
116103
117104 // initialize project
118- initializeWorkingDirectories ();
105+ gradlewPath = MetadataGenerationUtils .getPathFromProject (getLayout (), GRADLEW );
106+ testsDirectory = MetadataGenerationUtils .computeTestsDirectory (getLayout (), coordinates );
107+
119108 createStubs (isExistingLibrary );
120109 updateAllowedPackages (packages , isExistingLibrary );
121110
122111 // generate necessary boilerplate code
123112 addTests (testsLocation );
124113 addResources (resourcesLocation );
125114 addDockerImages (dockerImages );
126- addUserCodeFilterFile (packages );
115+ MetadataGenerationUtils . addUserCodeFilterFile (getLayout (), testsDirectory , packages );
127116 addAdditionalDependencies (additionalTestImplementationDependencies );
128- addAgentConfigBlock ();
117+ MetadataGenerationUtils . addAgentConfigBlock (testsDirectory );
129118
130119 // run agent in conditional mode
131- collectMetadata ();
120+ MetadataGenerationUtils . collectMetadata (getExecOperations (), testsDirectory , getLayout (), coordinates , gradlewPath );
132121
133122 // create a PR
134123 boolean shouldCreatePR = shouldCreatePullRequest ();
135124 if (shouldCreatePR ) {
136- String branch = BRANCH_NAME_PREFIX + coordinates .toString (). replace (':' , '-' );
125+ String branch = BRANCH_NAME_PREFIX + coordinates .replace (':' , '-' );
137126 createPullRequest (branch );
138127
139128 InteractiveTaskUtils .printUserInfo ("After your pull requests gets generated, please update the pull request description to mention all places where your pull request" +
@@ -143,15 +132,9 @@ void run() throws IOException {
143132 InteractiveTaskUtils .printSuccessfulStatement ("Contribution successfully completed! Thank you!" );
144133 }
145134
146- private Coordinates getCoordinates () {
135+ private String getCoordinates () {
147136 ContributingQuestion question = questions .get ("coordinates" );
148- return InteractiveTaskUtils .askQuestion (question .question (), question .help (), (answer ) -> {
149- try {
150- return CoordinateUtils .fromString (answer );
151- } catch (IllegalArgumentException ex ) {
152- throw new ContributingException (ex .getMessage ());
153- }
154- });
137+ return InteractiveTaskUtils .askQuestion (question .question (), question .help (), (answer ) -> answer );
155138 }
156139
157140 private Path getTestsLocation () {
@@ -200,7 +183,7 @@ private void checkPackages(Path testsPath) throws ContributingException {
200183 }
201184 }
202185
203- private Path getResourcesLocation (){
186+ private Path getResourcesLocation () {
204187 ContributingQuestion question = questions .get ("resourcesLocation" );
205188 return InteractiveTaskUtils .askQuestion (question .question (), question .help (), (answer ) -> {
206189 if (answer .equalsIgnoreCase ("-" )) {
@@ -241,21 +224,15 @@ private List<String> getAllowedPackages() {
241224 return InteractiveTaskUtils .askRecurringQuestions (question .question (), question .help (), 1 , answer -> answer );
242225 }
243226
244- private List <Coordinates > getAdditionalDependencies () {
227+ private List <String > getAdditionalDependencies () {
245228 ContributingQuestion question = questions .get ("additionalDependencies" );
246- return InteractiveTaskUtils .askRecurringQuestions (question .question (), question .help (), 0 , answer -> {
247- try {
248- return CoordinateUtils .fromString (answer );
249- } catch (IllegalArgumentException ex ) {
250- throw new ContributingException (ex .getMessage ());
251- }
252- });
229+ return InteractiveTaskUtils .askRecurringQuestions (question .question (), question .help (), 0 , answer -> answer );
253230 }
254231
255232 private void createStubs (boolean shouldUpdate ) {
256- InteractiveTaskUtils .printUserInfo ("Generating stubs for: " + coordinates );
233+ InteractiveTaskUtils .printUserInfo ("Generating stubs for: " + coordinates );
257234 String opt = shouldUpdate ? "--update" : "" ;
258- invokeCommand (gradlew + " scaffold --coordinates " + coordinates + " --skipTests " + opt , "Cannot generate stubs for: " + coordinates );
235+ MetadataGenerationUtils . invokeCommand (getExecOperations (), gradlewPath + " scaffold --coordinates " + coordinates + " --skipTests " + opt , "Cannot generate stubs for: " + coordinates );
259236 }
260237
261238 private void updateAllowedPackages (List <String > allowedPackages , boolean libraryAlreadyExists ) throws IOException {
@@ -264,8 +241,9 @@ private void updateAllowedPackages(List<String> allowedPackages, boolean library
264241
265242 List <MetadataIndexEntry > entries = objectMapper .readValue (metadataIndex , new TypeReference <>() {});
266243 int replaceEntryIndex = -1 ;
244+ Coordinates dependencyCoordinates = CoordinateUtils .fromString (coordinates );
267245 for (int i = 0 ; i < entries .size (); i ++) {
268- if (entries .get (i ).module ().equals (coordinates .group () + ":" + coordinates .artifact ())) {
246+ if (entries .get (i ).module ().equals (dependencyCoordinates .group () + ":" + dependencyCoordinates .artifact ())) {
269247 replaceEntryIndex = i ;
270248 break ;
271249 }
@@ -289,7 +267,7 @@ private void updateAllowedPackages(List<String> allowedPackages, boolean library
289267 objectMapper .writeValue (metadataIndex , entries );
290268 }
291269
292- private void addTests (Path originalTestsLocation ){
270+ private void addTests (Path originalTestsLocation ) {
293271 Path destination = testsDirectory .resolve ("src" ).resolve ("test" ).resolve ("java" );
294272 Path allTests = originalTestsLocation .resolve ("." );
295273
@@ -301,7 +279,7 @@ private void addTests(Path originalTestsLocation){
301279 });
302280 }
303281
304- private void addResources (Path originalResourcesDirectory ){
282+ private void addResources (Path originalResourcesDirectory ) {
305283 if (originalResourcesDirectory == null ) {
306284 return ;
307285 }
@@ -311,8 +289,8 @@ private void addResources(Path originalResourcesDirectory){
311289
312290 InteractiveTaskUtils .printUserInfo ("Copying resources from: " + originalResourcesDirectory + " to " + destination );
313291 getFileSystemOperations ().copy (copySpec -> {
314- copySpec .from (originalResourcesDirectory );
315- copySpec .into (destination );
292+ copySpec .from (originalResourcesDirectory );
293+ copySpec .into (destination );
316294 });
317295 }
318296
@@ -326,39 +304,24 @@ private void addDockerImages(List<String> images) throws IOException {
326304 ensureFileBelongsToProject (destination );
327305
328306 if (!Files .exists (destination )) {
329- Files .createFile (destination );
307+ Files .createDirectories (destination . getParent () );
330308 }
331309
332310 for (String image : images ) {
333- writeToFile (destination , image .concat (System .lineSeparator ()), StandardOpenOption .APPEND );
311+ MetadataGenerationUtils . writeToFile (destination , image .concat (System .lineSeparator ()), StandardOpenOption .APPEND );
334312 }
335313 }
336314
337- private void addUserCodeFilterFile (List <String > packages ) throws IOException {
338- InteractiveTaskUtils .printUserInfo ("Generating " + USER_CODE_FILTER_FILE );
339- List <Map <String , String >> filterFileRules = new ArrayList <>();
340-
341- // add exclude classes
342- filterFileRules .add (Map .of ("excludeClasses" , "**" ));
343-
344- // add include classes
345- packages .forEach (p -> filterFileRules .add (Map .of ("includeClasses" , p + ".**" )));
346-
347- DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter ();
348- prettyPrinter .indentArraysWith (DefaultIndenter .SYSTEM_LINEFEED_INSTANCE );
349- objectMapper .writer (prettyPrinter ).writeValue (testsDirectory .resolve (USER_CODE_FILTER_FILE ).toFile (), Map .of ("rules" , filterFileRules ));
350- }
351-
352- private void addAdditionalDependencies (List <Coordinates > dependencies ) throws IOException {
315+ private void addAdditionalDependencies (List <String > dependencies ) throws IOException {
353316 if (dependencies .isEmpty ()) {
354317 return ;
355318 }
356319
357- Path buildFilePath = testsDirectory .resolve (BUILD_FILE );
358- InteractiveTaskUtils .printUserInfo ("Adding following dependencies to " + BUILD_FILE + " file: " + dependencies );
320+ Path buildFilePath = testsDirectory .resolve (MetadataGenerationUtils . BUILD_FILE );
321+ InteractiveTaskUtils .printUserInfo ("Adding following dependencies to " + MetadataGenerationUtils . BUILD_FILE + " file: " + dependencies );
359322
360323 if (!Files .isRegularFile (buildFilePath )) {
361- throw new RuntimeException ("Cannot add additional dependencies to " + buildFilePath + ". Please check if a " + BUILD_FILE + " exists on that location." );
324+ throw new RuntimeException ("Cannot add additional dependencies to " + buildFilePath + ". Please check if a " + MetadataGenerationUtils . BUILD_FILE + " exists on that location." );
362325 }
363326
364327 ConfigurationStringBuilder sb = new ConfigurationStringBuilder ();
@@ -368,39 +331,13 @@ private void addAdditionalDependencies(List<Coordinates> dependencies) throws IO
368331 if (line .trim ().equalsIgnoreCase ("dependencies {" )) {
369332 sb .indent ();
370333 for (var dependency : dependencies ) {
371- sb .append ("testImplementation" ).space ().quote (dependency . toString () ).newLine ();
334+ sb .append ("testImplementation" ).space ().quote (dependency ).newLine ();
372335 }
373336 sb .unindent ();
374337 }
375338 }
376339
377- writeToFile (buildFilePath , sb .toString (), StandardOpenOption .WRITE );
378- }
379-
380- private void addAgentConfigBlock () throws IOException {
381- Path buildFilePath = testsDirectory .resolve (BUILD_FILE );
382- InteractiveTaskUtils .printUserInfo ("Configuring agent block in: " + BUILD_FILE );
383-
384- if (!Files .isRegularFile (buildFilePath )) {
385- throw new RuntimeException ("Cannot add agent block to " + buildFilePath + ". Please check if a " + BUILD_FILE + " exists on that location." );
386- }
387-
388- try (InputStream stream = ContributionTask .class .getResourceAsStream ("/contributing/agent.template" )) {
389- if (stream == null ) {
390- throw new RuntimeException ("Cannot find template for the graalvm configuration block" );
391- }
392-
393- String content = System .lineSeparator () + (new String (stream .readAllBytes (), StandardCharsets .UTF_8 ));
394- writeToFile (buildFilePath , content , StandardOpenOption .APPEND );
395- }
396- }
397-
398- private void collectMetadata () {
399- InteractiveTaskUtils .printUserInfo ("Generating metadata" );
400- invokeCommand (gradlew + " -Pagent test" , "Cannot generate metadata" , testsDirectory );
401-
402- InteractiveTaskUtils .printUserInfo ("Performing metadata copy" );
403- invokeCommand (gradlew + " metadataCopy --task test --dir " + metadataDirectory , "Cannot perform metadata copy" , testsDirectory );
340+ MetadataGenerationUtils .writeToFile (buildFilePath , sb .toString (), StandardOpenOption .WRITE );
404341 }
405342
406343 private boolean shouldCreatePullRequest () {
@@ -410,53 +347,20 @@ private boolean shouldCreatePullRequest() {
410347
411348 private void createPullRequest (String branch ) {
412349 InteractiveTaskUtils .printUserInfo ("Creating new branch: " + branch );
413- invokeCommand ("git switch -C " + branch , "Cannot create a new branch" );
350+ MetadataGenerationUtils . invokeCommand (getExecOperations (), "git switch -C " + branch , "Cannot create a new branch" );
414351
415352 InteractiveTaskUtils .printUserInfo ("Staging changes" );
416- invokeCommand ("git add ." , "Cannot add changes" );
353+ MetadataGenerationUtils . invokeCommand (getExecOperations (), "git add ." , "Cannot add changes" );
417354
418355 InteractiveTaskUtils .printUserInfo ("Committing changes" );
419- invokeCommand ("git" , List .of ("commit" , "-m" , "Add metadata for " + coordinates ), "Cannot commit changes" , null );
356+ MetadataGenerationUtils . invokeCommand (getExecOperations (), "git" , List .of ("commit" , "-m" , "Add metadata for " + coordinates ), "Cannot commit changes" , null );
420357
421358 InteractiveTaskUtils .printUserInfo ("Pushing changes" );
422- invokeCommand ("git push origin " + branch , "Cannot push to origin" );
359+ MetadataGenerationUtils . invokeCommand (getExecOperations (), "git push origin " + branch , "Cannot push to origin" );
423360
424361 InteractiveTaskUtils .printUserInfo ("Complete pull request creation on the above link" );
425362 }
426363
427- private void writeToFile (Path path , String content , StandardOpenOption writeOption ) throws IOException {
428- Files .createDirectories (path .getParent ());
429- Files .writeString (path , content , StandardCharsets .UTF_8 , writeOption );
430- }
431-
432- private void invokeCommand (String command , String errorMessage ) {
433- invokeCommand (command , errorMessage , null );
434- }
435-
436- private void invokeCommand (String command , String errorMessage , Path workingDirectory ) {
437- String [] commandParts = command .split (" " );
438- String executable = commandParts [0 ];
439-
440- List <String > args = List .of (Arrays .copyOfRange (commandParts , 1 , commandParts .length ));
441- invokeCommand (executable , args , errorMessage , workingDirectory );
442- }
443-
444- private void invokeCommand (String executable , List <String > args , String errorMessage , Path workingDirectory ) {
445- ByteArrayOutputStream execOutput = new ByteArrayOutputStream ();
446- var result = getExecOperations ().exec (execSpec -> {
447- if (workingDirectory != null ) {
448- execSpec .setWorkingDir (workingDirectory );
449- }
450- execSpec .setExecutable (executable );
451- execSpec .setArgs (args );
452- execSpec .setStandardOutput (execOutput );
453- });
454-
455- if (result .getExitValue () != 0 ) {
456- throw new RuntimeException (errorMessage + ". See: " + execOutput );
457- }
458- }
459-
460364 private void ensureFileBelongsToProject (Path file ) {
461365 if (!file .isAbsolute () || !file .startsWith (getLayout ().getProjectDirectory ().getAsFile ().getAbsolutePath ())) {
462366 throw new RuntimeException ("The following file doesn't belong to the metadata repository: " + file );
0 commit comments