Blame SOURCES/0002-Implement-a-custom-resolver-for-Tycho-in-local-mode.patch

35a07c
From 623916240bd0626e7fd5146bfb2acfe9c77502da Mon Sep 17 00:00:00 2001
35a07c
From: Roland Grunberg <rgrunber@redhat.com>
35a07c
Date: Tue, 12 Jun 2012 10:38:51 -0400
35a07c
Subject: [PATCH 2/6] Implement a custom resolver for Tycho in local mode.
35a07c
35a07c
When running in local mode, dependencies should be resolved by looking
35a07c
on the local system. Remote repositories should be ignored unless
35a07c
offline mode is disabled.
35a07c
35a07c
Use fedoraproject-p2 to resolve bundles from their system location.
35a07c
35a07c
Relax constraints for bundles used in Tycho's Equinox runtime.
35a07c
35a07c
Since Fedora 17, we need an Execution Environment of at least JavaSE-1.6
35a07c
for Eclipse bundles. Eclipse Juno platform bundles depend on
35a07c
javax.annotation. In Fedora this is provided by geronimo-annotation, but
35a07c
has a dependency on javax.lang.model (since 1.6).
35a07c
35a07c
Use the defined target environments in local mode when the property
35a07c
tycho.local.keepTarget is set.
35a07c
35a07c
In situations where Tycho must resolve maven artifacts, upstream's
35a07c
implementation only looks in the reactor cache. In Fedora, maven
35a07c
artifacts may be located on the system using repository layouts
35a07c
understood by XMvn. Therefore, when an artifact is not found in the
35a07c
reactor cache, resolution should be attempted using the XMvn Resolver.
35a07c
35a07c
Upstream/Fedora Tycho differ in the kind of OSGi Runtime used
35a07c
(org.eclipse.tycho:tycho-bundles-external:zip) so use separate location
35a07c
for our runtime (fedora-eclipse) to avoid collisions.
35a07c
35a07c
Change-Id: Ia1ece07ece2412bc4a88901631f3f651ad2b634b
35a07c
---
35a07c
 .../internal/DefaultEquinoxEmbedder.java      | 11 ++++-
35a07c
 .../remote/RemoteRepositoryCacheManager.java  | 11 +++++
35a07c
 .../p2/target/TargetDefinitionResolver.java   | 17 +++++--
35a07c
 .../target/TargetPlatformBundlePublisher.java | 15 ++-----
35a07c
 .../p2/target/TargetPlatformFactoryImpl.java  | 45 +++++++++++++++++--
35a07c
 .../p2/repository/LocalRepositoryReader.java  | 44 +++++++++++++++++-
35a07c
 .../TargetPlatformConfigurationStub.java      |  6 ++-
35a07c
 .../tycho-bundles-external.product            |  3 ++
35a07c
 .../tycho/core/locking/FileLockerImpl.java    | 26 ++++++++---
35a07c
 .../maven/TychoMavenLifecycleParticipant.java | 13 ++++++
35a07c
 .../core/osgitools/AbstractTychoProject.java  | 23 ++++++++++
35a07c
 .../core/osgitools/OsgiBundleProject.java     |  5 ++-
35a07c
 ...aultTargetPlatformConfigurationReader.java |  6 ++-
35a07c
 .../osgi/runtime/TychoOsgiRuntimeLocator.java | 27 ++++++++---
35a07c
 tycho-p2/tycho-p2-facade/pom.xml              |  5 +++
35a07c
 .../p2/resolver/P2DependencyResolver.java     |  8 ++++
35a07c
 16 files changed, 227 insertions(+), 38 deletions(-)
35a07c
35a07c
diff --git a/sisu-equinox/sisu-equinox-embedder/src/main/java/org/eclipse/sisu/equinox/embedder/internal/DefaultEquinoxEmbedder.java b/sisu-equinox/sisu-equinox-embedder/src/main/java/org/eclipse/sisu/equinox/embedder/internal/DefaultEquinoxEmbedder.java
35a07c
index 359c464..b644539 100644
35a07c
--- a/sisu-equinox/sisu-equinox-embedder/src/main/java/org/eclipse/sisu/equinox/embedder/internal/DefaultEquinoxEmbedder.java
35a07c
+++ b/sisu-equinox/sisu-equinox-embedder/src/main/java/org/eclipse/sisu/equinox/embedder/internal/DefaultEquinoxEmbedder.java
35a07c
@@ -240,7 +240,14 @@ public class DefaultEquinoxEmbedder extends AbstractLogEnabled
35a07c
                     if (verIdx > 0) {
35a07c
                         bundles.append(name.substring(0, verIdx));
35a07c
                     } else {
35a07c
-                        throw new EquinoxEmbedderException("File name doesn't match expected pattern: " + file);
35a07c
+                        // In Fedora, NAME_VERSION.QUALIFIER.jar is too fragile.
35a07c
+                        // Let's also accept NAME.jar
35a07c
+                        verIdx = name.lastIndexOf(".jar");
35a07c
+                        if (verIdx > 0) {
35a07c
+                            bundles.append(name.substring(0, verIdx));
35a07c
+                        } else {
35a07c
+                            throw new EquinoxEmbedderException("File name doesn't match expected pattern: " + file);
35a07c
+                        }
35a07c
                     }
35a07c
                 }
35a07c
             }
35a07c
@@ -248,7 +255,7 @@ public class DefaultEquinoxEmbedder extends AbstractLogEnabled
35a07c
     }
35a07c
 
35a07c
     protected boolean isFrameworkBundle(File file) {
35a07c
-        return file.getName().startsWith("org.eclipse.osgi_");
35a07c
+        return file.getName().startsWith("org.eclipse.osgi_") || file.getName().equals("org.eclipse.osgi.jar");
35a07c
     }
