org.bndtools.builder.DeltaWrapper Maven / Gradle / Ivy
package org.bndtools.builder;
import static java.util.stream.Collectors.toSet;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Stream;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import aQute.bnd.build.Project;
import aQute.bnd.osgi.Constants;
import aQute.bnd.osgi.Instructions;
import aQute.bnd.osgi.Processor;
import aQute.lib.io.IO;
import aQute.lib.strings.Strings;
import bndtools.central.Central;
class DeltaWrapper {
private static final IPath EXT = new Path("/cnf/ext");
private final Project model;
private final IResourceDelta delta;
private final BuildLogger log;
static final String[] defaultIgnoreProperties = new String[] {
Constants.DEFAULT_PROP_SRC_DIR, //
Constants.DEFAULT_PROP_TESTSRC_DIR, //
Constants.DEFAULT_PROP_TESTBIN_DIR, //
Constants.DEFAULT_PROP_TARGET_DIR
};
DeltaWrapper(Project model, IResourceDelta delta, BuildLogger log) {
this.model = model;
this.delta = delta;
this.log = log;
}
boolean hasCnfChanged() throws Exception {
if (delta == null) {
log.basic("Full build because delta for cnf is null");
return true;
}
if (havePropertiesChanged(model.getWorkspace())) {
log.basic("cnf properties have changed");
return true;
}
if (isNonMarkerChange(delta.findMember(EXT)))
return true;
return false;
}
boolean isNonMarkerChange(IResourceDelta delta) {
if (delta == null)
return false;
int kindMask = IResourceDelta.CHANGED | IResourceDelta.ADDED | IResourceDelta.REMOVED;
if ((kindMask & delta.getKind()) > 0) {
int flags = delta.getFlags();
if (flags > 0 && flags != IResourceDelta.MARKERS) {
log.basic("Delta affects resource %s with kind=%d and flags=%d", delta.getFullPath(), delta.getKind(),
delta.getFlags());
return true;
}
}
IResourceDelta[] children = delta.getAffectedChildren();
if (children != null)
for (IResourceDelta child : children) {
if (isNonMarkerChange(child))
return true;
}
return false;
}
/*
* Any change other then src, test, test_bin, or generated is fair game.
*/
boolean hasProjectChanged() throws Exception {
if (delta == null) {
log.basic("Full build because delta is null");
return true;
}
if (havePropertiesChanged(model)) {
log.basic("project properties have changed");
return true;
}
final AtomicBoolean result = new AtomicBoolean(false);
delta.accept(new IResourceDeltaVisitor() {
Set ignore = null;
@Override
public boolean visit(IResourceDelta visitDelta) throws CoreException {
if ((delta.getKind() & (IResourceDelta.ADDED | IResourceDelta.CHANGED | IResourceDelta.REMOVED)) == 0)
return false;
IResource resource = visitDelta.getResource();
if (resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT)
return true;
String path = resource.getProjectRelativePath()
.toString();
if (resource.getType() == IResource.FOLDER) {
if (ignore == null) {
ignore = Stream.concat(Arrays.stream(defaultIgnoreProperties)
.map(model::getProperty), //
Strings.splitAsStream(model.mergeProperties(Constants.BUILDERIGNORE)))
.collect(toSet());
}
if (check(path, ignore.stream())) {
return false;
}
}
if (IResourceDelta.MARKERS == delta.getFlags())
return false;
log.basic("%s changed", resource);
result.set(true);
return false;
}
});
return result.get();
}
boolean hasBuildfile() throws Exception {
File f = new File(model.getTarget(), Constants.BUILDFILES);
return has(f);
}
boolean havePropertiesChanged(Processor processor) throws Exception {
if (has(processor.getPropertiesFile()))
return true;
for (File incl : processor.getIncluded()) {
if (has(incl))
return true;
}
return false;
}
private boolean has(File f) throws Exception {
if (f == null)
return false;
if (delta == null)
return false;
IPath path = Central.toPath(f);
if (path == null)
return false;
IPath relativePath = path.makeRelativeTo(delta.getFullPath());
if (relativePath == null)
return false;
IResourceDelta delta = this.delta.findMember(relativePath);
if (delta == null)
return false;
//
// If ONLY the markers are changed we should ignore this delta
// or get an infinite loop
//
if ((delta.getFlags() == IResourceDelta.MARKERS))
return false;
if (delta.getKind() == IResourceDelta.ADDED || delta.getKind() == IResourceDelta.CHANGED
|| delta.getKind() == IResourceDelta.REMOVED)
return true;
return false;
}
static boolean check(String changed, String prefix) {
if (changed.equals(prefix))
return true;
if (changed.length() <= prefix.length())
return false;
char c = changed.charAt(prefix.length());
if (c == '/' && changed.startsWith(prefix))
return true;
return false;
}
static boolean check(String changed, Stream prefixes) {
return prefixes.anyMatch(prefix -> check(changed, prefix));
}
public boolean isTestBin(IResource resource) {
String path = resource.getProjectRelativePath()
.toString();
return check(path, model.getProperty(Constants.DEFAULT_PROP_TESTSRC_DIR));
}
/**
* Check if the target JARs have gone or out-of-date. We look for the
* buildfiles and the listed jars. If anything is odd, we rebuild.
*/
public boolean hasOutOfDateTarget(Project model, long lastModified) throws Exception {
//
// $/buildfiles must exists
//
File[] buildFiles = model.getBuildFiles(false);
if (buildFiles == null)
return true;
File file = IO.getFile(model.getTarget(), Constants.BUILDFILES);
if (!file.isFile() || (file.lastModified() < lastModified))
return true;
//
// buildfiles = line +
//
try {
String bf = IO.collect(file);
if (bf.isEmpty())
return true;
String[] split = bf.split("\r?\n");
for (String line : split) {
line = line.trim();
if (line.startsWith("#"))
continue;
File f = IO.getFile(model.getTarget(), line);
if (!f.isFile() || (f.lastModified() < lastModified))
return true;
}
} catch (Exception e) {
return true;
}
return false;
}
public boolean hasChangedSubbundles() throws CoreException {
if (delta == null)
return true;
final List files = toFiles(false);
Instructions instr = new Instructions(model.getProperty(Constants.SUB));
Collection selected = instr.select(files, false);
return !selected.isEmpty();
}
private List toFiles(final boolean dirsAlso) throws CoreException {
if (delta == null)
return null;
final List files = new ArrayList<>();
delta.accept(d -> {
IResource r = d.getResource();
if (r != null) {
if (r.getType() == IResource.PROJECT) {
return true;
}
if (r.getType() == IResource.FILE) {
files.add(d.getProjectRelativePath()
.toString());
return false;
}
if (r.getType() == IResource.FOLDER) {
if (dirsAlso)
files.add(d.getProjectRelativePath()
.toString());
return true;
}
}
return false;
});
return files;
}
public boolean hasEclipseChanged() {
if (delta == null)
return true;
IPath path = new Path(".classpath");
return delta.findMember(path) != null;
}
}