/******************************************************************************* * Copyright (c) 2014 Rohde & Schwarz GmbH & Co. KG and others. * 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 * * Contributors: * This class was heavily quoted from org.eclipse.cdt.arduino.core.ArduinoProjectGenerator * Martin Runge - initial implementation of cmake support *******************************************************************************/ package org.eclipse.cdt.cmake; import java.io.File; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.Writer; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.HashMap; import java.util.Map; import org.eclipse.cdt.cmake.ui.Messages; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.extension.CConfigurationData; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.resources.ICommand; import org.eclipse.cdt.managedbuilder.core.IToolChain; import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; import org.eclipse.cdt.managedbuilder.internal.core.ManagedBuildInfo; import org.eclipse.cdt.managedbuilder.internal.core.ManagedProject; import org.eclipse.cdt.managedbuilder.internal.core.ToolChain; import org.eclipse.cdt.utils.Platform; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.wizard.IWizard; import org.eclipse.jface.wizard.IWizardContainer; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.ide.undo.CreateProjectOperation; import org.eclipse.ui.ide.undo.WorkspaceUndoUtil; import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; import org.eclipse.ui.internal.ide.StatusUtil; import org.eclipse.ui.internal.wizards.newresource.ResourceMessages; import org.eclipse.ui.statushandlers.StatusAdapter; import org.eclipse.ui.statushandlers.StatusManager; import freemarker.core.ParseException; import freemarker.template.Configuration; import freemarker.template.MalformedTemplateNameException; import freemarker.template.Template; import freemarker.template.TemplateException; import freemarker.template.TemplateNotFoundException; /** * @author runge_m * */ public class CMakeProjectGenerator { public static final String CMAKE_TOOLCHAIN_ID = "org.eclipse.cdt.cmake.toolChain.debug"; public static final String CMAKE_BUILDER_ID = "org.eclipse.cdt.cmake.CMakeProjectBuilder"; public static final String GEN_MAKE_BUILDER_ID = "org.eclipse.cdt.managedbuilder.core.genmakebuilder"; private IProject project; private IWizardContainer container = null; /** * @param newProject */ public CMakeProjectGenerator(IProject newProject) { project = newProject; } /** * @param monitor */ public void setupProject(IProgressMonitor monitor) throws CoreException { // Add CMake nature IProjectDescription projDesc = project.getDescription(); boolean cmakeNatureAlreadyThere = false; String[] oldIds = projDesc.getNatureIds(); for(int i=0; i < oldIds.length; i++) { if(oldIds[i].equals(CMakeProjectNature.CMAKE_NATURE_ID)) { cmakeNatureAlreadyThere = true; } } if(!cmakeNatureAlreadyThere) { String[] newIds = new String[oldIds.length + 1]; System.arraycopy(oldIds, 0, newIds, 0, oldIds.length); newIds[newIds.length - 1] = CMakeProjectNature.CMAKE_NATURE_ID; projDesc.setNatureIds(newIds); project.setDescription(projDesc, monitor); } // create the CDT natures and build setup CCorePlugin.getDefault().createCDTProject(projDesc, project, monitor); CCorePlugin.getDefault().convertProjectFromCtoCC(project, monitor); ICProjectDescription cprojDesc = CCorePlugin.getDefault().createProjectDescription(project, false); ManagedBuildInfo info = ManagedBuildManager.createBuildInfo(project); ManagedProject mProj = new ManagedProject(cprojDesc); info.setManagedProject(mProj); createBuildConfigurations(cprojDesc); // Add CMake builder projDesc = project.getDescription(); ICommand[] oldBuilders = projDesc.getBuildSpec(); boolean genMakeBilderAlreadyThere = false; boolean cmakeBuilderAlreadyThere = false; int additionalBuilders = 2; for(int i=0; i < oldBuilders.length; i++) { if(oldBuilders[i].getBuilderName().equals(CMAKE_BUILDER_ID)) { cmakeBuilderAlreadyThere = true; additionalBuilders--; } if(oldBuilders[i].getBuilderName().equals(GEN_MAKE_BUILDER_ID)) { genMakeBilderAlreadyThere = true; additionalBuilders--; } } if(additionalBuilders < 0) { additionalBuilders = 0; } ICommand[] newBuilders = new ICommand[oldBuilders.length + additionalBuilders]; if(!cmakeBuilderAlreadyThere) { ICommand cmakeBuilder = projDesc.newCommand(); cmakeBuilder.setBuilderName(CMAKE_BUILDER_ID); cmakeBuilder.setBuilding(IncrementalProjectBuilder.FULL_BUILD, true); cmakeBuilder.setBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD, true); cmakeBuilder.setBuilding(IncrementalProjectBuilder.CLEAN_BUILD, true); newBuilders[0] = cmakeBuilder; } if(!genMakeBilderAlreadyThere) { ICommand makeBuilder = projDesc.newCommand(); makeBuilder.setBuilderName(GEN_MAKE_BUILDER_ID); makeBuilder.setBuilding(IncrementalProjectBuilder.FULL_BUILD, true); makeBuilder.setBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD, true); makeBuilder.setBuilding(IncrementalProjectBuilder.CLEAN_BUILD, true); newBuilders[1] = makeBuilder; } System.arraycopy(oldBuilders, 0, newBuilders, additionalBuilders, oldBuilders.length); projDesc.setBuildSpec(newBuilders); project.setDescription(projDesc, monitor); CCorePlugin.getDefault().setProjectDescription(project, cprojDesc, true, monitor); } public void extractHelloWorldTemplate(IProgressMonitor monitor) throws CoreException { // Generate files try { Configuration fmConfig = new Configuration(Configuration.VERSION_2_3_22); URL templateDirURL = FileLocator.find(Activator.getContext().getBundle(), new Path("/templates/ConsoleHelloWorld"), null); //$NON-NLS-1$ // workaround bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=145096 (spaces in URL not escaped for URI) URL tmpURL = FileLocator.toFileURL(templateDirURL); URI tmpURI = new URI(tmpURL.getProtocol(), tmpURL.getPath(), null); fmConfig.setDirectoryForTemplateLoading(new File(tmpURI)); final Map<String, Object> fmModel = new HashMap<>(); fmModel.put("projectName", project.getName()); //$NON-NLS-1$ instantiateTemplate( fmModel, fmConfig ); //generateFile(fmModel, fmConfig.getTemplate("Makefile"), project.getFile("Makefile")); //$NON-NLS-1$ //$NON-NLS-2$ //generateFile(fmModel, fmConfig.getTemplate("arduino.mk"), project.getFile("arduino.mk")); //$NON-NLS-1$ //$NON-NLS-2$ // sourceFile = project.getFile(project.getName() + ".cpp"); //$NON-NLS-1$ // generateFile(fmModel, fmConfig.getTemplate("arduino.cpp"), sourceFile); //$NON-NLS-1$ } catch (IOException e) { throw new CoreException(new Status(IStatus.ERROR, Activator.getId(), e.getLocalizedMessage(), e)); } catch (URISyntaxException e) { throw new CoreException(new Status(IStatus.ERROR, Activator.getId(), e.getLocalizedMessage(), e)); } catch (TemplateException e) { throw new CoreException(new Status(IStatus.ERROR, Activator.getId(), e.getLocalizedMessage(), e)); } } /** * @param cprojDesc * @return */ private ICConfigurationDescription createBuildConfigurations(ICProjectDescription cprojDesc) throws CoreException { ManagedProject managedProject = new ManagedProject(cprojDesc); String configId = ManagedBuildManager.calculateChildId(CMAKE_TOOLCHAIN_ID, null); IToolChain cmakeToolChain = ManagedBuildManager.getExtensionToolChain(CMAKE_TOOLCHAIN_ID); org.eclipse.cdt.managedbuilder.internal.core.Configuration newConfig = new org.eclipse.cdt.managedbuilder.internal.core.Configuration( managedProject, (ToolChain) cmakeToolChain, configId, "debug"); IToolChain newToolChain = newConfig.getToolChain(); switch (Platform.getOS()) { case "win32": { newConfig.getEditableBuilder().setCommand("mingw32-make.exe"); break; } default: { newConfig.getEditableBuilder().setCommand("make"); break; } } CConfigurationData data = newConfig.getConfigurationData(); return cprojDesc.createConfiguration(ManagedBuildManager.CFG_DATA_PROVIDER_ID, data); } /** * @param fmModel * @param fmConfig * @throws CoreException * @throws IOException * @throws TemplateException * @throws ParseException * @throws MalformedTemplateNameException * @throws TemplateNotFoundException */ private void instantiateTemplate(Map<String, Object> fmModel, Configuration fmConfig) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, TemplateException, IOException, CoreException { generateFile(fmModel, fmConfig.getTemplate("CMakeLists.txt"), project.getFile("CMakeLists.txt")); generateFile(fmModel, fmConfig.getTemplate("main.cpp"), project.getFile("main.cpp")); } private static void generateFile(Object model, Template template, final IFile outputFile) throws TemplateException, IOException, CoreException { final PipedInputStream in = new PipedInputStream(); PipedOutputStream out = new PipedOutputStream(in); final Writer writer = new OutputStreamWriter(out); Job job = new Job(Messages.CMakeProjectGenerator_0) { protected IStatus run(IProgressMonitor monitor) { try { outputFile.create(in, true, monitor); } catch (CoreException e) { return e.getStatus(); } return Status.OK_STATUS; } }; job.setRule(outputFile.getProject()); job.schedule(); template.process(model, writer); writer.close(); try { job.join(); } catch (InterruptedException e) { // TODO anything? } IStatus status = job.getResult(); if (!status.isOK()) throw new CoreException(status); } /** * @return */ public IFile getSourceFile() { // TODO Auto-generated method stub return null; } /** * @param shell * @return * */ public IProject createProject(IWizard parent, URI locationURI) { IWorkspace workspace = ResourcesPlugin.getWorkspace(); final IWizard containerRef = parent; final IProjectDescription description = workspace.newProjectDescription(project.getName()); description.setLocationURI(locationURI); // create the new project operation IRunnableWithProgress op = new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException { CreateProjectOperation op = new CreateProjectOperation(description, ResourceMessages.NewProject_windowTitle); try { // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=219901 // directly execute the operation so that the undo state is // not preserved. Making this undoable resulted in too many // accidental file deletions. op.execute(monitor, WorkspaceUndoUtil.getUIInfoAdapter(containerRef.getContainer().getShell())); } catch ( org.eclipse.core.commands.ExecutionException e) { throw new InvocationTargetException(e); } } }; // run the new project creation operation try { parent.getContainer().run(true, true, op); } catch (InterruptedException e) { return null; } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t instanceof ExecutionException && t.getCause() instanceof CoreException) { CoreException cause = (CoreException) t.getCause(); StatusAdapter status; if (cause.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) { status = new StatusAdapter( StatusUtil .newStatus( IStatus.WARNING, NLS .bind( ResourceMessages.NewProject_caseVariantExistsError, project.getName()), cause)); } else { status = new StatusAdapter(StatusUtil.newStatus(cause .getStatus().getSeverity(), ResourceMessages.NewProject_errorMessage, cause)); } status.setProperty(StatusAdapter.TITLE_PROPERTY, ResourceMessages.NewProject_errorMessage); StatusManager.getManager().handle(status, StatusManager.BLOCK); } else { StatusAdapter status = new StatusAdapter(new Status( IStatus.WARNING, IDEWorkbenchPlugin.IDE_WORKBENCH, 0, NLS.bind(ResourceMessages.NewProject_internalError, t .getMessage()), t)); status.setProperty(StatusAdapter.TITLE_PROPERTY, ResourceMessages.NewProject_errorMessage); StatusManager.getManager().handle(status, StatusManager.LOG | StatusManager.BLOCK); } return null; } return project; } }