From ffd411414d5993804a39334f8a39b0f48eb63970 Mon Sep 17 00:00:00 2001 From: Sebastian Ratz Date: Wed, 6 Mar 2024 16:04:32 +0000 Subject: [PATCH] tycho-p2-director:director: Fix handling of destination on macOS * In DirectorMojo, the adjustment of 'destination' must consider the actual target environment (p2os/p2ws/p2arch parameters) that is to be installed and only fall back to the running environment if no explicit target environment is given. * Document in the tycho-p2-director:director JavaDoc / mojo parameter description that this intentionally deviates from the behavior of the stand-alone director application invocation: eclipse -application org.eclipse.equinox.p2.director -destination ... * In DirectorMojo, add a consistency check that p2os/p2ws/p2arch must all be specified mutually. * The helper methods in DirectorRuntime are extended, to properly handle all three possible scenarios: 1) /path/without/app/bundle/layout --> /path/without/app/bundle/layout/Eclipse.app/Contents/Eclipse 2) /path/to/real.app/Contents/Eclipse --> /path/to/real.app/Contents/Eclipse 3) /path/to/real.app --> /path/to/real.app/Contents/Eclipse This allows us to remove redundant code in ProvisionedInstallationBuilder. * This also removes the option again which was introduced in #3091 (606a087f9d2e84af82b9c3679c553cd941aa8089). This is not used in production and was not having the desired effect. This simplifies the handling in AbstractEclipseTestMojo / ProvisionedInstallationBuilder even more. Fixes #3548. --- .../director/shared/DirectorRuntime.java | 36 ++- .../director-goal-standalone/pom.xml | 224 ++++++++++++++++-- .../tycho/test/P2DirectorPluginTest.java | 86 ++++++- .../plugins/p2/director/DirectorMojo.java | 61 ++++- .../plugins/p2/director/DirectorMojoTest.java | 185 +++++++++++++++ .../surefire/AbstractEclipseTestMojo.java | 32 +-- .../ProvisionedInstallationBuilder.java | 34 +-- .../ProvisionedInstallationBuilderTest.java | 32 ++- 8 files changed, 617 insertions(+), 73 deletions(-) create mode 100644 tycho-p2-director-plugin/src/test/java/org/eclipse/tycho/plugins/p2/director/DirectorMojoTest.java diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/director/shared/DirectorRuntime.java b/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/director/shared/DirectorRuntime.java index bf3eb04b1e..40d88c6895 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/director/shared/DirectorRuntime.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/director/shared/DirectorRuntime.java @@ -77,14 +77,35 @@ public interface Command { /** * Computes the destination of a director install based on a target environment - * + *

+ * Currently, this implements special handling for macOS and behaves as follows: + *

    + *
  • If baseLocation already conforms to the full app bundle layout + * (/path/to/Foo.app/Contents/Eclipse), baseLocation is returned + * as-is. + *
  • If baseLocation points to the root of an app bundle + * (/path/to/Foo.app), Contents/Eclipse is appended and the path + * /path/to/Foo.app/Contents/Eclipse is returned. + *
  • Otherwise, i.e. if no app bundle path is given (/path/to/work), a valid app + * bundle path is appended, and the path /path/to/work/Eclipse.app/Contents/Eclipse + * is returned. + *
