/**
* Copyright (C) 2005 - 2012 Eric Van Dewoestine
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.eclim.plugin.cdt.project;
import java.io.FileInputStream;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclim.Services;
import org.eclim.command.CommandLine;
import org.eclim.command.Error;
import org.eclim.eclipse.EclimPlugin;
import org.eclim.plugin.cdt.PluginResources;
import org.eclim.plugin.cdt.util.CUtils;
import org.eclim.plugin.core.project.ProjectManager;
import org.eclim.plugin.core.util.XmlUtils;
import org.eclim.util.IOUtils;
import org.eclim.util.file.FileOffsets;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.index.IIndexManager;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICModelStatus;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.IPathEntry;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager;
import org.eclipse.cdt.core.settings.model.extension.CConfigurationData;
import org.eclipse.cdt.core.settings.model.util.PathEntryTranslator;
import org.eclipse.cdt.managedbuilder.buildproperties.IBuildPropertyManager;
import org.eclipse.cdt.managedbuilder.buildproperties.IBuildPropertyValue;
import org.eclipse.cdt.managedbuilder.core.BuildListComparator;
import org.eclipse.cdt.managedbuilder.core.IBuilder;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IToolChain;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.internal.core.Configuration;
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.managedbuilder.internal.dataprovider.ConfigurationDataProvider;
import org.eclipse.cdt.managedbuilder.ui.wizards.CfgHolder;
import org.eclipse.cdt.managedbuilder.ui.wizards.MBSWizardHandler;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.wizard.IWizard;
import org.eclipse.swt.widgets.Composite;
/**
* Manager for c/c++ projects.
*
* @author Eric Van Dewoestine
*/
public class CProjectManager
implements ProjectManager
{
private static final String CPROJECT_XSD =
"/resources/schema/eclipse/cproject.xsd";
private static final Pattern ENTRY_PATTERN =
Pattern.compile(".*?:\\s+/.*?/(.*)\\s+for .*");
private static final Pattern INCLUDE_PATTERN =
Pattern.compile(".*?\\((.*?)\\).*");
@Override
public void create(final IProject project, CommandLine commandLine)
throws Exception
{
// TODO: - support other project types:
// executable, shared library, static library
// - support specifying the toolchain to use.
final IBuildPropertyManager buildManager =
ManagedBuildManager.getBuildPropertyManager();
final IBuildPropertyValue[] vs = buildManager
.getPropertyType(MBSWizardHandler.ARTIFACT).getSupportedValues();
Arrays.sort(vs, BuildListComparator.getInstance());
MBSWizardHandler handler = null;
toolchainLoop:
for (IBuildPropertyValue value : vs){
final IToolChain[] toolChains = ManagedBuildManager
.getExtensionsToolChains(MBSWizardHandler.ARTIFACT, value.getId(), false);
if (toolChains != null && toolChains.length > 0){
for (IToolChain tc : toolChains){
if (!tc.isAbstract() &&
!tc.isSystemObject() &&
tc.isSupported() &&
ManagedBuildManager.isPlatformOk(tc))
{
handler = new LocalMBSWizardHandler(
value, EclimPlugin.getShell(), null, tc);
// stop once we've found a toolchain w/ a config
CfgHolder[] cfgs = handler.getCfgItems(false);
if (cfgs != null &&
cfgs.length > 0 &&
cfgs[0].getConfiguration() != null)
{
break toolchainLoop;
}
}
}
// handle case where no toolchain w/ a config was found, like on
// windows when eclipse can't find cygwin or mingw.
IToolChain ntc = ManagedBuildManager
.getExtensionToolChain(ConfigurationDataProvider.PREF_TC_ID);
handler = new LocalSTDWizardHandler(
value, EclimPlugin.getShell(), null, ntc);
}
}
try{
handler.createProject(project, true, true, new NullProgressMonitor());
ICProject cproject = CUtils.getCProject(project);
CCorePlugin.getIndexManager().reindex(cproject);
}catch(Exception e){
throw new RuntimeException(e);
}
}
@Override
public List<Error> update(IProject project, CommandLine commandLine)
throws Exception
{
PluginResources resources = (PluginResources)
Services.getPluginResources(PluginResources.NAME);
List<Error> errors = XmlUtils.validateXml(
project.getName(),
".cproject",
resources.getResource(CPROJECT_XSD).toString());
if(errors.size() > 0){
return errors;
}
// FIXME: force load of file from disk
ICProjectDescriptionManager manager =
CoreModel.getDefault().getProjectDescriptionManager();
//manager.updateProjectDescriptions(new IProject[]{project}, null);
ICProjectDescription desc = manager.getProjectDescription(project, false);
ICConfigurationDescription config = desc.getDefaultSettingConfiguration();
PathEntryTranslator.PathEntryCollector cr =
PathEntryTranslator.collectEntries(project, config);
IPathEntry[] entries = cr.getEntries(
PathEntryTranslator.INCLUDE_USER, config);
ICProject cproject = CoreModel.getDefault().create(project);
String dotcproject = project.getFile(".cproject").getRawLocation().toOSString();
FileOffsets offsets = FileOffsets.compile(dotcproject);
String cprojectValue = IOUtils.toString(new FileInputStream(dotcproject));
for (IPathEntry entry : entries){
ICModelStatus status =
CoreModel.validatePathEntry(cproject, entry, true, true);
if(!status.isOK()){
errors.add(createErrorFromStatus(
offsets, dotcproject, cprojectValue, entry, status));
}
}
return errors;
}
@Override
public void delete(IProject project, CommandLine commandLine)
throws Exception
{
}
@Override
public void refresh(IProject project, CommandLine commandLine)
throws Exception
{
ICProject cproject = CUtils.getCProject(project);
CCorePlugin.getIndexManager().reindex(cproject);
CCorePlugin.getIndexManager()
.joinIndexer(IIndexManager.FOREVER, new NullProgressMonitor());
}
@Override
public void refresh(IProject project, IFile file)
throws Exception
{
}
/**
* Creates an Error from the supplied IJavaModelStatus.
*
* @param offsets File offsets for the classpath file.
* @param filename The filename of the error.
* @param contents The contents of the file as a String.
* @param status The IJavaModelStatus.
* @return The Error.
*/
private Error createErrorFromStatus(
FileOffsets offsets,
String filename,
String contents,
IPathEntry entry,
ICModelStatus status)
throws Exception
{
int line = 0;
int col = 0;
// get the pattern to search for from the status message.
Matcher matcher = null;
String pattern = null;
switch(entry.getEntryKind()){
case IPathEntry.CDT_OUTPUT:
case IPathEntry.CDT_SOURCE:
matcher = ENTRY_PATTERN.matcher(status.getString());
pattern = "name=\"" + matcher.replaceFirst("$1") + "\"";
break;
case IPathEntry.CDT_INCLUDE:
case IPathEntry.CDT_INCLUDE_FILE:
matcher = INCLUDE_PATTERN.matcher(status.getString());
pattern = "value=\"" + matcher.replaceFirst("$1") + "\"";
break;
}
if (matcher != null){
// find the pattern in the classpath file.
matcher = Pattern.compile("\\Q" + pattern + "\\E").matcher(contents);
if(matcher.find()){
int[] position = offsets.offsetToLineColumn(matcher.start());
line = position[0];
col = position[1];
}
}
return new Error(status.getMessage(), filename, line, col, false);
}
private class LocalMBSWizardHandler
extends MBSWizardHandler
{
private IToolChain toolchain;
public LocalMBSWizardHandler(
IBuildPropertyValue val, Composite p, IWizard w, IToolChain tc)
{
super(val, p, w);
this.toolchain = tc;
}
public IToolChain[] getSelectedToolChains()
{
return new IToolChain[]{toolchain};
}
protected void doCustom(IProject project)
{
// no-op
}
public CfgHolder[] getCfgItems(boolean defaults)
{
CfgHolder[] cfgs = super.getCfgItems(defaults);
if (cfgs == null || cfgs.length == 0){
IToolChain tc = ManagedBuildManager
.getExtensionToolChain(ConfigurationDataProvider.PREF_TC_ID);
cfgs = new CfgHolder[1];
cfgs[0] = new CfgHolder(tc, null);
}
return cfgs;
}
/*public CfgHolder[] getCfgItems(boolean defaults)
{
CfgHolder[] cfg = super.getCfgItems(true);
if (cfg == null || cfg.length == 0){
IConfiguration config = ManagedBuildManager
.getExtensionConfiguration(
"org.eclipse.cdt.build.core.prefbase.cfg");
//.getExtensionConfiguration(ConfigurationDataProvider.PREF_CFG_ID);
IToolChain noTc = ManagedBuildManager
.getExtensionToolChain(ConfigurationDataProvider.PREF_TC_ID);
cfg = new CfgHolder[1];
cfg[0] = new CfgHolder(noTc, config);
logger.warn("No suitable toolchain config found, " +
"using 'No Toolchain' config.");
}
return cfg;
}*/
}
private class LocalSTDWizardHandler
extends LocalMBSWizardHandler
{
public LocalSTDWizardHandler(
IBuildPropertyValue val, Composite p, IWizard w, IToolChain tc)
{
super(val, p, w, tc);
}
/**** Copied from STDWizardHandler ****/
public void createProject(
IProject project,
boolean defaults,
boolean onFinish,
IProgressMonitor monitor)
throws CoreException
{
try {
monitor.beginTask("", 100); //$NON-NLS-1$
setProjectDescription(project, defaults, onFinish, monitor);
doTemplatesPostProcess(project);
doCustom(project);
monitor.worked(30);
} finally {
monitor.done();
}
}
/**** Copied from STDWizardHandler ****/
private void setProjectDescription(
IProject project,
boolean defaults,
boolean onFinish,
IProgressMonitor monitor)
throws CoreException
{
ICProjectDescriptionManager mngr =
CoreModel.getDefault().getProjectDescriptionManager();
ICProjectDescription des =
mngr.createProjectDescription(project, false, !onFinish);
ManagedBuildInfo info = ManagedBuildManager.createBuildInfo(project);
ManagedProject mProj = new ManagedProject(des);
info.setManagedProject(mProj);
monitor.worked(20);
cfgs = CfgHolder.unique(getCfgItems(false));
cfgs = CfgHolder.reorder(cfgs);
int work = 50 / cfgs.length;
for (int i = 0; i < cfgs.length; i++) {
String s = (cfgs[i].getToolChain() == null) ?
"0" : ((ToolChain)(cfgs[i].getToolChain())).getId(); //$NON-NLS-1$
Configuration cfg = new Configuration(
mProj,
(ToolChain)cfgs[i].getToolChain(),
ManagedBuildManager.calculateChildId(s, null),
cfgs[i].getName());
cfgs[i].setConfiguration(cfg);
IBuilder bld = cfg.getEditableBuilder();
if (bld != null) {
if(bld.isInternalBuilder()){
IConfiguration prefCfg =
ManagedBuildManager.getPreferenceConfiguration(false);
IBuilder prefBuilder = prefCfg.getBuilder();
cfg.changeBuilder(
prefBuilder,
ManagedBuildManager.calculateChildId(cfg.getId(), null),
prefBuilder.getName());
bld = cfg.getEditableBuilder();
bld.setBuildPath(null);
}
bld.setManagedBuildOn(false);
}
cfg.setArtifactName(mProj.getDefaultArtifactName());
CConfigurationData data = cfg.getConfigurationData();
des.createConfiguration(ManagedBuildManager.CFG_DATA_PROVIDER_ID, data);
monitor.worked(work);
}
mngr.setProjectDescription(project, des);
}
}
}