Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tycho-4.0.x] Support linked resources #3488

Merged
merged 1 commit into from
Feb 8, 2024
Merged
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 @@ -148,7 +148,7 @@ public void execute() throws MojoExecutionException, MojoFailureException {
if (eclipseProjectValue.isEmpty() || !eclipseProjectValue.get().hasNature(ApiPlugin.NATURE_ID)) {
return;
}

EclipseProject eclipseProject = eclipseProjectValue.get();
if (supportedPackagingTypes.contains(project.getPackaging())) {
Log log = getLog();
if (skipIfReplaced && wasReplaced()) {
Expand Down Expand Up @@ -179,14 +179,15 @@ public void execute() throws MojoExecutionException, MojoFailureException {
}
ApiAnalysisResult analysisResult;
if (parallel) {
analysisResult = performAnalysis(baselineBundles, dependencyBundles, eclipseFramework);
analysisResult = performAnalysis(baselineBundles, dependencyBundles, eclipseFramework, eclipseProject);
} else {
synchronized (ApiAnalysisMojo.class) {
// due to
// https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/issues/3885#note_1266412 we
// can not execute more than one analysis without excessive memory consumption
// unless this is fixed it is safer to only run one analysis at a time
analysisResult = performAnalysis(baselineBundles, dependencyBundles, eclipseFramework);
analysisResult = performAnalysis(baselineBundles, dependencyBundles, eclipseFramework,
eclipseProject);
}
}
log.info("API Analysis finished in " + time(start) + ".");
Expand Down Expand Up @@ -253,10 +254,11 @@ public void execute() throws MojoExecutionException, MojoFailureException {
}

private ApiAnalysisResult performAnalysis(Collection<Path> baselineBundles, Collection<Path> dependencyBundles,
EclipseFramework eclipseFramework) throws MojoExecutionException {
EclipseFramework eclipseFramework, EclipseProject eclipseProject) throws MojoExecutionException {
try {
ApiAnalysis analysis = new ApiAnalysis(baselineBundles, dependencyBundles, project.getName(),
fileToPath(apiFilter), fileToPath(apiPreferences), fileToPath(project.getBasedir()), debug,
eclipseProject.getFile(fileToPath(apiFilter)), eclipseProject.getFile(fileToPath(apiPreferences)),
fileToPath(project.getBasedir()), debug,
fileToPath(project.getArtifact().getFile()),
stringToPath(project.getBuild().getOutputDirectory()));
return eclipseFramework.execute(analysis);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -740,18 +740,22 @@ protected CompilerConfiguration getCompilerConfiguration(List<String> compileSou
CompilerConfiguration compilerConfiguration = super.getCompilerConfiguration(compileSourceRoots,
compileSourceExcludes);
if (useProjectSettings) {
String prefsFilePath = project.getBasedir() + File.separator + PREFS_FILE_PATH;
if (!new File(prefsFilePath).exists()) {
getLog().debug("Parameter 'useProjectSettings' is set to true, but preferences file '" + prefsFilePath
+ "' could not be found");
} else {
Path prefsFilePath = tychoProjectManager.getEclipseProject(project)
.map(eclipse -> eclipse.getFile(PREFS_FILE_PATH))
.orElseGet(() -> new File(project.getBasedir(), PREFS_FILE_PATH).toPath());

if (Files.isRegularFile(prefsFilePath)) {
// make sure that "-properties" is the first custom argument, otherwise it's not possible to override
// any project setting on the command line because the last argument wins.
List<Entry<String, String>> copy = new ArrayList<>(
compilerConfiguration.getCustomCompilerArgumentsEntries());
compilerConfiguration.getCustomCompilerArgumentsEntries().clear();
addCompilerCustomArgument(compilerConfiguration, "-properties", prefsFilePath);
addCompilerCustomArgument(compilerConfiguration, "-properties",
prefsFilePath.toAbsolutePath().toString());
compilerConfiguration.getCustomCompilerArgumentsEntries().addAll(copy);
} else {
getLog().debug("Parameter 'useProjectSettings' is set to true, but preferences file '" + prefsFilePath
+ "' could not be found");
}
}
compilerConfiguration.setTargetVersion(getTargetLevel());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;

/**
* represents information gathered from an "eclipse project" usually stored in a file named
Expand All @@ -33,4 +34,22 @@ public interface EclipseProject {
static EclipseProject parse(Path projectFile) throws IOException {
return ProjectParser.parse(projectFile);
}

/**
* Resolves a path according to the project, this will resolve linked resources
*
* @param path
* @return the resolved path
*/
Path getFile(Path path);

/**
* Resolves a path according to the project, this will resolve linked resources
*
* @param path
* @return the resolved path
*/
Path getFile(String path);

Collection<ProjectVariable> getVariables();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,21 @@

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
Expand All @@ -31,6 +42,8 @@

class ProjectParser {

private static final Pattern PARENT_PROJECT_PATTERN = Pattern.compile("PARENT-(\\d+)-PROJECT_LOC");

public static EclipseProject parse(Path path) throws IOException {
try (InputStream stream = Files.newInputStream(path)) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Expand Down Expand Up @@ -58,6 +71,15 @@ public static EclipseProject parse(Path path) throws IOException {
for (int i = 0; i < length; i++) {
natureSet.add(natureNodes.item(i).getTextContent());
}
NodeList variableNodes = root.getElementsByTagName("variable");
List<ProjectVariable> variables = IntStream.range(0, variableNodes.getLength())
.mapToObj(i -> variableNodes.item(i)).map(Element.class::cast).map(e -> parseVariable(e))
.filter(Objects::nonNull).toList();
NodeList linkNodes = root.getElementsByTagName("link");
Map<Path, LinkDescription> links = IntStream.range(0, linkNodes.getLength())
.mapToObj(i -> linkNodes.item(i)).map(Element.class::cast).map(e -> parseLink(e))
.filter(Objects::nonNull).collect(Collectors.toMap(LinkDescription::name, Function.identity()));

return new EclipseProject() {

@Override
Expand Down Expand Up @@ -100,10 +122,128 @@ public String getComment() {
}
return comment;
}

@Override
public Path getFile(Path path) {
if (path == null) {
return null;
}
if (!path.isAbsolute()) {
path = location.resolve(path);
}
if (Files.isRegularFile(path)) {
//if the file is already there we don't need to bother any links
return path;
}
Path relative = location.relativize(path);
for (Entry<Path, LinkDescription> entry : links.entrySet()) {
Path linkPath = entry.getKey();
if (relative.startsWith(linkPath)) {
LinkDescription link = entry.getValue();
if (link.type() == LinkDescription.FILE) {
//the path must actually match each others as it is a file!
if (linkPath.startsWith(relative)) {
Path resolvedPath = resolvePath(link.locationURI());
return location.resolve(resolvedPath).normalize();
}
} else if (link.type() == LinkDescription.FOLDER) {
Path linkRelative = linkPath.relativize(relative);
Path resolvedPath = resolvePath(link.locationURI());
if (resolvedPath != null) {
return location.resolve(resolvedPath).resolve(linkRelative).normalize();
}
}
}
}
return path;
}

@Override
public Path getFile(String path) {
return getFile(location.resolve(path));
}

@Override
public Collection<ProjectVariable> getVariables() {
return variables;
}
};
} catch (SAXException | ParserConfigurationException e) {
throw new IOException("parsing failed", e);
}
}

private static Path resolvePath(URI uri) {
String schemeSpecificPart = uri.getSchemeSpecificPart();
if (schemeSpecificPart != null) {
Path path = Path.of(schemeSpecificPart);
int count = path.getNameCount();
if (count > 0) {
//only the first path is allowed to be a variable...
Path first = path.getName(0);
String name = first.toString();
Matcher parentMatcher = PARENT_PROJECT_PATTERN.matcher(name);
if (parentMatcher.matches()) {
Path resolvedPath = Path.of("..");
int p = Integer.parseInt(parentMatcher.group(1));
for (int i = 1; i < p; i++) {
resolvedPath = resolvedPath.resolve("..");
}
for (int i = 1; i < count; i++) {
resolvedPath = resolvedPath.resolve(path.getName(i));
}
return resolvedPath;
}
return path;
}
}
return null;
}

private static ProjectVariable parseVariable(Element element) {
try {
String name = element.getElementsByTagName("name").item(0).getTextContent();
String value = element.getElementsByTagName("value").item(0).getTextContent();
return new ProjectVariable(name, value);
} catch (RuntimeException e) {
//something is wrong here...
return null;
}
}

private static LinkDescription parseLink(Element element) {
try {
String name = element.getElementsByTagName("name").item(0).getTextContent();
String type = element.getElementsByTagName("type").item(0).getTextContent();
String locationURI = element.getElementsByTagName("locationURI").item(0).getTextContent();
return new LinkDescription(Path.of(name), Integer.parseInt(type), URI.create(locationURI));
} catch (RuntimeException e) {
//something is wrong here...
return null;
}
}

static final record LinkDescription(Path name, int type, URI locationURI) {
/**
* Type constant (bit mask value 1) which identifies file resources.
*/
static final int FILE = 0x1;

/**
* Type constant (bit mask value 2) which identifies folder resources.
*/
static final int FOLDER = 0x2;

/**
* Type constant (bit mask value 4) which identifies project resources.
*/
static final int PROJECT = 0x4;

/**
* Type constant (bit mask value 8) which identifies the root resource.
*/
static final int ROOT = 0x8;

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.eclipse.tycho.model.project;

public record ProjectVariable(String name, String value) {

}