Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ public interface GraalVMReachabilityMetadataRepository {
*/
Set<DirectoryConfiguration> findConfigurationsFor(Consumer<? super Query> queryBuilder);

/**
* Returns whether the repository covers the requested artifacts. Some repository
* entries intentionally do not provide configuration files, for example artifacts
* marked as not applicable to native-image.
*
* @param queryBuilder the query builder
* @return true if the repository has configuration files or another coverage marker
*/
default boolean isCoveredByRepository(Consumer<? super Query> queryBuilder) {
return !findConfigurationsFor(queryBuilder).isEmpty();
}

/**
* Returns a list of configuration directories for the specified artifact.
* There may be more than one configuration directory for a given artifact,
Expand All @@ -78,6 +90,16 @@ default Set<DirectoryConfiguration> findConfigurationsFor(String gavCoordinates)
return findConfigurationsFor(q -> q.forArtifacts(gavCoordinates));
}

/**
* Returns whether the repository covers the specified artifact.
*
* @param gavCoordinates the artifact GAV coordinates (group:artifact:version)
* @return true if the repository has configuration files or another coverage marker
*/
default boolean isCoveredByRepository(String gavCoordinates) {
return isCoveredByRepository(q -> q.forArtifacts(gavCoordinates));
}

/**
* Returns the set of configuration directories for all the modules supplied
* as an argument.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public static Report run(Collection<DependencyCoordinate> dependencies,
List<Result> results = new ArrayList<>(candidates.size());
for (DependencyCoordinate dependency : candidates) {
try {
Set<DirectoryConfiguration> configurations = repository.findConfigurationsFor(query -> {
boolean covered = repository.isCoveredByRepository(query -> {
query.forArtifact(artifact -> {
artifact.gav(dependency.coordinates());
String forcedVersion = effectiveForcedVersions.get(dependency.groupAndArtifact());
Expand All @@ -127,7 +127,7 @@ public static Report run(Collection<DependencyCoordinate> dependencies,
}
});
});
if (!configurations.isEmpty()) {
if (covered) {
results.add(Result.supported(dependency));
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ public Set<DirectoryConfiguration> findConfigurationsFor(Consumer<? super Query>
return moduleIndex.findConfigurationDirectories(groupId, artifactId)
.stream()
.map(dir -> {
VersionToConfigDirectoryIndex index = artifactIndexes.computeIfAbsent(dir, SingleModuleJsonVersionToConfigDirectoryIndex::new);
VersionToConfigDirectoryIndex index = artifactIndexes.computeIfAbsent(dir,
SingleModuleJsonVersionToConfigDirectoryIndex::new);
if (artifactQuery.getForcedConfig().isPresent()) {
String configVersion = artifactQuery.getForcedConfig().get();
logger.log(groupId, artifactId, version, "Configuration is forced to version " + configVersion);
Expand Down Expand Up @@ -135,6 +136,52 @@ public Set<DirectoryConfiguration> findConfigurationsFor(Consumer<? super Query>
.collect(Collectors.toSet());
}

@Override
public boolean isCoveredByRepository(Consumer<? super Query> queryBuilder) {
DefaultQuery query = new DefaultQuery();
queryBuilder.accept(query);
return query.getArtifacts()
.stream()
.anyMatch(artifactQuery -> {
String groupId = artifactQuery.getGroupId();
String artifactId = artifactQuery.getArtifactId();
String version = artifactQuery.getVersion();
return moduleIndex.findConfigurationDirectories(groupId, artifactId)
.stream()
.anyMatch(dir -> {
VersionToConfigDirectoryIndex index = artifactIndexes.computeIfAbsent(dir, SingleModuleJsonVersionToConfigDirectoryIndex::new);
Optional<DirectoryConfiguration> configuration;
if (artifactQuery.getForcedConfig().isPresent()) {
String configVersion = artifactQuery.getForcedConfig().get();
logger.log(groupId, artifactId, version, "Configuration is forced to version " + configVersion);
configuration = index.findConfiguration(groupId, artifactId, configVersion);
} else {
configuration = index.findConfiguration(groupId, artifactId, version);
if (!configuration.isPresent() && artifactQuery.isUseLatestVersion()) {
logger.log(groupId, artifactId, version,
"Configuration directory not found. Trying latest version.");
configuration = index.findLatestConfigurationFor(groupId, artifactId, version);
if (!configuration.isPresent()) {
logger.log(groupId, artifactId, version, "Latest version not found!");
}
}
}
if (configuration.isPresent()) {
Path path = configuration.get().getDirectory();
logger.log(groupId, artifactId, version,
"Configuration directory is " + rootDirectory.relativize(path));
return true;
}
if (index.isNotForNativeImage(groupId, artifactId, version)) {
logger.log(groupId, artifactId, version, "Artifact is marked as not for native-image.");
return true;
}
logger.log(groupId, artifactId, version, "missing.");
return false;
});
});
}

public Path getRootDirectory() {
return rootDirectory;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,16 @@ public class Artifact {
private final String directory;
private final boolean latest;
private final boolean override;
private final boolean notForNativeImage;
private final Pattern defaultForPattern;

public Artifact(Set<String> versions, String directory,
boolean latest, boolean override, String defaultFor) {
boolean latest, boolean override, String defaultFor, boolean notForNativeImage) {
this.versions = versions;
this.directory = directory;
this.latest = latest;
this.override = override;
this.notForNativeImage = notForNativeImage;
this.defaultForPattern = (defaultFor == null ? null : Pattern.compile(defaultFor));
}

Expand All @@ -78,4 +80,14 @@ public boolean isOverride() {
public boolean isDefaultFor(String version) {
return defaultForPattern != null && defaultForPattern.matcher(version).matches();
}

public boolean isNotForNativeImage(String version) {
if (!notForNativeImage) {
return false;
}
return versions.isEmpty() && defaultForPattern == null
|| versions.contains(version)
|| isDefaultFor(version)
|| latest;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ public Optional<DirectoryConfiguration> findLatestConfigurationFor(String groupI
findConfigurationFor(groupId, artifactId, version, Artifact::isLatest);
}

@Override
public boolean isNotForNativeImage(String groupId, String artifactId, String version) {
return artifacts.stream().anyMatch(artifact -> artifact.isNotForNativeImage(version));
}

private Optional<DirectoryConfiguration> findConfigurationFor(String groupId, String artifactId, String version,
Predicate<? super Artifact> predicate) {
if (artifacts.isEmpty()) {
Expand All @@ -134,7 +139,8 @@ private Artifact fromJson(JSONObject json) {
boolean latest = json.optBoolean("latest");
boolean override = json.optBoolean("override");
String defaultFor = json.optString("default-for", null);
return new Artifact(testVersions, directory, latest, override, defaultFor);
boolean notForNativeImage = json.optBoolean("not-for-native-image");
return new Artifact(testVersions, directory, latest, override, defaultFor, notForNativeImage);
}

private Set<String> readTestedVersions(JSONArray array) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,16 @@ public interface VersionToConfigDirectoryIndex {
* @return a configuration, or empty if no configuration directory is available
*/
Optional<DirectoryConfiguration> findLatestConfigurationFor(String groupId, String artifactId, String version);

/**
* Returns whether the artifact is marked as not applicable to native-image.
*
* @param groupId the group ID of the artifact
* @param artifactId the artifact ID of the artifact
* @param version the version of the artifact
* @return true if the artifact is intentionally not covered by configuration files
*/
default boolean isNotForNativeImage(String groupId, String artifactId, String version) {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import com.github.openjson.JSONObject;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;
import org.graalvm.reachability.internal.FileSystemRepository;
import org.junit.jupiter.api.Test;

import java.io.IOException;
Expand Down Expand Up @@ -139,6 +140,36 @@ void reportsSupportedAndMissingDependenciesAndGeneratesPrefilledLinks() {
"console should list the prefilled issue URL as a numbered footnote, was:\n" + console);
}

@Test
void treatsNotForNativeImageIndexEntriesAsSupported() throws Exception {
Path repoPath = Path.of(MissingMetadataCommandSupportTest.class
.getResource("/repos/repo-not-for-native-image")
.toURI());
MissingMetadataCommandSupport.Report report = MissingMetadataCommandSupport.run(
List.of(new MissingMetadataCommandSupport.DependencyCoordinate("com.foo", "native-only", "1.0")),
new FileSystemRepository(repoPath),
Set.of(),
java.util.Map.of(),
new MissingMetadataCommandSupport.Options(
"maven",
"demo-app",
repoPath.toUri().toString(),
false,
null,
MissingMetadataCommandSupport.DEFAULT_TARGET_REPOSITORY,
"http://127.0.0.1:9/api/v3",
Clock.fixed(Instant.parse("2026-04-09T10:00:00Z"), ZoneOffset.UTC)
)
);

JSONObject json = new JSONObject(report.toJsonString());
JSONObject summary = json.getJSONObject("summary");
JSONObject result = json.getJSONArray("results").getJSONObject(0);
assertEquals(1, summary.getInt("supported"));
assertEquals(0, summary.getInt("missing"));
assertEquals("supported", result.getString("status"));
}

@Test
void reusesExistingOpenIssueAndSuppressesDuplicateSearchesByGroupAndArtifact() throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(0), 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,17 @@ void canUseLatestConfigDir() {
result.isEmpty();
}

@Test
void notForNativeImageIndexCoversArtifactWithoutConfigurationFiles() {
// when:
withRepo("repo-not-for-native-image");

// then:
assertTrue(repository.isCoveredByRepository("com.foo:native-only:1.0"));
lookup("com.foo:native-only:1.0");
result.isEmpty();
}

private void lookup(Consumer<? super Query> builder) {
result = new Result(repository.findConfigurationsFor(builder), repoPath);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,15 @@ void checkIndexWithDefaultFor() throws URISyntaxException {
assertEquals(repoPath.resolve("2.0"), latest.get().getDirectory());
}

@Test
void checkNotForNativeImageIndex() throws URISyntaxException {
withIndex("artifact-not-for-native-image/com.foo/native-only");

Optional<DirectoryConfiguration> config = index.findConfiguration("com.foo", "native-only", "1.0");
assertFalse(config.isPresent());
assertTrue(index.isNotForNativeImage("com.foo", "native-only", "1.0"));
}

private void withIndex(String json) throws URISyntaxException {
repoPath = new File(SingleModuleJsonVersionToConfigDirectoryIndexTest.class.getResource("/json/" + json).toURI()).toPath();
index = new SingleModuleJsonVersionToConfigDirectoryIndex(repoPath);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"not-for-native-image": true,
"reason": "This artifact is not applicable to native-image.",
"replacement": "com.foo:native-classes:1.0"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"not-for-native-image": true,
"reason": "This artifact is not applicable to native-image.",
"replacement": "com.foo:native-classes:1.0"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":"1.2.0"}
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ public Set<DirectoryConfiguration> findConfigurationsFor(Consumer<? super Query>
return repository.findConfigurationsFor(queryBuilder);
}

@Override
public boolean isCoveredByRepository(Consumer<? super Query> queryBuilder) {
return repository.isCoveredByRepository(queryBuilder);
}

/**
* Returns a list of configuration directories for the specified artifact.
* There may be more than one configuration directory for a given artifact,
Expand Down
Loading