Skip to content

Allow package info classes to be resolved as a dependency#1565

Open
wakingrufus wants to merge 1 commit intoTNG:mainfrom
wakingrufus:dependency-package-info
Open

Allow package info classes to be resolved as a dependency#1565
wakingrufus wants to merge 1 commit intoTNG:mainfrom
wakingrufus:dependency-package-info

Conversation

@wakingrufus
Copy link
Copy Markdown

@wakingrufus wakingrufus commented Dec 10, 2025

Allow package info classes to be resolved as a dependency
exclude package-info classes from package metrics calculations

Resolves #1564

@wakingrufus wakingrufus force-pushed the dependency-package-info branch 7 times, most recently from 6da5209 to 43288eb Compare December 11, 2025 23:23
@wakingrufus wakingrufus marked this pull request as ready for review December 11, 2025 23:23
@wakingrufus wakingrufus changed the title failing tests to reproduce #1564 Allow package info classes to be resolved as a dependency Dec 11, 2025
@wakingrufus wakingrufus force-pushed the dependency-package-info branch from 43288eb to 070d20f Compare December 12, 2025 15:41
@wakingrufus
Copy link
Copy Markdown
Author

I updated this PR to exclude the package-info classes when calculating package metrics. this fixes the failing tests

@wakingrufus wakingrufus force-pushed the dependency-package-info branch from 070d20f to 81cfe2d Compare December 12, 2025 15:57
@wakingrufus
Copy link
Copy Markdown
Author

Happy New Year @codecholeric and @hankem !
Just a gentle reminder that I would appreciate if this PR could be looked at.
Thanks again!

@wakingrufus
Copy link
Copy Markdown
Author

Hi again @codecholeric and @hankem. I know I have a few PRs open at the moment, but this one is quite important. I would really appreciate of someone could take a look at this please.

wakingrufus added a commit to nebula-plugins/nebula-archrules-plugin that referenced this pull request Feb 19, 2026
… class dependency when scanning

since TNG/ArchUnit#1565 has not been merged in a timely manner, we need a custom implementation on our end to fix this issue until it is finally merged.
@wakingrufus wakingrufus force-pushed the dependency-package-info branch from ab61e25 to 63c579b Compare February 19, 2026 17:33
wakingrufus added a commit to nebula-plugins/nebula-archrules-plugin that referenced this pull request Feb 19, 2026
… class dependency when scanning

since TNG/ArchUnit#1565 has not been merged in a timely manner, we need a custom implementation on our end to fix this issue until it is finally merged.
wakingrufus added a commit to nebula-plugins/nebula-archrules-plugin that referenced this pull request Feb 19, 2026
… class dependency when scanning

since TNG/ArchUnit#1565 has not been merged in a timely manner, we need a custom implementation on our end to fix this issue until it is finally merged.
wakingrufus added a commit to nebula-plugins/nebula-archrules-plugin that referenced this pull request Feb 19, 2026
… class dependency when scanning

since TNG/ArchUnit#1565 has not been merged in a timely manner, we need a custom implementation on our end to fix this issue until it is finally merged.
@wakingrufus wakingrufus force-pushed the dependency-package-info branch from 63c579b to 3b8fda6 Compare April 21, 2026 14:41
@hankem
Copy link
Copy Markdown
Member

hankem commented Apr 22, 2026

I'm very sorry for the late response! I'll try to find more time. 🤞

@wakingrufus
Copy link
Copy Markdown
Author

I'm very sorry for the late response! I'll try to find more time. 🤞

No problem, Thanks!

Copy link
Copy Markdown

@StefanGraeber StefanGraeber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dislike that the package-info is now included in JavaPackage.getClasses() (even as stub if it doesn't exist, which might be a breaking change for some use cases) and don't understand whether or how all classes in the package are retained when recreating the package in the JavaClass class.
IMO it also is the wrong location for the logic of resolving package info.

Can you explain in more detail which problem you want to solve?
To my understanding, the issue seems to be, that the package-info is not always resolved when you expect it to be loaded, but I'm not sure whether that's limited to loading single classes via new ClassFileImporter() or whether this also affects the "normal" execution with @AnalyzeClasses annotation

}

@Test
public void function_getPackageInfo() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking at the previous tests in this class, I would expect that a test with this name would test
JavaClass.Functions.GET_PACKAGE_INFO, which doesn't exist.
This test doesn't fit the pattern of the other tests.
I'm not sure whether we would want to have that function, as I'm not sure what I should return apart from the package itself

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks. this is fixed now.