35a07c
 
35a07c
     String getReferenceUrl(File file) {
35a07c
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/remote/RemoteRepositoryCacheManager.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/remote/RemoteRepositoryCacheManager.java
35a07c
index 1f233e1..c9a6dc1 100644
35a07c
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/remote/RemoteRepositoryCacheManager.java
35a07c
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/remote/RemoteRepositoryCacheManager.java
35a07c
@@ -12,14 +12,18 @@ package org.eclipse.tycho.p2.remote;
35a07c
 
35a07c
 import java.io.File;
35a07c
 import java.io.IOException;
35a07c
+import java.net.MalformedURLException;
35a07c
 import java.net.URI;
35a07c
+import java.net.URL;
35a07c
 
35a07c
 import org.eclipse.core.runtime.IProgressMonitor;
35a07c
 import org.eclipse.core.runtime.IStatus;
35a07c
 import org.eclipse.core.runtime.Status;
35a07c
 import org.eclipse.equinox.internal.p2.repository.CacheManager;
35a07c
+import org.eclipse.equinox.internal.p2.repository.Messages;
35a07c
 import org.eclipse.equinox.internal.p2.repository.Transport;
35a07c
 import org.eclipse.equinox.p2.core.ProvisionException;
35a07c
+import org.eclipse.osgi.util.NLS;
35a07c
 import org.eclipse.tycho.core.shared.MavenContext;
35a07c
 import org.eclipse.tycho.core.shared.MavenLogger;
35a07c
 import org.eclipse.tycho.p2.impl.Activator;
35a07c
@@ -51,6 +55,13 @@ class RemoteRepositoryCacheManager extends CacheManager {
35a07c
     @Override
35a07c
     public File createCache(URI repositoryLocation, String prefix, IProgressMonitor monitor)
35a07c
             throws IOException, ProvisionException {
35a07c
+        try {
35a07c
+            new URL(repositoryLocation.toASCIIString());
35a07c
+        } catch (MalformedURLException e) {
35a07c
+            throw new ProvisionException(new Status(IStatus.ERROR, org.eclipse.equinox.internal.p2.repository.Activator.ID,
35a07c
+                    ProvisionException.REPOSITORY_NOT_FOUND, NLS.bind(Messages.CacheManager_CannotLoadNonUrlLocation,
35a07c
+                            repositoryLocation), null));
35a07c
+        }
35a07c
         File cacheFile = getCache(repositoryLocation, prefix);
35a07c
         if (offline) {
35a07c
             if (cacheFile != null) {
35a07c
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetDefinitionResolver.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetDefinitionResolver.java
35a07c
index e5e99a1..1cf8089 100644
35a07c
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetDefinitionResolver.java
35a07c
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetDefinitionResolver.java
35a07c
@@ -20,6 +20,7 @@ import java.util.Set;
35a07c
 
35a07c
 import org.eclipse.core.runtime.IProgressMonitor;
35a07c
 import org.eclipse.core.runtime.NullProgressMonitor;
35a07c
+import org.eclipse.core.runtime.URIUtil;
35a07c
 import org.eclipse.equinox.p2.core.IProvisioningAgent;
35a07c
 import org.eclipse.equinox.p2.core.ProvisionException;
35a07c
 import org.eclipse.equinox.p2.metadata.IInstallableUnit;
35a07c
@@ -121,7 +122,12 @@ public final class TargetDefinitionResolver {
35a07c
                 resolverRun.addLocation((InstallableUnitLocation) locationDefinition);
35a07c
 
35a07c
                 for (Repository repository : ((InstallableUnitLocation) locationDefinition).getRepositories()) {
35a07c
-                    artifactRepositories.add(repository.getLocation());
35a07c
+                    // We cannot resolve a non-file URI in local mode
35a07c
+                    if ((System.getProperty("TYCHO_MVN_LOCAL") == null && System.getProperty("TYCHO_MVN_RPMBUILD") == null)
35a07c
+                            || URIUtil.isFileURI(repository.getLocation())
35a07c
+                            || "fedora".equals(repository.getLocation().getScheme())) {
35a07c
+                        artifactRepositories.add(repository.getLocation());
35a07c
+                    }
35a07c
                 }
35a07c
             } else {
35a07c
                 logger.warn("Target location type '" + locationDefinition.getTypeDescription() + "' is not supported");
35a07c
@@ -278,8 +284,13 @@ public final class TargetDefinitionResolver {
35a07c
 
35a07c
             loadedRepositories = new ArrayList<>();
35a07c
             for (Repository repository : locationDefinition.getRepositories()) {
35a07c
-                repositoryIdManager.addMapping(repository.getId(), repository.getLocation());
35a07c
-                loadedRepositories.add(loadRepository(repository));
35a07c
+                // We cannot resolve a non-file URI in local mode
35a07c
+                if ((System.getProperty("TYCHO_MVN_LOCAL") == null && System.getProperty("TYCHO_MVN_RPMBUILD") == null)
35a07c
+                        || URIUtil.isFileURI(repository.getLocation())
35a07c
+                        || "fedora".equals(repository.getLocation().getScheme())) {
35a07c
+                    repositoryIdManager.addMapping(repository.getId(), repository.getLocation());
35a07c
+                    loadedRepositories.add(loadRepository(repository));
35a07c
+                }
35a07c
             }
35a07c
         }
35a07c
 
35a07c
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetPlatformBundlePublisher.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetPlatformBundlePublisher.java
35a07c
index 6a59c2a..0d15db9 100644
35a07c
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetPlatformBundlePublisher.java
35a07c
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetPlatformBundlePublisher.java
35a07c
@@ -28,6 +28,7 @@ import org.eclipse.tycho.core.shared.MavenLogger;
35a07c
 import org.eclipse.tycho.p2.impl.publisher.MavenPropertiesAdvice;
35a07c
 import org.eclipse.tycho.p2.impl.publisher.repo.TransientArtifactRepository;
35a07c
 import org.eclipse.tycho.p2.metadata.IArtifactFacade;
35a07c
+import org.eclipse.tycho.p2.repository.LocalRepositoryReader;
35a07c
 import org.eclipse.tycho.p2.repository.MavenRepositoryCoordinates;
35a07c
 import org.eclipse.tycho.repository.local.GAVArtifactDescriptor;
35a07c
 import org.eclipse.tycho.repository.p2base.artifact.provider.IRawArtifactFileProvider;
35a07c
@@ -216,15 +217,6 @@ public class TargetPlatformBundlePublisher {
35a07c
             GAVArtifactDescriptor descriptorForRepository = new GAVArtifactDescriptor(baseDescriptor,
35a07c
                     repositoryCoordinates);
35a07c
 
35a07c
-            File requiredArtifactLocation = new File(getBaseDir(),
35a07c
-                    descriptorForRepository.getMavenCoordinates().getLocalRepositoryPath());
35a07c
-            File actualArtifactLocation = mavenArtifact.getLocation();
35a07c
-            if (!equivalentPaths(requiredArtifactLocation, actualArtifactLocation)) {
35a07c
-                throw new AssertionFailedException(
35a07c
-                        "The Maven artifact to be added to the target platform is not stored at the required location on disk: required \""
35a07c
-                                + requiredArtifactLocation + "\" but was \"" + actualArtifactLocation + "\"");
35a07c
-            }
35a07c
-
35a07c
             internalAddInternalDescriptor(descriptorForRepository);
35a07c
         }
35a07c
 
35a07c
@@ -257,8 +249,9 @@ public class TargetPlatformBundlePublisher {
35a07c
 
35a07c
         @Override
35a07c
         protected File internalGetArtifactStorageLocation(IArtifactDescriptor descriptor) {
35a07c
-            String relativePath = toInternalDescriptor(descriptor).getMavenCoordinates().getLocalRepositoryPath();
35a07c
-            return new File(getBaseDir(), relativePath);
35a07c
+            MavenRepositoryCoordinates coord = toInternalDescriptor(descriptor).getMavenCoordinates();
35a07c
+            LocalRepositoryReader reader = new LocalRepositoryReader(getBaseDir());
35a07c
+            return reader.getLocalArtifactLocation(coord.getGav(), coord.getClassifier(), coord.getExtensionOrDefault());
35a07c
         }
35a07c
 
35a07c
         private File getBaseDir() {
35a07c
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetPlatformFactoryImpl.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetPlatformFactoryImpl.java
35a07c
index 7854bca..2247be6 100644
35a07c
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetPlatformFactoryImpl.java
35a07c
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetPlatformFactoryImpl.java
35a07c
@@ -32,6 +32,9 @@ import org.eclipse.core.runtime.URIUtil;
35a07c
 import org.eclipse.equinox.p2.core.IProvisioningAgent;
35a07c
 import org.eclipse.equinox.p2.core.ProvisionException;
35a07c
 import org.eclipse.equinox.p2.metadata.IInstallableUnit;
35a07c
+import org.eclipse.equinox.p2.metadata.expression.ExpressionUtil;
35a07c
+import org.eclipse.equinox.p2.metadata.expression.IExpression;
35a07c
+import org.eclipse.equinox.p2.query.IQuery;
35a07c
 import org.eclipse.equinox.p2.query.IQueryResult;
35a07c
 import org.eclipse.equinox.p2.query.QueryUtil;
35a07c
 import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager;
35a07c
@@ -273,9 +276,43 @@ public class TargetPlatformFactoryImpl implements TargetPlatformFactory {
35a07c
             metadataRepositories.add(localMetadataRepository);
35a07c
         }
35a07c
 
35a07c
-        for (IMetadataRepository repository : metadataRepositories) {
35a07c
-            IQueryResult<IInstallableUnit> matches = repository.query(QueryUtil.ALL_UNITS, monitor);
35a07c
-            result.addAll(matches.toUnmodifiableSet());
35a07c
+        if (System.getProperty("TYCHO_MVN_LOCAL") != null) {
35a07c
+            final IExpression notmatchIU_ID = ExpressionUtil.parse("id != $0");
35a07c
+            Set<IMetadataRepository> fedoraRepos = new HashSet<IMetadataRepository> ();
35a07c
+
35a07c
+            // Sanity check even though the repo we want should be at index 1
35a07c
+            for (IMetadataRepository repository : metadataRepositories) {
35a07c
+                if ("fedora".equals(repository.getLocation().getScheme())) {
35a07c
+                    fedoraRepos.add(repository);
35a07c
+                }
35a07c
+            }
35a07c
+
35a07c
+            IQuery<IInstallableUnit> noLocalIUs = QueryUtil.createIUAnyQuery();
35a07c
+
35a07c
+            // Create a conjunction query that negates all IUs on the local system
35a07c
+            for (IMetadataRepository repo : fedoraRepos) {
35a07c
+                for (IInstallableUnit unit : repo.query(QueryUtil.ALL_UNITS, null).toUnmodifiableSet()) {
35a07c
+                    noLocalIUs = QueryUtil.createCompoundQuery(noLocalIUs,
35a07c
+                            QueryUtil.createMatchQuery(notmatchIU_ID, unit.getId()), true);
35a07c
+                }
35a07c
+            }
35a07c
+
35a07c
+            for (IMetadataRepository repository : metadataRepositories) {
35a07c
+                IQueryResult<IInstallableUnit> matches;
35a07c
+                if ("fedora".equals(repository.getLocation().getScheme())) {
35a07c
+                    matches = repository.query(QueryUtil.ALL_UNITS, monitor);
35a07c
+                } else {
35a07c
+                    // Don't collect any remote IUs that can be found on the system
35a07c
+                    // This will favour IUs in the system local p2 repository
35a07c
+                    matches = repository.query(noLocalIUs, monitor);
35a07c
+                }
35a07c
+                result.addAll(matches.toUnmodifiableSet());
35a07c
+            }
35a07c
+        } else {
35a07c
+            for (IMetadataRepository repository : metadataRepositories) {
35a07c
+                IQueryResult<IInstallableUnit> matches = repository.query(QueryUtil.ALL_UNITS, monitor);
35a07c
+                result.addAll(matches.toUnmodifiableSet());
35a07c
+            }
35a07c
         }
35a07c
 
35a07c
         result.addAll(pomDependenciesContent.gatherMavenInstallableUnits());
35a07c
@@ -329,7 +366,7 @@ public class TargetPlatformFactoryImpl implements TargetPlatformFactory {
35a07c
         List<URI> allRemoteArtifactRepositories = new ArrayList<>();
35a07c
 
35a07c
         for (MavenRepositoryLocation location : completeRepositories) {
35a07c
-            if (!offline || URIUtil.isFileURI(location.getURL())) {
35a07c
+            if (!offline || URIUtil.isFileURI(location.getURL()) || "fedora".equals(location.getURL().getScheme())) {
35a07c
                 allRemoteArtifactRepositories.add(location.getURL());
35a07c
             }
35a07c
         }
35a07c
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.shared/src/main/java/org/eclipse/tycho/p2/repository/LocalRepositoryReader.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.shared/src/main/java/org/eclipse/tycho/p2/repository/LocalRepositoryReader.java
35a07c
index e05f871..74b8028 100644
35a07c
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.shared/src/main/java/org/eclipse/tycho/p2/repository/LocalRepositoryReader.java
35a07c
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.shared/src/main/java/org/eclipse/tycho/p2/repository/LocalRepositoryReader.java
35a07c
@@ -11,6 +11,8 @@
35a07c
 package org.eclipse.tycho.p2.repository;
35a07c
 
35a07c
 import java.io.File;
35a07c
+import java.lang.reflect.Constructor;
35a07c
+import java.lang.reflect.Method;
35a07c
 
35a07c
 public class LocalRepositoryReader implements RepositoryReader {
35a07c
 
35a07c
@@ -21,8 +23,46 @@ public class LocalRepositoryReader implements RepositoryReader {
35a07c
     }
35a07c
 
35a07c
     @Override
35a07c
+    @SuppressWarnings({ "unchecked", "rawtypes" })
35a07c
     public File getLocalArtifactLocation(GAV gav, String classifier, String extension) {
35a07c
-        return new File(localMavenRepositoryRoot, RepositoryLayoutHelper.getRelativePath(gav, classifier, extension));
35a07c
-    }
35a07c
+        File file = new File(localMavenRepositoryRoot, RepositoryLayoutHelper.getRelativePath(gav, classifier,
35a07c
+                extension));
35a07c
+        // In Fedora the artifact may be in an XMvn-defined repository location (not in reactor cache)
35a07c
+        if (!file.exists()) {
35a07c
+            try {
35a07c
+                // Create Plexus config
35a07c
+                Class pcclazz = Class.forName("org.codehaus.plexus.ContainerConfiguration");
35a07c
+                Object conf = Class.forName("org.codehaus.plexus.DefaultContainerConfiguration").newInstance();
35a07c
+                pcclazz.getMethod("setAutoWiring", boolean.class).invoke(conf, true);
35a07c
+                pcclazz.getMethod("setClassPathScanning", String.class).invoke(conf, "index");
35a07c
+
35a07c
+                // Use plexus container to lookup the reader
35a07c
+                Class pclazz = Class.forName("org.codehaus.plexus.DefaultPlexusContainer");
35a07c
+                Object plexus = pclazz.getConstructor(pcclazz).newInstance(conf);
35a07c
+
35a07c
+                // Retrieve the workspace reader from the plexus container
35a07c
+                Method mLookup = pclazz.getMethod("lookup", String.class, String.class);
35a07c
+                Object reader = mLookup.invoke(plexus, "org.eclipse.aether.repository.WorkspaceReader", "ide");
35a07c
 
35a07c
+                // Create an Aether Artifact based on GAV, classifier, and extension
35a07c
+                Class iartclazz = Class.forName("org.eclipse.aether.artifact.Artifact");
35a07c
+                Class artclazz = Class.forName("org.eclipse.aether.artifact.DefaultArtifact");
35a07c
+                Constructor cNew = artclazz.getConstructor(String.class, String.class, String.class, String.class,
35a07c
+                        String.class);
35a07c
+                Object artifact = cNew.newInstance(gav.getGroupId(), gav.getArtifactId(), classifier, extension,
35a07c
+                        gav.getVersion());
35a07c
+
35a07c
+                // Invoke "findArtifact" method of the workspace reader on the artifact
35a07c
+                Method mfindArtifact = reader.getClass().getMethod("findArtifact", iartclazz);
35a07c
+                File newFile = (File) mfindArtifact.invoke(reader, artifact);
35a07c
+                if (newFile != null) {
35a07c
+                    file = newFile;
35a07c
+                }
35a07c
+            } catch (Exception e) {
35a07c
+                e.printStackTrace();
35a07c
+            }
35a07c
+        }
35a07c
+        return file;
35a07c
+
35a07c
+    }
35a07c
 }
35a07c
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.shared/src/main/java/org/eclipse/tycho/p2/target/facade/TargetPlatformConfigurationStub.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.shared/src/main/java/org/eclipse/tycho/p2/target/facade/TargetPlatformConfigurationStub.java
35a07c
index 19d12c6..abe89e8 100644
35a07c
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.shared/src/main/java/org/eclipse/tycho/p2/target/facade/TargetPlatformConfigurationStub.java
35a07c
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.shared/src/main/java/org/eclipse/tycho/p2/target/facade/TargetPlatformConfigurationStub.java
35a07c
@@ -56,7 +56,11 @@ public class TargetPlatformConfigurationStub {
35a07c
     }
35a07c
 
35a07c
     public void addP2Repository(MavenRepositoryLocation location) {
35a07c
-        this.repositories.add(location);
35a07c
+        // We cannot resolve a non-file URI in local mode while offline
35a07c
+        if (System.getProperty("TYCHO_MVN_RPMBUILD") == null || "file".equalsIgnoreCase(location.getURL().getScheme())
35a07c
+                || "fedora".equalsIgnoreCase(location.getURL().getScheme())) {
35a07c
+            this.repositories.add(location);
35a07c
+        }
35a07c
     }
35a07c
 
35a07c
     // convenience method for tests
35a07c
diff --git a/tycho-bundles/tycho-bundles-external/tycho-bundles-external.product b/tycho-bundles/tycho-bundles-external/tycho-bundles-external.product
35a07c
index 11b7c8b..182122d 100644
35a07c
--- a/tycho-bundles/tycho-bundles-external/tycho-bundles-external.product
35a07c
+++ b/tycho-bundles/tycho-bundles-external/tycho-bundles-external.product
35a07c
@@ -79,6 +79,9 @@
35a07c
       <plugin id="org.sat4j.core"/>
35a07c
       <plugin id="org.sat4j.pb"/>
35a07c
       <plugin id="org.tukaani.xz"/>
35a07c
+      <plugin id="org.kxml2"/>
35a07c
+      <plugin id="org.xmlpull"/>
35a07c
+      <plugin id="org.fedoraproject.p2"/>
35a07c
    </plugins>
35a07c
 
35a07c
    <configurations>
35a07c
diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/locking/FileLockerImpl.java b/tycho-core/src/main/java/org/eclipse/tycho/core/locking/FileLockerImpl.java
35a07c
index e4612c3..3abcc5d 100644
35a07c
--- a/tycho-core/src/main/java/org/eclipse/tycho/core/locking/FileLockerImpl.java
35a07c
+++ b/tycho-core/src/main/java/org/eclipse/tycho/core/locking/FileLockerImpl.java
35a07c
@@ -27,22 +27,36 @@ public class FileLockerImpl implements FileLocker {
35a07c
     final File lockMarkerFile;
35a07c
 
35a07c
     public FileLockerImpl(File file, Location anyLocation) {
35a07c
+        File lockFileCandidate = null;
35a07c
         try {
35a07c
             if (file.isDirectory()) {
35a07c
-                this.lockMarkerFile = new File(file, LOCKFILE_SUFFIX).getCanonicalFile();
35a07c
+                lockFileCandidate = new File(file, LOCKFILE_SUFFIX).getCanonicalFile();
35a07c
             } else {
35a07c
-                this.lockMarkerFile = new File(file.getParentFile(), file.getName() + LOCKFILE_SUFFIX)
35a07c
-                        .getCanonicalFile();
35a07c
+                lockFileCandidate = new File(file.getParentFile(), file.getName() + LOCKFILE_SUFFIX).getCanonicalFile();
35a07c
             }
35a07c
-            if (lockMarkerFile.isDirectory()) {
35a07c
-                throw new RuntimeException("Lock marker file " + lockMarkerFile + " already exists and is a directory");
35a07c
+
35a07c
+            if (lockFileCandidate.isDirectory()) {
35a07c
+                throw new RuntimeException("Lock marker file " + lockFileCandidate + " already exists and is a directory");
35a07c
             }
35a07c
-            File parentDir = lockMarkerFile.getParentFile();
35a07c
+            File parentDir = lockFileCandidate.getParentFile();
35a07c
             if (!parentDir.isDirectory() && !parentDir.mkdirs()) {
35a07c
                 throw new RuntimeException("Could not create parent directory " + parentDir + " of lock marker file");
35a07c
             }
35a07c
+
35a07c
+            String baseDir = System.getProperty("user.dir");
35a07c
+            String reactorCache = baseDir + "/.m2/";
35a07c
+            // In Fedora we can only assume reactor cache is safe for read/write.
35a07c
+            if (!lockFileCandidate.getAbsolutePath().startsWith(reactorCache)) {
35a07c
+                String lockFileDir = reactorCache + LOCKFILE_SUFFIX;
35a07c
+                // If the file is located within baseDir, no need to repeat
35a07c
+                String lockFileName = file.getAbsolutePath().replace(baseDir, "").replace("/", "-").replaceFirst("-", "/") + LOCKFILE_SUFFIX;
35a07c
+                lockFileCandidate = new File(lockFileDir, lockFileName);
35a07c
+            }
35a07c
+
35a07c
+            this.lockMarkerFile = lockFileCandidate;
35a07c
             this.lockFileLocation = anyLocation.createLocation(null, null, false);
35a07c
             this.lockFileLocation.set(lockMarkerFile.toURL(), false, lockMarkerFile.getAbsolutePath());
35a07c
+
35a07c
         } catch (MalformedURLException e) {
35a07c
             throw new RuntimeException(e);
35a07c
         } catch (IOException e) {
35a07c
diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/maven/TychoMavenLifecycleParticipant.java b/tycho-core/src/main/java/org/eclipse/tycho/core/maven/TychoMavenLifecycleParticipant.java
35a07c
index f733774..1bd97e6 100644
35a07c
--- a/tycho-core/src/main/java/org/eclipse/tycho/core/maven/TychoMavenLifecycleParticipant.java
35a07c
+++ b/tycho-core/src/main/java/org/eclipse/tycho/core/maven/TychoMavenLifecycleParticipant.java
35a07c
@@ -30,6 +30,7 @@ import org.apache.maven.project.MavenProject;
35a07c
 import org.codehaus.plexus.PlexusContainer;
35a07c
 import org.codehaus.plexus.component.annotations.Component;
35a07c
 import org.codehaus.plexus.component.annotations.Requirement;
35a07c
+import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
35a07c
 import org.codehaus.plexus.logging.Logger;
35a07c
 import org.eclipse.tycho.ReactorProject;
35a07c
 import org.eclipse.tycho.core.osgitools.BundleReader;
35a07c
@@ -86,6 +87,18 @@ public class TychoMavenLifecycleParticipant extends AbstractMavenLifecyclePartic
35a07c
 
35a07c
             configureComponents(session);
35a07c
 
35a07c
+            try {
35a07c
+                if (plexus.lookup("org.fedoraproject.xmvn.resolver.Resolver") != null) {
35a07c
+                    if (session.isOffline()) {
35a07c
+                        System.setProperty("TYCHO_MVN_RPMBUILD", "");
35a07c
+                    } else {
35a07c
+                        System.setProperty("TYCHO_MVN_LOCAL", "");
35a07c
+                    }
35a07c
+                }
35a07c
+            } catch (ComponentLookupException e) {
35a07c
+                // No XMvn (Upstream Maven in use)
35a07c
+            }
35a07c
+
35a07c
             for (MavenProject project : projects) {
35a07c
                 resolver.setupProject(session, project, DefaultReactorProject.adapt(project));
35a07c
             }
35a07c
diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/AbstractTychoProject.java b/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/AbstractTychoProject.java
35a07c
index 94b02f1..f833854 100644
35a07c
--- a/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/AbstractTychoProject.java
35a07c
+++ b/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/AbstractTychoProject.java
35a07c
@@ -19,6 +19,9 @@ import org.eclipse.tycho.artifacts.DependencyArtifacts;
35a07c
 import org.eclipse.tycho.core.TargetPlatformConfiguration;
35a07c
 import org.eclipse.tycho.core.TychoConstants;
35a07c
 import org.eclipse.tycho.core.TychoProject;
35a07c
+import org.eclipse.tycho.core.ee.ExecutionEnvironmentUtils;
35a07c
+import org.eclipse.tycho.core.ee.UnknownEnvironmentException;
35a07c
+import org.eclipse.tycho.core.ee.shared.ExecutionEnvironment;
35a07c
 import org.eclipse.tycho.core.ee.shared.ExecutionEnvironmentConfiguration;
35a07c
 import org.eclipse.tycho.core.osgitools.targetplatform.LocalDependencyResolver;
35a07c
 import org.eclipse.tycho.core.osgitools.targetplatform.MultiEnvironmentDependencyArtifacts;
35a07c
@@ -94,15 +97,35 @@ public abstract class AbstractTychoProject extends AbstractLogEnabled implements
35a07c
 
35a07c
         String configuredForcedProfile = tpConfiguration.getExecutionEnvironment();
35a07c
         if (configuredForcedProfile != null) {
35a07c
+            configuredForcedProfile = overrideToAtLeastJavaSE16(configuredForcedProfile);
35a07c
             sink.overrideProfileConfiguration(configuredForcedProfile,
35a07c
                     "target-platform-configuration <executionEnvironment>");
35a07c
         }
35a07c
 
35a07c
         String configuredDefaultProfile = tpConfiguration.getExecutionEnvironmentDefault();
35a07c
         if (configuredDefaultProfile != null) {
35a07c
+            configuredDefaultProfile = overrideToAtLeastJavaSE16(configuredDefaultProfile);
35a07c
             sink.setProfileConfiguration(configuredDefaultProfile,
35a07c
                     "target-platform-configuration <executionEnvironmentDefault>");
35a07c
         }
35a07c
     }
35a07c
 
35a07c
+    public String overrideToAtLeastJavaSE16 (String profile) {
35a07c
+        try {
35a07c
+            ExecutionEnvironment ee = ExecutionEnvironmentUtils.getExecutionEnvironment(profile);
35a07c
+
35a07c
+            if (System.getProperty("TYCHO_MVN_LOCAL") != null || System.getProperty("TYCHO_MVN_RPMBUILD") != null) {
35a07c
+                // EE must be at least JavaSE-1.6
35a07c
+                final ExecutionEnvironment javaSE16 = ExecutionEnvironmentUtils.getExecutionEnvironment("JavaSE-1.6");
35a07c
+                if (! ee.isCompatibleCompilerTargetLevel(javaSE16.getCompilerTargetLevelDefault())) {
35a07c
+                    ee = javaSE16;
35a07c
+                }
35a07c
+            }
35a07c
+
35a07c
+            return ee.getProfileName();
35a07c
+        } catch (UnknownEnvironmentException e) {
35a07c
+            // can't happen, ee is validated during configuration parsing
35a07c
+            return null;
35a07c
+        }
35a07c
+    }
35a07c
 }
35a07c
diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/OsgiBundleProject.java b/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/OsgiBundleProject.java
35a07c
index 13ed51d..bd21204 100644
35a07c
--- a/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/OsgiBundleProject.java
35a07c
+++ b/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/OsgiBundleProject.java
35a07c
@@ -504,6 +504,7 @@ public class OsgiBundleProject extends AbstractTychoProject implements BundlePro
35a07c
         String pdeProfile = getEclipsePluginProject(DefaultReactorProject.adapt(project)).getBuildProperties()
35a07c
                 .getJreCompilationProfile();
35a07c
         if (pdeProfile != null) {
35a07c
+            pdeProfile = overrideToAtLeastJavaSE16(pdeProfile);
35a07c
             sink.setProfileConfiguration(pdeProfile.trim(), "build.properties");
35a07c
 
35a07c
         } else {
35a07c
@@ -514,13 +515,13 @@ public class OsgiBundleProject extends AbstractTychoProject implements BundlePro
35a07c
 
35a07c
                 switch (tpConfiguration.getBREEHeaderSelectionPolicy()) {
35a07c
                 case first:
35a07c
-                    sink.setProfileConfiguration(manifestBREEs[0].getProfileName(),
35a07c
+                    sink.setProfileConfiguration(overrideToAtLeastJavaSE16(manifestBREEs[0].getProfileName()),
35a07c
                             "Bundle-RequiredExecutionEnvironment (first entry)");
35a07c
                     break;
35a07c
 
35a07c
                 case minimal:
35a07c
                     ExecutionEnvironment manifestMinimalEE = Collections.min(Arrays.asList(manifestBREEs));
35a07c
-                    sink.setProfileConfiguration(manifestMinimalEE.getProfileName(),
35a07c
+                    sink.setProfileConfiguration(overrideToAtLeastJavaSE16(manifestMinimalEE.getProfileName()),
35a07c
                             "Bundle-RequiredExecutionEnvironment (minimal entry)");
35a07c
                 }
35a07c
 
35a07c
diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/DefaultTargetPlatformConfigurationReader.java b/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/DefaultTargetPlatformConfigurationReader.java
35a07c
index ed413e1..0b89bae 100644
35a07c
--- a/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/DefaultTargetPlatformConfigurationReader.java
35a07c
+++ b/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/DefaultTargetPlatformConfigurationReader.java
35a07c
@@ -68,7 +68,11 @@ public class DefaultTargetPlatformConfigurationReader {
35a07c
                             + configuration.toString());
35a07c
                 }
35a07c
 
35a07c
-                addTargetEnvironments(result, project, configuration);
35a07c
+                // Use the defined environments only in local mode with tycho.local.keepTarget
35a07c
+                if ((System.getProperty("TYCHO_MVN_LOCAL") == null && System.getProperty("TYCHO_MVN_RPMBUILD") == null)
35a07c
+                        || System.getProperty("tycho.local.keepTarget") != null) {
35a07c
+                    addTargetEnvironments(result, project, configuration);
35a07c
+                }
35a07c
 
35a07c
                 setTargetPlatformResolver(result, configuration);
35a07c
 
35a07c
diff --git a/tycho-core/src/main/java/org/eclipse/tycho/osgi/runtime/TychoOsgiRuntimeLocator.java b/tycho-core/src/main/java/org/eclipse/tycho/osgi/runtime/TychoOsgiRuntimeLocator.java
35a07c
index 35f1b6b..b64653e 100644
35a07c
--- a/tycho-core/src/main/java/org/eclipse/tycho/osgi/runtime/TychoOsgiRuntimeLocator.java
35a07c
+++ b/tycho-core/src/main/java/org/eclipse/tycho/osgi/runtime/TychoOsgiRuntimeLocator.java
35a07c
@@ -12,6 +12,8 @@ package org.eclipse.tycho.osgi.runtime;
35a07c
 
35a07c
 import java.io.File;
35a07c
 import java.io.IOException;
35a07c
+import java.nio.file.Files;
35a07c
+import java.nio.file.StandardCopyOption;
35a07c
 import java.util.ArrayList;
35a07c
 import java.util.List;
35a07c
 import java.util.Map;
35a07c
@@ -163,36 +165,49 @@ public class TychoOsgiRuntimeLocator implements EquinoxRuntimeLocator {
35a07c
             File artifactFile = new File(session.getLocalRepository().getBasedir(), session.getLocalRepository()
35a07c
                     .pathOf(artifact));
35a07c
             File eclipseDir = new File(artifactFile.getParentFile(), "eclipse");
35a07c
+            File eclipseSaveDir = new File(artifactFile.getParentFile(), "eclipse-save");
35a07c
+            File fedoraDir = new File(artifactFile.getParentFile(), "fedora-eclipse");
35a07c
 
35a07c
             FileLocker locker = fileLockService.getFileLocker(artifactFile);
35a07c
             locker.lock();
35a07c
             try {
35a07c
-                if (!eclipseDir.exists() || artifact.isSnapshot()) {
35a07c
+                if (!fedoraDir.exists() || artifact.isSnapshot()) {
35a07c
                     logger.debug("Extracting Tycho's OSGi runtime");
35a07c
 
35a07c
-                    if (artifact.getFile().lastModified() > eclipseDir.lastModified()) {
35a07c
+                    if (artifact.getFile().lastModified() > fedoraDir.lastModified()) {
35a07c
                         logger.debug("Unpacking Tycho's OSGi runtime to " + eclipseDir);
35a07c
                         try {
35a07c
-                            FileUtils.deleteDirectory(eclipseDir);
35a07c
+                            FileUtils.deleteDirectory(fedoraDir);
35a07c
+                            if (eclipseDir.exists()) {
35a07c
+                                FileUtils.rename(eclipseDir, eclipseSaveDir);
35a07c
+                            }
35a07c
                         } catch (IOException e) {
35a07c
-                            logger.warn("Failed to delete Tycho's OSGi runtime " + eclipseDir + ": " + e.getMessage());
35a07c
+                            logger.warn("Failed to delete Tycho's OSGi runtime " + fedoraDir + ": " + e.getMessage());
35a07c
                         }
35a07c
                         unArchiver.setSourceFile(artifact.getFile());
35a07c
                         unArchiver.setDestDirectory(eclipseDir.getParentFile());
35a07c
                         try {
35a07c
                             unArchiver.extract();
35a07c
+                            logger.debug("Moving Tycho's OSGi runtime to " + fedoraDir);
35a07c
+                            FileUtils.rename(eclipseDir, fedoraDir);
35a07c
+                            if (eclipseSaveDir.exists()) {
35a07c
+                                FileUtils.rename(eclipseSaveDir, eclipseDir);
35a07c
+                            }
35a07c
                         } catch (ArchiverException e) {
35a07c
                             throw new MavenExecutionException("Failed to unpack Tycho's OSGi runtime: "
35a07c
                                     + e.getMessage(), e);
35a07c
+                        } catch (IOException e) {
35a07c
+                            throw new MavenExecutionException("Failed to move Tycho's OSGi runtime: " + e.getMessage(),
35a07c
+                                    e);
35a07c
                         }
35a07c
 
35a07c
-                        eclipseDir.setLastModified(artifact.getFile().lastModified());
35a07c
+                        fedoraDir.setLastModified(artifact.getFile().lastModified());
35a07c
                     }
35a07c
                 }
35a07c
             } finally {
35a07c
                 locker.release();
35a07c
             }
35a07c
-            description.addInstallation(eclipseDir);
35a07c
+            description.addInstallation(fedoraDir);
35a07c
         } else {
35a07c
             description.addBundle(artifact.getFile());
35a07c
         }
35a07c
diff --git a/tycho-p2/tycho-p2-facade/pom.xml b/tycho-p2/tycho-p2-facade/pom.xml
35a07c
index 9c59b14..54cc384 100644
35a07c
--- a/tycho-p2/tycho-p2-facade/pom.xml
35a07c
+++ b/tycho-p2/tycho-p2-facade/pom.xml
35a07c
@@ -57,6 +57,11 @@
35a07c
 			<artifactId>junit</artifactId>
35a07c
 			<scope>test</scope>
35a07c
 		</dependency>
35a07c
+		<dependency>
35a07c
+			<groupId>org.fedoraproject.p2</groupId>
35a07c
+			<artifactId>org.fedoraproject.p2</artifactId>
35a07c
+			<version>0.0.1-SNAPSHOT</version>
35a07c
+		</dependency>
35a07c
 	</dependencies>
35a07c
 
35a07c
 	<build>
35a07c
diff --git a/tycho-p2/tycho-p2-facade/src/main/java/org/eclipse/tycho/p2/resolver/P2DependencyResolver.java b/tycho-p2/tycho-p2-facade/src/main/java/org/eclipse/tycho/p2/resolver/P2DependencyResolver.java
35a07c
index d5be20c..8405058 100644
35a07c
--- a/tycho-p2/tycho-p2-facade/src/main/java/org/eclipse/tycho/p2/resolver/P2DependencyResolver.java
35a07c
+++ b/tycho-p2/tycho-p2-facade/src/main/java/org/eclipse/tycho/p2/resolver/P2DependencyResolver.java
35a07c
@@ -89,6 +89,7 @@ import org.eclipse.tycho.p2.resolver.facade.P2ResolverFactory;
35a07c
 import org.eclipse.tycho.p2.target.facade.PomDependencyCollector;
35a07c
 import org.eclipse.tycho.p2.target.facade.TargetPlatformConfigurationStub;
35a07c
 import org.eclipse.tycho.repository.registry.facade.ReactorRepositoryManagerFacade;
35a07c
+import org.fedoraproject.p2.EclipseSystemLayout;
35a07c
 
35a07c
 @Component(role = DependencyResolver.class, hint = P2DependencyResolver.ROLE_HINT, instantiationStrategy = "per-lookup")
35a07c
 public class P2DependencyResolver extends AbstractLogEnabled implements DependencyResolver, Initializable {
35a07c
@@ -209,6 +210,13 @@ public class P2DependencyResolver extends AbstractLogEnabled implements Dependen
35a07c
             pomDependencies.setProjectLocation(project.getBasedir());
35a07c
         }
35a07c
 
35a07c
+        // Add Fedora Local P2 Repository when running in local mode
35a07c
+        if (System.getProperty("TYCHO_MVN_LOCAL") != null || System.getProperty("TYCHO_MVN_RPMBUILD") != null) {
35a07c
+            for (URI uri : EclipseSystemLayout.getRepositories()) {
35a07c
+                tpConfiguration.addP2Repository(new MavenRepositoryLocation(uri.getPath(), uri));
35a07c
+            }
35a07c
+        }
35a07c
+
35a07c
         for (ArtifactRepository repository : project.getRemoteArtifactRepositories()) {
35a07c
             addEntireP2RepositoryToTargetPlatform(repository, tpConfiguration);
35a07c
         }
35a07c
-- 
35a07c
2.20.1
35a07c