+ * * @param baseLocation + * the base location * @param env - * @return + * the target environment + * @return the adjusted location to conform to layouts required by the target environment */ public static File getDestination(File baseLocation, TargetEnvironment env) { - if (PlatformPropertiesUtils.OS_MACOSX.equals(env.getOs()) && !hasRequiredMacLayout(baseLocation)) { - return new File(baseLocation, "Eclipse.app/Contents/Eclipse/"); + if (PlatformPropertiesUtils.OS_MACOSX.equals(env.getOs())) { + if (hasRequiredMacLayout(baseLocation)) { + return baseLocation; + } else if (isMacOsAppBundleRoot(baseLocation)) { + return new File(baseLocation, "Contents/Eclipse/"); + } else { + return new File(baseLocation, "Eclipse.app/Contents/Eclipse/"); + } } return baseLocation; } @@ -100,9 +121,14 @@ private static boolean hasRequiredMacLayout(File folder) { File folder2 = folder.getParentFile(); if (folder2 != null && "Contents".equals(folder2.getName())) { File parent = folder2.getParentFile(); - return parent != null && parent.getName().endsWith(".app"); + return parent != null && isMacOsAppBundleRoot(parent); } } return false; } + + private static boolean isMacOsAppBundleRoot(File folder) { + return folder.getName().endsWith(".app"); + } + } diff --git a/tycho-its/projects/tycho-p2-director-plugin/director-goal-standalone/pom.xml b/tycho-its/projects/tycho-p2-director-plugin/director-goal-standalone/pom.xml index 9577921acf..ad63103197 100644 --- a/tycho-its/projects/tycho-p2-director-plugin/director-goal-standalone/pom.xml +++ b/tycho-its/projects/tycho-p2-director-plugin/director-goal-standalone/pom.xml @@ -7,28 +7,206 @@ 0.1.0-SNAPSHOT pom - - - - org.eclipse.tycho - tycho-p2-director-plugin - ${tycho-version} - - - run-director - - director - - package - - ${target-platform} - org.eclipse.osgi - target/dummy - - - - - - + + + director-windows + + + + org.eclipse.tycho + tycho-p2-director-plugin + ${tycho-version} + + + run-director-windows + + director + + package + + ${target-platform} + org.eclipse.osgi + target/productwindows + win32 + win32 + x86_64 + + + + + + + + + director-linux + + + + org.eclipse.tycho + tycho-p2-director-plugin + ${tycho-version} + + + run-director-linux + + director + + package + + ${target-platform} + org.eclipse.osgi + target/productlinux + linux + gtk + x86_64 + + + + + + + + + director-macos-destination-with-app-suffix + + + + org.eclipse.tycho + tycho-p2-director-plugin + ${tycho-version} + + + run-director-macos-destination-with-app-suffix + + director + + package + + ${target-platform} + org.eclipse.osgi + target/productmacos.app + macosx + cocoa + x86_64 + + + + + + + + + director-macos-destination-without-app-suffix + + + + org.eclipse.tycho + tycho-p2-director-plugin + ${tycho-version} + + + run-director-macos-destinationout-with-app-suffix + + director + + package + + ${target-platform} + org.eclipse.osgi + target/productmacos + macosx + cocoa + x86_64 + + + + + + + + + director-macos-destination-with-full-bundle-path + + + + org.eclipse.tycho + tycho-p2-director-plugin + ${tycho-version} + + + run-director-macos-destination-with-full-bundle-path + + director + + package + + ${target-platform} + org.eclipse.osgi + target/productmacos.app/Contents/Eclipse + macosx + cocoa + x86_64 + + + + + + + + + director-running-environment + + + + org.eclipse.tycho + tycho-p2-director-plugin + ${tycho-version} + + + run-director-running-environment + + director + + package + + ${target-platform} + org.eclipse.osgi + target/product + + + + + + + + + director-iconsistent-p2-arguments + + + + org.eclipse.tycho + tycho-p2-director-plugin + ${tycho-version} + + + run-director-iconsistent-p2-arguments + + director + + package + + ${target-platform} + org.eclipse.osgi + target/product + win32 + + + + + + + + + \ No newline at end of file diff --git a/tycho-its/src/test/java/org/eclipse/tycho/test/P2DirectorPluginTest.java b/tycho-its/src/test/java/org/eclipse/tycho/test/P2DirectorPluginTest.java index 9844723f5c..f288b47e02 100644 --- a/tycho-its/src/test/java/org/eclipse/tycho/test/P2DirectorPluginTest.java +++ b/tycho-its/src/test/java/org/eclipse/tycho/test/P2DirectorPluginTest.java @@ -1,15 +1,99 @@ package org.eclipse.tycho.test; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.nio.file.Files; +import java.nio.file.Path; + +import org.apache.maven.it.VerificationException; import org.apache.maven.it.Verifier; +import org.eclipse.tycho.TargetEnvironment; import org.junit.Test; public class P2DirectorPluginTest extends AbstractTychoIntegrationTest { @Test - public void testDirectorStandalone() throws Exception { + public void testDirectorStandaloneWindows() throws Exception { + Verifier verifier = getVerifier("tycho-p2-director-plugin/director-goal-standalone", true, true); + verifier.addCliOption("-Pdirector-windows"); + verifier.executeGoal("package"); + verifier.verifyErrorFreeLog(); + + assertTrue(Files.isDirectory(Path.of(verifier.getBasedir(), "target", "productwindows", "plugins"))); + } + + @Test + public void testDirectorStandaloneLinux() throws Exception { + Verifier verifier = getVerifier("tycho-p2-director-plugin/director-goal-standalone", true, true); + verifier.addCliOption("-Pdirector-linux"); + verifier.executeGoal("package"); + verifier.verifyErrorFreeLog(); + + assertTrue(Files.isDirectory(Path.of(verifier.getBasedir(), "target", "productlinux", "plugins"))); + } + + @Test + public void testDirectorStandaloneMacOsDestinationWithAppSuffix() throws Exception { + Verifier verifier = getVerifier("tycho-p2-director-plugin/director-goal-standalone", true, true); + verifier.addCliOption("-Pdirector-macos-destination-with-app-suffix"); + verifier.executeGoal("package"); + verifier.verifyErrorFreeLog(); + + assertTrue(Files.isDirectory( + Path.of(verifier.getBasedir(), "target", "productmacos.app", "Contents", "Eclipse", "plugins"))); + } + + @Test + public void testDirectorStandaloneMacOsDestinationWithoutAppSuffix() throws Exception { Verifier verifier = getVerifier("tycho-p2-director-plugin/director-goal-standalone", true, true); + verifier.addCliOption("-Pdirector-macos-destination-without-app-suffix"); verifier.executeGoal("package"); verifier.verifyErrorFreeLog(); + + assertTrue(Files.isDirectory(Path.of(verifier.getBasedir(), "target", "productmacos", "Eclipse.app", "Contents", + "Eclipse", "plugins"))); + } + + @Test + public void testDirectorStandaloneMacOsDestinationWithFullBundlePath() throws Exception { + Verifier verifier = getVerifier("tycho-p2-director-plugin/director-goal-standalone", true, true); + verifier.addCliOption("-Pdirector-macos-destination-with-full-bundle-path"); + verifier.executeGoal("package"); + verifier.verifyErrorFreeLog(); + + assertTrue(Files.isDirectory( + Path.of(verifier.getBasedir(), "target", "productmacos.app", "Contents", "Eclipse", "plugins"))); + } + + @Test + public void testDirectorStandaloneUsingRunningEnvironment() throws Exception { + Verifier verifier = getVerifier("tycho-p2-director-plugin/director-goal-standalone", true, true); + verifier.addCliOption("-Pdirector-running-environment"); + verifier.executeGoal("package"); + verifier.verifyErrorFreeLog(); + + if ("macosx".equals(TargetEnvironment.getRunningEnvironment().getOs())) { + assertTrue(Files.isDirectory(Path.of(verifier.getBasedir(), "target", "product", "Eclipse.app", "Contents", + "Eclipse", "plugins"))); + } else { + assertTrue(Files.isDirectory(Path.of(verifier.getBasedir(), "target", "product", "plugins"))); + } + } + + @Test + public void testDirectorStandaloneInconsistentP2Options() throws Exception { + Verifier verifier = getVerifier("tycho-p2-director-plugin/director-goal-standalone", true, true); + verifier.addCliOption("-Pdirector-iconsistent-p2-arguments"); + try { + verifier.executeGoal("package"); + fail(VerificationException.class.getName() + " expected"); + } catch (VerificationException e) { + } + verifier.verifyTextInLog( + "p2os / p2ws / p2arch must be mutually specified, p2os=win32 given, p2ws missing, p2arch missing"); + assertFalse(Files.isDirectory(Path.of(verifier.getBasedir(), "target", "product"))); } } diff --git a/tycho-p2-director-plugin/src/main/java/org/eclipse/tycho/plugins/p2/director/DirectorMojo.java b/tycho-p2-director-plugin/src/main/java/org/eclipse/tycho/plugins/p2/director/DirectorMojo.java index 617939ccbf..466c684c45 100644 --- a/tycho-p2-director-plugin/src/main/java/org/eclipse/tycho/plugins/p2/director/DirectorMojo.java +++ b/tycho-p2-director-plugin/src/main/java/org/eclipse/tycho/plugins/p2/director/DirectorMojo.java @@ -101,6 +101,23 @@ public class DirectorMojo extends AbstractMojo { /** * The folder in which the targeted product is located. + *

+ * Note: This applies special handling for macOS ({@link #p2os} macosx) and behaves + * as follows: + *

    + *
  • If destination already conforms to the full app bundle layout + * (/path/to/Foo.app/Contents/Eclipse), destination is used as-is. + *
  • If destination points to the root of an app bundle + * (/path/to/Foo.app), Contents/Eclipse is appended and the path and + * /path/to/Foo.app/Contents/Eclipse is used. + *
  • Otherwise, i.e. if no app bundle path is given (/path/to/work), a valid app + * bundle path is appended, and the path /path/to/work/Eclipse.app/Contents/Eclipse + * is used. + *
+ * This intentionally deviates from the stand-alone behavior of + * eclipse -application org.eclipse.equinox.p2.director in order to simplify + * cross-mojo workflows within Tycho (e.g. the same logic is applied by + * tycho-surefire-plugin:integration-test. */ @Parameter(property = "destination", required = true) private File destination; @@ -258,18 +275,32 @@ public class DirectorMojo extends AbstractMojo { /** * The OS to use when the profile is created. + *

+ * If this is specified, {@link #p2ws} and {@link #p2arch} must also be specified for + * consistency. + *

+ * If none of them are specified, the values are derived from the running environment. */ @Parameter(property = "p2.os") private String p2os; /** * The windowing system to use when the profile is created. + *

+ * If this is specified, {@link #p2os} and {@link #p2arch} must also be specified for + * consistency. + *

+ * If none of them are specified, the values are derived from the running environment. */ @Parameter(property = "p2.ws") private String p2ws; /** * The architecture to use when the profile is created. + *

+ * If this is specified, {@link #p2os} and {@link #p2ws} must also be specified for consistency. + *

+ * If none of them are specified, the values are derived from the running environment. */ @Parameter(property = "p2.arch") private String p2arch; @@ -359,9 +390,14 @@ public class DirectorMojo extends AbstractMojo { @Override public void execute() throws MojoExecutionException, MojoFailureException { + checkMutualP2Args(); + + TargetEnvironment targetEnv = this.p2os == null ? TargetEnvironment.getRunningEnvironment() + : new TargetEnvironment(p2os, p2ws, p2arch); + File adjustedDestination = DirectorRuntime.getDestination(this.destination, targetEnv); + CommandLineArguments args = new CommandLineArguments(); - args.addNonNull("-destination", - DirectorRuntime.getDestination(destination, TargetEnvironment.getRunningEnvironment())); + args.addNonNull("-destination", adjustedDestination); args.addNonNull("-metadatarepository", metadatarepositories); args.addNonNull("-artifactrepository", artifactrepositories); args.addNonNull("-repository", getRepositories()); @@ -400,6 +436,27 @@ public void execute() throws MojoExecutionException, MojoFailureException { args.addNonNull("-trustedAuthorities", trustedAuthorities); args.addNonNull("-trustedPGPKeys", trustedPGPKeys); args.addNonNull("-trustedCertificates", trustedCertificates); + + runDirector(args); + } + + private void checkMutualP2Args() throws MojoExecutionException { + Map mutualP2Options = new LinkedHashMap<>(); + mutualP2Options.put("p2os", p2os); + mutualP2Options.put("p2ws", p2ws); + mutualP2Options.put("p2arch", p2arch); + if (mutualP2Options.values().stream().anyMatch(Objects::nonNull)) { + if (mutualP2Options.values().stream().anyMatch(Objects::isNull)) { + String msg = "p2os / p2ws / p2arch must be mutually specified, " + // + mutualP2Options.entrySet().stream().map( + e -> e.getKey() + (e.getValue() == null ? " missing" : "=" + e.getValue() + " given")) + .collect(Collectors.joining(", ")); + throw new MojoExecutionException(msg); + } + } + } + + protected void runDirector(CommandLineArguments args) throws MojoFailureException { try { //FIXME forcefully init OSGi unless we have a fix for https://github.com/eclipse-equinox/p2/pull/439 agent.getService(IMetadataRepositoryManager.class); diff --git a/tycho-p2-director-plugin/src/test/java/org/eclipse/tycho/plugins/p2/director/DirectorMojoTest.java b/tycho-p2-director-plugin/src/test/java/org/eclipse/tycho/plugins/p2/director/DirectorMojoTest.java new file mode 100644 index 0000000000..a43101ccc0 --- /dev/null +++ b/tycho-p2-director-plugin/src/test/java/org/eclipse/tycho/plugins/p2/director/DirectorMojoTest.java @@ -0,0 +1,185 @@ +package org.eclipse.tycho.plugins.p2.director; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.File; +import java.lang.reflect.Field; +import java.util.List; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.codehaus.plexus.util.ReflectionUtils; +import org.eclipse.tycho.p2.CommandLineArguments; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class DirectorMojoTest { + + private DirectorMojo directorMojo; + + private CommandLineArguments recordedArgs; + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Before + public void setUp() throws Exception { + directorMojo = new DirectorMojo() { + @Override + protected void runDirector(CommandLineArguments args) throws MojoFailureException { + recordedArgs = args; + } + }; + } + + @Test + public void testMutualP2Options_NonePresent() throws Exception { + setParameter(directorMojo, "destination", tempFolder.newFolder("mandatory")); + directorMojo.execute(); + + assertTrue(recordedArgs.asList().stream().skip(2).toList().isEmpty()); + } + + @Test + public void testMutualP2Options_AllPresent() throws Exception { + setParameter(directorMojo, "destination", tempFolder.newFolder("mandatory")); + setParameter(directorMojo, "p2os", "win32"); + setParameter(directorMojo, "p2ws", "win32"); + setParameter(directorMojo, "p2arch", "x86_64"); + + directorMojo.execute(); + + assertEquals(List.of("-p2.os", "win32", "-p2.ws", "win32", "-p2.arch", "x86_64"), + recordedArgs.asList().stream().skip(2).toList()); + } + + @Test + public void testMutualP2Options_OsMissing() throws Exception { + setParameter(directorMojo, "destination", tempFolder.newFolder("mandatory")); + setParameter(directorMojo, "p2ws", "win32"); + setParameter(directorMojo, "p2arch", "x86_64"); + + try { + directorMojo.execute(); + fail(MojoExecutionException.class.getName() + " expected"); + } catch (MojoExecutionException e) { + assertEquals( + "p2os / p2ws / p2arch must be mutually specified, p2os missing, p2ws=win32 given, p2arch=x86_64 given", + e.getMessage()); + } + } + + @Test + public void testMutualP2Options_WsMissing() throws Exception { + setParameter(directorMojo, "destination", tempFolder.newFolder("mandatory")); + setParameter(directorMojo, "p2os", "win32"); + setParameter(directorMojo, "p2arch", "x86_64"); + + try { + directorMojo.execute(); + fail(MojoExecutionException.class.getName() + " expected"); + } catch (MojoExecutionException e) { + assertEquals( + "p2os / p2ws / p2arch must be mutually specified, p2os=win32 given, p2ws missing, p2arch=x86_64 given", + e.getMessage()); + } + } + + @Test + public void testMutualP2Options_ArchAndOsMissing() throws Exception { + setParameter(directorMojo, "destination", tempFolder.newFolder("mandatory")); + setParameter(directorMojo, "p2ws", "win32"); + + try { + directorMojo.execute(); + fail(MojoExecutionException.class.getName() + " expected"); + } catch (MojoExecutionException e) { + assertEquals( + "p2os / p2ws / p2arch must be mutually specified, p2os missing, p2ws=win32 given, p2arch missing", + e.getMessage()); + } + } + + @Test + public void testDestination_Windows() throws Exception { + setParameter(directorMojo, "destination", tempFolder.newFolder("work")); + setParameter(directorMojo, "p2os", "win32"); + setParameter(directorMojo, "p2ws", "win32"); + setParameter(directorMojo, "p2arch", "x86_64"); + + directorMojo.execute(); + + assertEquals(List.of("-destination", new File(tempFolder.getRoot(), "work").getAbsolutePath()), + recordedArgs.asList().stream().limit(2).toList()); + } + + @Test + public void testDestination_Linux() throws Exception { + setParameter(directorMojo, "destination", tempFolder.newFolder("work")); + setParameter(directorMojo, "p2os", "linux"); + setParameter(directorMojo, "p2ws", "gtk"); + setParameter(directorMojo, "p2arch", "x86_64"); + + directorMojo.execute(); + + assertEquals(List.of("-destination", new File(tempFolder.getRoot(), "work").getAbsolutePath()), + recordedArgs.asList().stream().limit(2).toList()); + } + + @Test + public void testDestination_MacOs_NoAppBundleGiven() throws Exception { + setParameter(directorMojo, "destination", tempFolder.newFolder("work")); + setParameter(directorMojo, "p2os", "macosx"); + setParameter(directorMojo, "p2ws", "cocoa"); + setParameter(directorMojo, "p2arch", "x86_64"); + + directorMojo.execute(); + + assertEquals( + List.of("-destination", + new File(tempFolder.getRoot(), "work/Eclipse.app/Contents/Eclipse").getAbsolutePath()), + recordedArgs.asList().stream().limit(2).toList()); + } + + @Test + public void testDestination_MacOs_AppBundleRootGiven() throws Exception { + setParameter(directorMojo, "destination", tempFolder.newFolder("work/Foo.app")); + setParameter(directorMojo, "p2os", "macosx"); + setParameter(directorMojo, "p2ws", "cocoa"); + setParameter(directorMojo, "p2arch", "x86_64"); + + directorMojo.execute(); + + assertEquals( + List.of("-destination", + new File(tempFolder.getRoot(), "work/Foo.app/Contents/Eclipse").getAbsolutePath()), + recordedArgs.asList().stream().limit(2).toList()); + } + + @Test + public void testDestination_MacOs_InstallAreaInsideAppBundleGiven() throws Exception { + setParameter(directorMojo, "destination", tempFolder.newFolder("work/Foo.app/Contents/Eclipse")); + setParameter(directorMojo, "p2os", "macosx"); + setParameter(directorMojo, "p2ws", "cocoa"); + setParameter(directorMojo, "p2arch", "x86_64"); + + directorMojo.execute(); + + assertEquals( + List.of("-destination", + new File(tempFolder.getRoot(), "work/Foo.app/Contents/Eclipse").getAbsolutePath()), + recordedArgs.asList().stream().limit(2).toList()); + } + + private static void setParameter(Object object, String variable, Object value) + throws IllegalArgumentException, IllegalAccessException { + Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses(variable, object.getClass()); + field.setAccessible(true); + field.set(object, value); + } + +} diff --git a/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/AbstractEclipseTestMojo.java b/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/AbstractEclipseTestMojo.java index e357d68ede..1ea7408f8b 100644 --- a/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/AbstractEclipseTestMojo.java +++ b/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/AbstractEclipseTestMojo.java @@ -307,12 +307,6 @@ public abstract class AbstractEclipseTestMojo extends AbstractTestMojo { @Parameter private BundleStartLevel defaultStartLevel; - /** - * If {@link #testRuntime} is p2Installed installs all configured environments - */ - @Parameter - private boolean installAllEnvironments; - /** * Flaky tests will re-run until they pass or the number of reruns has been exhausted. See * surefire documentation for details. @@ -442,7 +436,11 @@ public abstract class AbstractEclipseTestMojo extends AbstractTestMojo { * that require a fully p2-provisioned installation. To install a product IU, add it as extra * requirement to the test bundle (see example below). Note that this installation mode comes * with a certain performance overhead for executing the provisioning operations otherwise not - * required. + * required. Also note, that in this mode, in case the primary installation target environment + * is macOS, {@link #work} is post-processed to ensure a proper macOS layout. That is, + * Eclipse.app/Contents/Eclipse is automatically appended (or only + * Contents/Eclipse, if work already ends with + * .app). * * * Example configuration which will install product IU under test "example.product.id" using p2: @@ -693,23 +691,9 @@ private EquinoxInstallation createProvisionedInstallation() throws MojoExecution installationBuilder.setDestination(work); List list = getTestTargetEnvironments(); TargetEnvironment testEnvironment = list.get(0); - if (installAllEnvironments) { - TargetPlatformConfiguration configuration = projectManager.getTargetPlatformConfiguration(project); - List targetEnvironments = configuration.getEnvironments(); - EquinoxInstallation installation = null; - for (TargetEnvironment targetEnvironment : targetEnvironments) { - getLog().info("Provisioning with environment " + targetEnvironment + "..."); - installationBuilder.setProfileName(targetEnvironment.toString()); - EquinoxInstallation current = installationBuilder.install(targetEnvironment); - if (targetEnvironment == testEnvironment) { - installation = current; - } - } - return installation; - } else { - getLog().info("Provisioning with environment " + testEnvironment + "..."); - return installationBuilder.install(testEnvironment); - } + installationBuilder.setTargetEnvironment(testEnvironment); + getLog().info("Provisioning with environment " + testEnvironment + "..."); + return installationBuilder.install(); } catch (MojoExecutionException e) { throw e; } catch (MojoFailureException e) { diff --git a/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/provisioning/ProvisionedInstallationBuilder.java b/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/provisioning/ProvisionedInstallationBuilder.java index 7be66e98b8..3e453f09dc 100644 --- a/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/provisioning/ProvisionedInstallationBuilder.java +++ b/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/provisioning/ProvisionedInstallationBuilder.java @@ -36,7 +36,8 @@ public class ProvisionedInstallationBuilder { private List artifactRepos = new ArrayList<>(); private List ius = new ArrayList<>(); private File workingDir; - private File effectiveDestination; + private File destination; + private TargetEnvironment targetEnvironment; private String profileName; private boolean installFeatures = true; @@ -74,17 +75,15 @@ public void addIUsToBeInstalled(List ius) { } public void setDestination(File destination) { - // For new MacOS layouts turn a given 'RCP.app' dir into 'RCP.app/Contents/Eclipse' - // This is what is expected from Eclipse runtime as install root anyways. - if (destination.getName().endsWith(".app")) { - this.effectiveDestination = new File(destination, "Contents/Eclipse"); - } else { - this.effectiveDestination = destination; - } + this.destination = destination; + } + + public void setTargetEnvironment(TargetEnvironment targetEnvironment) { + this.targetEnvironment = targetEnvironment; } public File getEffectiveDestination() { - return effectiveDestination; + return DirectorRuntime.getDestination(destination, targetEnvironment); } public void setProfileName(String name) { @@ -95,11 +94,11 @@ public void setInstallFeatures(boolean installFeatures) { this.installFeatures = installFeatures; } - public EquinoxInstallation install(TargetEnvironment main) throws Exception { + public EquinoxInstallation install() throws Exception { validate(); publishPlainBundleJars(); - executeDirector(main); - return new ProvisionedEquinoxInstallation(DirectorRuntime.getDestination(effectiveDestination, main)); + executeDirector(); + return new ProvisionedEquinoxInstallation(getEffectiveDestination()); } private void publishPlainBundleJars() throws Exception { @@ -119,17 +118,18 @@ private void publishPlainBundleJars() throws Exception { artifactRepos.add(bundlesRepoURI); } - private void executeDirector(TargetEnvironment env) throws MojoFailureException { - DirectorRuntime.Command command = directorRuntime.newInstallCommand(String.valueOf(env)); + private void executeDirector() throws MojoFailureException { + DirectorRuntime.Command command = directorRuntime.newInstallCommand(String.valueOf(targetEnvironment)); command.addMetadataSources(metadataRepos); command.addArtifactSources(artifactRepos); for (String iu : ius) { command.addUnitToInstall(iu); } - command.setDestination(DirectorRuntime.getDestination(effectiveDestination, env)); + File effectiveDestination = getEffectiveDestination(); + command.setDestination(effectiveDestination); command.setProfileName(profileName); command.setInstallFeatures(installFeatures); - command.setEnvironment(env); + command.setEnvironment(targetEnvironment); log.info("Installing IUs " + ius + " to " + effectiveDestination + " using " + command.getProfileProperties()); try { command.execute(); @@ -140,7 +140,7 @@ private void executeDirector(TargetEnvironment env) throws MojoFailureException private void validate() { assertNotNull(workingDir, "workingDir"); - assertNotNull(effectiveDestination, "destination"); + assertNotNull(destination, "destination"); assertNotEmpty(metadataRepos, "metadataRepos"); assertNotEmpty(artifactRepos, "artifactRepos"); assertNotEmpty(ius, "ius"); diff --git a/tycho-surefire/tycho-surefire-plugin/src/test/java/org/eclipse/tycho/surefire/provisioning/ProvisionedInstallationBuilderTest.java b/tycho-surefire/tycho-surefire-plugin/src/test/java/org/eclipse/tycho/surefire/provisioning/ProvisionedInstallationBuilderTest.java index 65d734ce59..e64745cf09 100644 --- a/tycho-surefire/tycho-surefire-plugin/src/test/java/org/eclipse/tycho/surefire/provisioning/ProvisionedInstallationBuilderTest.java +++ b/tycho-surefire/tycho-surefire-plugin/src/test/java/org/eclipse/tycho/surefire/provisioning/ProvisionedInstallationBuilderTest.java @@ -16,12 +16,19 @@ import java.io.File; +import org.eclipse.tycho.PlatformPropertiesUtils; +import org.eclipse.tycho.TargetEnvironment; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; public class ProvisionedInstallationBuilderTest { + private static final TargetEnvironment ENV_LINUX = new TargetEnvironment(PlatformPropertiesUtils.OS_LINUX, + PlatformPropertiesUtils.WS_GTK, PlatformPropertiesUtils.ARCH_X86_64); + private static final TargetEnvironment ENV_MACOS = new TargetEnvironment(PlatformPropertiesUtils.OS_MACOSX, + PlatformPropertiesUtils.WS_COCOA, PlatformPropertiesUtils.ARCH_X86_64); + @Rule public TemporaryFolder tempDir = new TemporaryFolder(); @@ -30,18 +37,41 @@ public void setDestination_LayoutNormal() throws Exception { ProvisionedInstallationBuilder builder = new ProvisionedInstallationBuilder(null, null); File work = tempDir.newFolder("work"); + builder.setTargetEnvironment(ENV_LINUX); builder.setDestination(work); assertEquals(work, builder.getEffectiveDestination()); } @Test - public void setDestination_LayoutMacOS() throws Exception { + public void setDestination_LayoutMacOS_NoAppBundleGiven() throws Exception { + ProvisionedInstallationBuilder builder = new ProvisionedInstallationBuilder(null, null); + + File work = tempDir.newFolder("work"); + builder.setTargetEnvironment(ENV_MACOS); + builder.setDestination(work); + File destinationExpected = new File(work, "Eclipse.app/Contents/Eclipse"); + assertEquals(destinationExpected, builder.getEffectiveDestination()); + } + + @Test + public void setDestination_LayoutMacOS_AppBundleRootGiven() throws Exception { ProvisionedInstallationBuilder builder = new ProvisionedInstallationBuilder(null, null); File work = tempDir.newFolder("work.app"); + builder.setTargetEnvironment(ENV_MACOS); builder.setDestination(work); File destinationExpected = new File(work, "Contents/Eclipse"); assertEquals(destinationExpected, builder.getEffectiveDestination()); } + @Test + public void setDestination_LayoutMacOS_InstallAreaInsideAppBundleGiven() throws Exception { + ProvisionedInstallationBuilder builder = new ProvisionedInstallationBuilder(null, null); + + File work = tempDir.newFolder("work.app/Contents/Eclipse"); + builder.setTargetEnvironment(ENV_MACOS); + builder.setDestination(work); + assertEquals(work, builder.getEffectiveDestination()); + } + }