@@ -0,0 +1,7 @@
package com.tngtech.archunit.core.importer.testexamples.packageinforesolution;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this new package name.
is it important that it is subpackage of the package in which OtherClass is defined or shall just be any other package?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is important that this class is in a different package than OtherClass. other than that, this package could be anything

}

void completePackageInfoFrom(ImportContext context) {
this.javaPackage = JavaPackage.from(Arrays.asList(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with the class resolution yet, but this looks to me as if the package is re-created with just the current class and the package-info class.
If you look e.g. at the package testexamples, you have these classes (and more):

  • SomeClass
  • OtherClass
  • package-info

assuming we are now in the JavaClass representing SomeClass, I expect that getPackage().getClasses() contains all three of these classes, but I don't see how it could return anything else then Set.of(SomeClass, package-info), as this statement replaces the package and just passes these 2 classes into the new factory.
I don't see any call in JavaPackage.from checking the package of the class, which could contain the information.

It is also not clear to me how the dependencies and subpackages are build if all the other classes in teh package are missing.

Wouldn't it be much cleaner if we could resolve the package-info class when initially building the JavaPackage?
Why should each JavaClass in that package be responsible for resolving the package-info?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because initially building JavaPackage is only something that happens if the package-info class is in the initial list of files passed to ClassfileImporter. when class dependencies are resolved, if the other class lives in another (non-imported) package, only the package name is known. this method that is added allows us to resolve the rest of the package info for this package, even if the entire package was not included in the import.

JavaClass otherClass = new ClassFileImporter()
.importClass(OtherClass.class);

JavaPackage checkedClassPackageInfo = otherClass.getPackage();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JavaPackage has the methods tryGetPackageInfo and getPackageInfo.
please include in the test they work as well.
as one calls the other, I would prefer to tests sth like assertThat(checkedClassPackageInfo.getPackageInfo()).isPresent(), even thought this is kind of redundant with the getAnnotations method which also relies on that field.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like these exist in JavaPackageTest:

    @Test
    public void test_getPackageInfo() {
        JavaPackage annotatedPackage = importPackage("packageexamples.annotated");
        JavaPackage nonAnnotatedPackage = importPackage("packageexamples");

        assertThat(annotatedPackage.getPackageInfo()).isNotNull();

        assertThatThrownBy(nonAnnotatedPackage::getPackageInfo)
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining(nonAnnotatedPackage.getDescription() + " does not contain a package-info.java");
    }

    @Test
    public void test_tryGetPackageInfo() {
        JavaPackage annotatedPackage = importPackage("packageexamples.annotated");
        JavaPackage nonAnnotatedPackage = importPackage("packageexamples");

        assertThat(annotatedPackage.tryGetPackageInfo()).isPresent();
        assertThat(nonAnnotatedPackage.tryGetPackageInfo()).isEmpty();
    }

JavaPackage checkedClassPackageInfo = otherClass.getPackage();
assertThat(checkedClassPackageInfo.getAnnotations()).hasSize(1);
assertThat(checkedClassPackageInfo.isAnnotatedWith(SomeAnnotation.class)).isTrue();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When running checkedClassPackageInfo.getClasses(), this set now includes the package-info, which it didn't before.
I don't think that that class should be included in that Set as it is not a real class and shall instead be accessed via JavaPackage.getPackageInfo() which returns the JavaClass representing package-info as HasAnnotations. This change in type seems deliberate to me as this special class doesn't have all features of a JavaClass.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already the normal behavior in scanning a package. Ill add this assertion to JavaPackageTest.test_getPackageInfo() to show this:

        assertThat(annotatedPackage.getClasses().stream().map(JavaClass::getFullName).collect(Collectors.toList()))
                .as("all classes and package-info are loaded as JavaClass")
                .containsExactlyInAnyOrder(
                        "com.tngtech.archunit.core.domain.packageexamples.annotated.package-info",
                        "com.tngtech.archunit.core.domain.packageexamples.annotated.PackageLevelAnnotation",
                        "com.tngtech.archunit.core.domain.packageexamples.annotated.WithinAnnotatedPackage"
                );

@StefanGraeber
Copy link
Copy Markdown

I can't really localize the issue (and it might be a me-problem), but using windows 11 and IntelliJ, incremental builds fail with this error

> Task :archunit:compileJdk9mainJava FAILED
repopath\ArchUnit\archunit\src\jdk9main\java\com\tngtech\archunit\core\domain\Java9DomainPlugin.java:30: error: cannot access AnnotationPropertiesFormatter
                        .formatProperties(config -> config
                        ^
  class file for com.tngtech.archunit.core.domain.AnnotationFormatter$AnnotationPropertiesFormatter not found
1 error

as soon as I include your changes in JavaClass.
I can't reproduce the issue with adding some random new method in that class

@wakingrufus
Copy link
Copy Markdown
Author

I dislike that the package-info is now included in JavaPackage.getClasses() (even as stub if it doesn't exist, which might be a breaking change for some use cases) and don't understand whether or how all classes in the package are retained when recreating the package in the JavaClass class. IMO it also is the wrong location for the logic of resolving package info.

Can you explain in more detail which problem you want to solve? To my understanding, the issue seems to be, that the package-infois not always resolved when you expect it to be loaded, but I'm not sure whether that's limited to loading single classes vianew ClassFileImporter()or whether this also affects the "normal" execution with@AnalyzeClasses` annotation

Thanks for taking a look at this. I will add a more detailed description of the use case in the PR description.

@wakingrufus
Copy link
Copy Markdown
Author

I dislike that the package-info is now included in JavaPackage.getClasses() (even as stub if it doesn't exist, which might be a breaking change for some use cases) and don't understand whether or how all classes in the package are retained when recreating the package in the JavaClass class. IMO it also is the wrong location for the logic of resolving package info.
Can you explain in more detail which problem you want to solve? To my understanding, the issue seems to be, that the package-infois not always resolved when you expect it to be loaded, but I'm not sure whether that's limited to loading single classes vianew ClassFileImporter()or whether this also affects the "normal" execution with@AnalyzeClasses` annotation

Thanks for taking a look at this. I will add a more detailed description of the use case in the PR description.

I dislike that the package-info is now included in JavaPackage.getClasses() (even as stub if it doesn't exist, which might be a breaking change for some use cases) and don't understand whether or how all classes in the package are retained when recreating the package in the JavaClass class. IMO it also is the wrong location for the logic of resolving package info.
Can you explain in more detail which problem you want to solve? To my understanding, the issue seems to be, that the package-infois not always resolved when you expect it to be loaded, but I'm not sure whether that's limited to loading single classes vianew ClassFileImporter()or whether this also affects the "normal" execution with@AnalyzeClasses` annotation

Thanks for taking a look at this. I will add a more detailed description of the use case in the PR description.

@StefanGraeber Actually, I updated the associated issue, instead: #1564

@wakingrufus wakingrufus force-pushed the dependency-package-info branch from 3b8fda6 to 7cd61cd Compare April 27, 2026 15:59
@wakingrufus
Copy link
Copy Markdown
Author

I can't really localize the issue (and it might be a me-problem), but using windows 11 and IntelliJ, incremental builds fail with this error

> Task :archunit:compileJdk9mainJava FAILED
repopath\ArchUnit\archunit\src\jdk9main\java\com\tngtech\archunit\core\domain\Java9DomainPlugin.java:30: error: cannot access AnnotationPropertiesFormatter
                        .formatProperties(config -> config
                        ^
  class file for com.tngtech.archunit.core.domain.AnnotationFormatter$AnnotationPropertiesFormatter not found
1 error

as soon as I include your changes in JavaClass. I can't reproduce the issue with adding some random new method in that class

I can't really localize the issue (and it might be a me-problem), but using windows 11 and IntelliJ, incremental builds fail with this error

> Task :archunit:compileJdk9mainJava FAILED
repopath\ArchUnit\archunit\src\jdk9main\java\com\tngtech\archunit\core\domain\Java9DomainPlugin.java:30: error: cannot access AnnotationPropertiesFormatter
                        .formatProperties(config -> config
                        ^
  class file for com.tngtech.archunit.core.domain.AnnotationFormatter$AnnotationPropertiesFormatter not found
1 error

as soon as I include your changes in JavaClass. I can't reproduce the issue with adding some random new method in that class

I was able to run ./gradlew :archunit:compileJdk9mainJava on ubuntu with openjdk 17. Maybe try running the gradle command directly?

fix for TNG#1564
exclude package-info classes from package metrics calculations

Signed-off-by: John Burns <wakingrufus@gmail.com>
@wakingrufus wakingrufus force-pushed the dependency-package-info branch from 7cd61cd to 7be3ecf Compare April 27, 2026 16:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

package-info should be resolvable through DependencyResolutionProcess

3 participants