project/com.nokia.carbide.cpp.project.core/src/com/nokia/carbide/cpp/project/core/ProjectCorePlugin.java
/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package com.nokia.carbide.cpp.project.core;
import com.nokia.carbide.cdt.builder.CarbideBuilderPlugin;
import com.nokia.carbide.cdt.builder.EpocEngineHelper;
import com.nokia.carbide.cdt.builder.project.ICarbideProjectInfo;
import com.nokia.carbide.cdt.builder.project.ISISBuilderInfo;
import com.nokia.carbide.cpp.internal.api.project.core.ProjectCorePluginUtility;
import com.nokia.carbide.cpp.internal.project.core.Messages;
import com.nokia.carbide.cpp.sdk.core.ISymbianBuildContext;
import com.nokia.cpp.internal.api.utils.core.Logging;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.CProjectNature;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
import org.eclipse.cdt.internal.core.model.CModelManager;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.osgi.framework.BundleContext;
import java.io.File;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.util.List;
import java.util.Map;
/**
* The activator class controls the plug-in life cycle
*/
public class ProjectCorePlugin extends Plugin {
// The plug-in ID
public static final String PLUGIN_ID = "com.nokia.carbide.cpp.project.core"; //$NON-NLS-1$
public static final String CARBIDE_PROJECT_ID = ProjectCorePlugin.getUniqueId() + ".carbidecppproject"; //$NON-NLS-1$
private static final String PROJECT_FILE_NAME = ".project"; //$NON-NLS-1$
private static final String CDT_PROJECT_FILE_NAME = ".cproject"; //$NON-NLS-1$
private static final String OLD_CDT_PROJECT_FILE_NAME = ".cdtproject"; //$NON-NLS-1$
private static final String CARBIDE_BUILD_SETTINGS_FILE_NAME = ".settings/.carbide_build_settings"; //$NON-NLS-1$
private static final String CDT_CORE_PREFS_FILE_NAME = ".settings/org.eclipse.cdt.core.prefs"; //$NON-NLS-1$
// The shared instance
private static ProjectCorePlugin plugin;
// internal utility class
private static ProjectCorePluginUtility pluginUtility;
private static long projectCreationStartTime = -1L;
/**
* The constructor
*/
public ProjectCorePlugin() {
plugin = this;
pluginUtility = new ProjectCorePluginUtility();
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.Plugins#start(org.osgi.framework.BundleContext)
*/
public void start(BundleContext context) throws Exception {
super.start(context);
pluginUtility.startup();
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception {
plugin = null;
pluginUtility.shutdown();
super.stop(context);
}
/**
* Returns the shared instance
*
* @return the shared instance
*/
public static ProjectCorePlugin getDefault() {
return plugin;
}
/**
* Gets the unique id for this plugin
* @return the unique id for this plugin
*/
public static String getUniqueId() {
if (getDefault() == null) {
return PLUGIN_ID;
}
return getDefault().getBundle().getSymbolicName();
}
/**
* Creates an Eclipse project with the given name and location. Does some error checking first to make
* sure there's not already another open project at that location. Deletes any project files that exist
* at that location as well.
* <p>
* This method is intended to be used to create a project prior to adding folders/files and setting up
* Carbide build settings.
* </p>
*
* @param name the name of the project to create
* @param location the full file system path where the .project file should be created. pass null to use the default location.
* @return the newly created IProject
* @throws CoreException
*/
public static IProject createProject(String name, String location) throws CoreException {
projectCreationStartTime = System.currentTimeMillis();
IProject projectHandle = ResourcesPlugin.getWorkspace().getRoot().getProject(name);
if (!projectHandle.exists()) {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IProjectDescription description = workspace.newProjectDescription(projectHandle.getName());
if (location != null && !location.trim().equals("")) { //$NON-NLS-1$
// don't set the location if it's in the workspace. Eclipse doesn't
// like this and will refuse to create the project. see Eclipse Bugzilla
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=76417
IPath workspaceRoot = ResourcesPlugin.getWorkspace().getRoot().getLocation();
if (workspaceRoot != null) {
IPath defaultLocation = workspaceRoot.append(name);
IPath locationPath = Path.fromPortableString(location);
if (defaultLocation.toOSString().compareToIgnoreCase(locationPath.toOSString()) != 0) {
description.setLocation(locationPath);
description.setLocationURI(URIUtil.toURI(locationPath.toOSString()));
}
}
}
// see if there is already a .project file at this location. if so, see if it's a project in the
// workspace, opened or closed. if so then we need to throw an error because we can't overwrite it.
// if it's not a project in the workspace, ask the user if we should overwrite it or not.
IPath projectLocation = location != null ? new Path(location) : workspace.getRoot().getLocation().append(projectHandle.getName());
IContainer container = workspace.getRoot().getContainerForLocation(projectLocation);
if (container != null && container.getType() == IResource.PROJECT) {
// there is a project at this location already
throw new CoreException(new Status(IStatus.ERROR, PLUGIN_ID, 0, Messages.getString("ProjectCorePlugin.TheProject") + container.getName() + Messages.getString("ProjectCorePlugin.AlreadyExistsInDirectory") + projectLocation.toOSString(), null)); //$NON-NLS-1$ //$NON-NLS-2$
}
// see if .project, .cproject files and .settings directories exist in the file
// system. if so then they are probably left over from an older project and we can
// just overwrite them. the check above would have found them if the project was
// a part of the workspace, so it's not an active project. maybe we should ask the
// user but that may be more confusing.
File projectFile = projectLocation.append(PROJECT_FILE_NAME).toFile();
if (projectFile.exists()) {
projectFile.delete();
}
File cdtProjectFile = projectLocation.append(CDT_PROJECT_FILE_NAME).toFile();
if (cdtProjectFile.exists()) {
cdtProjectFile.delete();
}
File oldCdtProjectFile = projectLocation.append(OLD_CDT_PROJECT_FILE_NAME).toFile();
if (oldCdtProjectFile.exists()) {
oldCdtProjectFile.delete();
}
File carbideBuildFile = projectLocation.append(CARBIDE_BUILD_SETTINGS_FILE_NAME).toFile();
if (carbideBuildFile.exists()) {
carbideBuildFile.delete();
}
File cdtCorePrefsFile = projectLocation.append(CDT_CORE_PREFS_FILE_NAME).toFile();
if (cdtCorePrefsFile.exists()) {
cdtCorePrefsFile.delete();
}
// create the Eclipse project
IProgressMonitor monitor = new NullProgressMonitor();
projectHandle.create(description, monitor);
projectHandle.open(monitor);
} else {
throw new CoreException(new Status(IStatus.ERROR, PLUGIN_ID, 0, Messages.getString("ProjectCorePlugin.TheProject") + name + Messages.getString("ProjectCorePlugin.AlreadyExistsInWorkspace"), null)); //$NON-NLS-1$ //$NON-NLS-2$
}
return projectHandle;
}
/**
* Takes a plain Eclipse project and turns it into a Carbide.c++ project.
* <p>
* This method is intended to be called after creating a project using {@link ProjectCorePlugin#createProject(String, String)}
* and adding all folders and files to it. It sets up the project so it has the Carbide.c++ build nature and sets up all of
* the project settings for it.
* </p>
*
* @param project the project handle returned from {@link ProjectCorePlugin#createProject(String, String)}
* @param projectRelativeBldInfPath the project relative path to the bld.inf file
* @param buildConfigs the list of ISymbianBuildContext's to be used as build configs for the project. can be empty but not null.
* @param infComponentsList the list of mmp/makes files if a subset is to be built, otherwise an empty list (not null) if the entire bld.inf should be built.
* @param debugMMP not used since 1.3
* @param pkgMappings is the Map<ISymbianBuildContext, String> where build contexts are mapped to pkg file relative paths - can be null for none.
* The String value in pkgMappings is same as {@link ISISBuilderInfo#setPKGFile(String)}
* @param monitor progress monitor for this operation.
* @return the ICProject handle
* @throws CoreException
*/
public static ICProject postProjectCreatedActions(final IProject project, String projectRelativeBldInfPath,
List<ISymbianBuildContext> buildConfigs, List<String> infComponentsList, String debugMMP,
Map<ISymbianBuildContext, String> pkgMappings, IProgressMonitor monitor) throws CoreException {
if (project == null || !project.exists()) {
throw new CoreException(new Status(IStatus.ERROR, PLUGIN_ID, 0, "Invalid project passed into postProjectCreatedActions", null)); //$NON-NLS-1$
}
// make sure there is at least one build config as CDT will not allow you to create
// a project with no build configs
if (buildConfigs.size() < 1) {
throw new CoreException(new Status(IStatus.ERROR, PLUGIN_ID, 0, "At least one build configuration required for postProjectCreatedActions", null)); //$NON-NLS-1$
}
// disable build automatically while creating the project
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IWorkspaceDescription workspaceDesc = workspace.getDescription();
boolean autoBuilding = workspaceDesc.isAutoBuilding();
workspaceDesc.setAutoBuilding(false);
workspace.setDescription(workspaceDesc);
// OK, let's create the project now.
ICProject cProject = CoreModel.getDefault().create(project);
monitor.worked(1);
if (monitor.isCanceled()) {
return cProject;
}
try {
// do this ourselves rather than calling CCorePlugin#createCProject as we need open to not
// background refresh, otherwise we can get many resource deltas after the project is created
// and the resource listener has no way of knowing that it shouldn't add these new files to
// the mmp file(s).
// don't use the real monitor here as these calls would change the task name
IProgressMonitor nullMonitor = new NullProgressMonitor();
CProjectNature.addCNature(project, nullMonitor);
CCorePlugin.getDefault().convertProjectFromCtoCC(project, nullMonitor);
monitor.worked(1);
if (monitor.isCanceled()) {
return cProject;
}
// add our carbide builder nature
CarbideBuilderPlugin.addBuildNature(project);
monitor.worked(1);
if (monitor.isCanceled()) {
return cProject;
}
monitor.subTask(Messages.getString("ProjectCorePlugin.CreatingProjectSettingsTask")); //$NON-NLS-1$
// create the c project description
ICProjectDescription projDes = CCorePlugin.getDefault().createProjectDescription(project, false, true);
// setup the builder settings
ProjectCorePluginUtility.setupBuilderSettings(projDes, projectRelativeBldInfPath, buildConfigs, infComponentsList, pkgMappings);
CModelManager.getDefault().resetBinaryParser(project);
monitor.worked(1);
if (monitor.isCanceled()) {
return cProject;
}
} finally {
}
workspaceDesc.setAutoBuilding(autoBuilding);
workspace.setDescription(workspaceDesc);
reportProjectCreationStats(project);
return cProject;
}
private static void reportProjectCreationStats(final IProject project) {
if (projectCreationStartTime < 0)
return;
Thread t = new Thread() {
public void run() {
long resourceCount = countResources(project);
NumberFormat nfTime = NumberFormat.getNumberInstance();
nfTime.setMaximumFractionDigits(2);
nfTime.setMinimumFractionDigits(2);
double creationTime = (System.currentTimeMillis() - projectCreationStartTime) / 1000.0;
int mmpCount = getMMPFileCount(project);
log(new Status(IStatus.INFO, getUniqueId(),
MessageFormat.format(Messages.getString("ProjectCorePlugin.ProjectCreationStatsFormat"), //$NON-NLS-1$
project.getName(), resourceCount, mmpCount, nfTime.format(creationTime))));
projectCreationStartTime = -1;
}
private int getMMPFileCount(final IProject project) {
int mmpCount;
ICarbideProjectInfo cpi = CarbideBuilderPlugin.getBuildManager().getProjectInfo(project);
if (cpi.isBuildingFromInf())
mmpCount = EpocEngineHelper.getMMPFilesForProject(cpi).size();
else
mmpCount = cpi.getNormalInfBuildComponents().size();
return mmpCount;
}
private long countResources(IProject project) {
final long count[] = { 0 };
try {
project.accept(new IResourceProxyVisitor() {
public boolean visit(IResourceProxy proxy) throws CoreException {
count[0]++;
return true;
}
}, 0);
} catch (CoreException e) {
log(e);
}
return count[0];
}
};
t.start();
}
public static void log(IStatus status) {
Logging.log(plugin, status);
}
public static void log(Throwable thr) {
Logging.log(plugin, Logging.newStatus(plugin, thr));
}
}