/** * Copyright (c) 2015 Montages AG * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Inspired by org.eclipse.tycho.surefire.TestMojo */ package org.eclipse.gmf.tooling.codegen.maven; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Properties; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Dependency; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.apache.maven.toolchain.Toolchain; import org.apache.maven.toolchain.ToolchainManager; import org.eclipse.gmf.tooling.codegen.launcher.GMFLauncherConstants; import org.eclipse.sisu.equinox.launching.BundleStartLevel; import org.eclipse.sisu.equinox.launching.DefaultEquinoxInstallationDescription; import org.eclipse.sisu.equinox.launching.EquinoxInstallation; import org.eclipse.sisu.equinox.launching.EquinoxInstallationDescription; import org.eclipse.sisu.equinox.launching.EquinoxInstallationFactory; import org.eclipse.sisu.equinox.launching.EquinoxLauncher; import org.eclipse.sisu.equinox.launching.internal.EquinoxLaunchConfiguration; import org.eclipse.tycho.ArtifactDescriptor; import org.eclipse.tycho.ArtifactType; import org.eclipse.tycho.ReactorProject; import org.eclipse.tycho.artifacts.DependencyArtifacts; import org.eclipse.tycho.core.DependencyResolver; import org.eclipse.tycho.core.DependencyResolverConfiguration; import org.eclipse.tycho.core.TargetPlatformConfiguration; import org.eclipse.tycho.core.TychoConstants; import org.eclipse.tycho.core.maven.ToolchainProvider; import org.eclipse.tycho.core.maven.ToolchainProvider.JDKUsage; import org.eclipse.tycho.core.osgitools.DefaultReactorProject; import org.eclipse.tycho.core.resolver.DefaultDependencyResolverFactory; import org.eclipse.tycho.core.resolver.shared.OptionalResolutionAction; import org.eclipse.tycho.core.resolver.shared.PlatformPropertiesUtils; import org.eclipse.tycho.core.utils.TychoProjectUtils; @Mojo(name = "generate", defaultPhase=LifecyclePhase.GENERATE_SOURCES, requiresProject=true) public class MavenGenerator extends AbstractMojo { public static final String SEPARATOR = File.separator; @Parameter(property = "session", readonly = true, required = true) private MavenSession session; @Parameter(defaultValue = "SYSTEM") private JDKUsage useJDK; @Parameter private BundleStartLevel defaultStartLevel; @Parameter(required = true) private String gmfgen; @Parameter(defaultValue = "${basedir}") private String output; @Parameter private File[] relatedProjects; @Parameter private File formatter; @Parameter private String customEmitter; @Parameter private String customEmitterBundle; @Parameter(property = "project", readonly = true) private MavenProject project; @Parameter(defaultValue = "${basedir}/../target/work") private File work; @Parameter(defaultValue = "${basedir}/../target/osgi/") private File osgiDataDirectory; @Component private ToolchainManager toolchainManager; @Component private ToolchainProvider toolchainProvider; @Component private EquinoxInstallationFactory installationFactory; @Component private DefaultDependencyResolverFactory dependencyResolverLocator; @Parameter private Dependency[] dependencies; @Component private EquinoxLauncher launcher; public void execute() throws MojoExecutionException { checkOverlapPath(work, project.getBasedir(), "Work dir", "Project dir"); checkOverlapPath(osgiDataDirectory, project.getBasedir(), "OSGi dir", "Project dir"); checkOverlapPath(work, output, "Work dir", "Path to generated plugin"); checkOverlapPath(osgiDataDirectory, output, "OSGi dir", "Path to generated plugin"); EquinoxInstallation gmfGenerationInstallation = createGMFGenerationIntallation(); EquinoxLaunchConfiguration cli = new EquinoxLaunchConfiguration(gmfGenerationInstallation); String executable = null; Toolchain tc = getToolchain(); if (tc != null) { executable = tc.findTool("java"); } cli.setJvmExecutable(executable); cli.setWorkingDirectory(project.getBasedir()); Properties properties = (Properties) project.getContextValue(TychoConstants.CTX_MERGED_PROPERTIES); cli.addVMArguments("-Dosgi.os=" + PlatformPropertiesUtils.getOS(properties), // "-Dosgi.ws=" + PlatformPropertiesUtils.getWS(properties), // "-Dosgi.arch=" + PlatformPropertiesUtils.getArch(properties)); cli.addProgramArguments("-data", osgiDataDirectory.getAbsolutePath(), // "-install", gmfGenerationInstallation.getLocation().getAbsolutePath(), // "-application", "org.eclipse.gmf.tooling.codegen.launcher.GMFGenerateApplication", "-configuration", gmfGenerationInstallation.getConfigurationLocation().getAbsolutePath()); String workDirPath = work.getAbsolutePath(); StringBuilder tempFile = new StringBuilder(workDirPath); if (!workDirPath.endsWith(SEPARATOR)) { tempFile.append(SEPARATOR); } tempFile.append("gmflauncher.properties"); String tempFilePath = tempFile.toString(); prepareGeneratePropertyFile(tempFilePath); cli.addProgramArguments(GMFLauncherConstants.KEY_GENERATION_PROPERTIES, tempFilePath); int result = launcher.execute(cli, 0); if (result != 0) { throw new MojoExecutionException("Generation error"); } } protected Toolchain getToolchain() throws MojoExecutionException { if (JDKUsage.SYSTEM.equals(useJDK)) { if (toolchainManager != null) { return toolchainManager.getToolchainFromBuildContext("jdk", session); } return null; } String profileName = TychoProjectUtils.getExecutionEnvironmentConfiguration(project).getProfileName(); Toolchain toolChain = toolchainProvider.findMatchingJavaToolChain(session, profileName); if (toolChain == null) { throw new MojoExecutionException("useJDK = BREE configured, but no toolchain of type 'jdk' with id '" + profileName + "' found. See http://maven.apache.org/guides/mini/guide-using-toolchains.html"); } return toolChain; } EquinoxInstallation createGMFGenerationIntallation() throws MojoExecutionException { DependencyResolver platformResolver = dependencyResolverLocator.lookupDependencyResolver(project); final List<Dependency> extraDependencies = getExtraDependencies(); List<ReactorProject> reactorProjects = getReactorProjects(); final DependencyResolverConfiguration resolverConfiguration = new DependencyResolverConfiguration() { @Override public OptionalResolutionAction getOptionalResolutionAction() { return OptionalResolutionAction.IGNORE; } @Override public List<Dependency> getExtraRequirements() { return extraDependencies; } }; DependencyArtifacts generateRuntimeArtifacts = platformResolver.resolveDependencies(session, project, null, reactorProjects, resolverConfiguration); if (generateRuntimeArtifacts == null) { throw new MojoExecutionException("Cannot determinate build target platform location -- code not generated"); } work.mkdirs(); EquinoxInstallationDescription generateAppDescr = new DefaultEquinoxInstallationDescription(); generateAppDescr.setDefaultBundleStartLevel(defaultStartLevel); generateAppDescr.addBundlesToExplode(new LinkedList<String>()); generateAppDescr.addFrameworkExtensions(new LinkedList<File>()); for (ArtifactDescriptor artifact : generateRuntimeArtifacts.getArtifacts(ArtifactType.TYPE_ECLIPSE_PLUGIN)) { ReactorProject otherProject = artifact.getMavenProject(); if (otherProject != null) { if (otherProject.sameProject(project)) { generateAppDescr.addBundle(artifact.getKey(), project.getBasedir()); continue; } File file = otherProject.getArtifact(artifact.getClassifier()); if (file != null) { generateAppDescr.addBundle(artifact.getKey(), file); continue; } } generateAppDescr.addBundle(artifact); } return installationFactory.createInstallation(generateAppDescr, work); } private List<Dependency> getExtraDependencies() { final List<Dependency> dependencies = new ArrayList<Dependency>(); if (this.dependencies != null) { dependencies.addAll(Arrays.asList(this.dependencies)); } TargetPlatformConfiguration configuration = TychoProjectUtils.getTargetPlatformConfiguration(project); dependencies.addAll(configuration.getDependencyResolverConfiguration().getExtraRequirements()); dependencies.add(newBundleDependency("org.eclipse.osgi")); dependencies.add(newBundleDependency(EquinoxInstallationDescription.EQUINOX_LAUNCHER)); dependencies.add(newBundleDependency("org.eclipse.core.runtime")); dependencies.add(newBundleDependency("org.eclipse.gmf.tooling.codegen.launcher")); return dependencies; } protected Dependency newBundleDependency(String bundleId) { Dependency ideapp = new Dependency(); ideapp.setArtifactId(bundleId); ideapp.setType(ArtifactType.TYPE_ECLIPSE_PLUGIN); return ideapp; } private List<ReactorProject> getReactorProjects() { return DefaultReactorProject.adapt(session); } private void checkOverlapPath(File containerDir, File overlapDir, String containerName, String overlapDirName) throws MojoExecutionException { checkOverlapPath(containerDir, overlapDir.getAbsolutePath(), containerName, overlapDirName); } private void checkOverlapPath(File containerDir, String overlapDirPath, String containerName, String overlapDirName) throws MojoExecutionException { String containerDirPath = containerDir.getAbsolutePath(); if (containerDirPath.startsWith(overlapDirPath)) { throw new MojoExecutionException(containerDir + ":" + containerDir + " cannot be located in:" + overlapDirName +":"+ overlapDirPath); } } public void prepareGeneratePropertyFile(String tempFile) throws MojoExecutionException { Properties prop = new Properties(); OutputStream outputStrem = null; try { File outputFile = new File(tempFile); outputStrem = new FileOutputStream(outputFile); // set the properties value prop.setProperty(GMFLauncherConstants.PROPERTY_GMFGEN_MODEL, gmfgen); prop.setProperty(GMFLauncherConstants.PROPERTY_GENERATE_PROJECT_LOCATION, output); prop.setProperty(GMFLauncherConstants.PROPERTY_GMFGEN_PROJECT_LOCATION, project.getBasedir().getAbsolutePath()); if (relatedProjects != null && relatedProjects.length > 0) { StringBuilder sb = new StringBuilder(relatedProjects[0].getAbsolutePath()); for (int i = 1; i < relatedProjects.length; i++) { sb.append(','); sb.append(relatedProjects[i].getAbsolutePath()); } prop.setProperty(GMFLauncherConstants.PROPERTY_RELATED_PROJECTS, sb.toString()); } if (formatter.exists()) { prop.setProperty(GMFLauncherConstants.PROPERTY_FORMATTER, formatter.getAbsolutePath()); } if (customEmitterBundle != null && !customEmitterBundle.isEmpty() && customEmitter != null && !customEmitter.isEmpty()) { prop.setProperty(GMFLauncherConstants.PROPERTY_CUSTOM_EMITTER, customEmitter); prop.setProperty(GMFLauncherConstants.PROPERTY_CUSTOM_EMITTER_BUNDLE, customEmitterBundle); } prop.setProperty(GMFLauncherConstants.PROPERTY_TRACE, new Boolean(getLog().isDebugEnabled()).toString()); prop.store(outputStrem, null); } catch (FileNotFoundException e) { throw new MojoExecutionException(e.getMessage()); } catch (IOException e) { throw new MojoExecutionException(e.getMessage()); } finally { if (outputStrem != null) { try { outputStrem.close(); } catch (IOException e) { throw new MojoExecutionException(e.getMessage()); } } } } }