#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

399 lines
13 KiB

// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.Xml.Linq;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project.Converter;
namespace ICSharpCode.SharpDevelop.Project
{
public enum OutputType {
[Description("${res:Dialog.Options.PrjOptions.Configuration.CompileTarget.Exe}")]
Exe,
[Description("${res:Dialog.Options.PrjOptions.Configuration.CompileTarget.WinExe}")]
WinExe,
[Description("${res:Dialog.Options.PrjOptions.Configuration.CompileTarget.Library}")]
Library,
[Description("${res:Dialog.Options.PrjOptions.Configuration.CompileTarget.Module}")]
Module
}
/// <summary>
/// A compilable project based on MSBuild.
/// </summary>
public abstract class CompilableProject : MSBuildBasedProject, IUpgradableProject
{
#region Static methods
/// <summary>
/// Gets the file extension of the assembly created when building a project
/// with the specified output type.
/// Example: OutputType.Exe => ".exe"
/// </summary>
public static string GetExtension(OutputType outputType)
{
switch (outputType) {
case OutputType.WinExe:
case OutputType.Exe:
return ".exe";
case OutputType.Module:
return ".netmodule";
default:
return ".dll";
}
}
#endregion
/// <summary>
/// A list of project properties that cause reparsing of references when they are changed.
/// </summary>
protected readonly ISet<string> reparseReferencesSensitiveProperties = new SortedSet<string>();
/// <summary>
/// A list of project properties that cause reparsing of code when they are changed.
/// </summary>
protected readonly ISet<string> reparseCodeSensitiveProperties = new SortedSet<string>();
protected CompilableProject(ICSharpCode.SharpDevelop.Internal.Templates.ProjectCreateInformation information)
: base(information)
{
this.OutputType = OutputType.Exe;
this.RootNamespace = information.RootNamespace;
this.AssemblyName = information.ProjectName;
ClientProfileTargetFramework clientProfile = information.TargetFramework as ClientProfileTargetFramework;
if (clientProfile != null) {
SetProperty(null, null, "TargetFrameworkVersion", clientProfile.FullFramework.Name, PropertyStorageLocations.Base, true);
SetProperty(null, null, "TargetFrameworkProfile", "Client", PropertyStorageLocations.Base, true);
} else if (information.TargetFramework != null) {
SetProperty(null, null, "TargetFrameworkVersion", information.TargetFramework.Name, PropertyStorageLocations.Base, true);
}
SetProperty("Debug", null, "OutputPath", @"bin\Debug\",
PropertyStorageLocations.ConfigurationSpecific, true);
SetProperty("Release", null, "OutputPath", @"bin\Release\",
PropertyStorageLocations.ConfigurationSpecific, true);
InvalidateConfigurationPlatformNames();
SetProperty("Debug", null, "DebugSymbols", "True",
PropertyStorageLocations.ConfigurationSpecific, true);
SetProperty("Release", null, "DebugSymbols", "False",
PropertyStorageLocations.ConfigurationSpecific, true);
SetProperty("Debug", null, "DebugType", "Full",
PropertyStorageLocations.ConfigurationSpecific, true);
SetProperty("Release", null, "DebugType", "None",
PropertyStorageLocations.ConfigurationSpecific, true);
SetProperty("Debug", null, "Optimize", "False",
PropertyStorageLocations.ConfigurationSpecific, true);
SetProperty("Release", null, "Optimize", "True",
PropertyStorageLocations.ConfigurationSpecific, true);
}
protected CompilableProject(ProjectLoadInformation information)
: base(information)
{
}
/// <summary>
/// Gets the path where temporary files are written to during compilation.
/// </summary>
[Browsable(false)]
public string IntermediateOutputFullPath {
get {
string outputPath = GetEvaluatedProperty("IntermediateOutputPath");
if (string.IsNullOrEmpty(outputPath)) {
outputPath = GetEvaluatedProperty("BaseIntermediateOutputPath");
if (string.IsNullOrEmpty(outputPath)) {
outputPath = "obj";
}
outputPath = Path.Combine(outputPath, this.ActiveConfiguration.Configuration);
}
return Path.Combine(Directory, outputPath);
}
}
/// <summary>
/// Gets the full path to the xml documentation file generated by the project, or
/// <c>null</c> if no xml documentation is being generated.
/// </summary>
[Browsable(false)]
public string DocumentationFileFullPath {
get {
string file = GetEvaluatedProperty("DocumentationFile");
if (string.IsNullOrEmpty(file))
return null;
return Path.Combine(Directory, file);
}
}
// Make Language abstract again to ensure backend-binding implementers don't forget
// to set it.
public abstract override string Language {
get;
}
[Browsable(false)]
public string TargetFrameworkVersion {
get { return GetEvaluatedProperty("TargetFrameworkVersion") ?? "v2.0"; }
set { SetProperty("TargetFrameworkVersion", value); }
}
[Browsable(false)]
public string TargetFrameworkProfile {
get { return GetEvaluatedProperty("TargetFrameworkProfile"); }
set { SetProperty("TargetFrameworkProfile", value); }
}
public override string AssemblyName {
get { return GetEvaluatedProperty("AssemblyName") ?? Name; }
set { SetProperty("AssemblyName", value); }
}
public override string RootNamespace {
get { return GetEvaluatedProperty("RootNamespace") ?? ""; }
set { SetProperty("RootNamespace", value); }
}
/// <summary>
/// The full path of the assembly generated by the project.
/// </summary>
public override string OutputAssemblyFullPath {
get {
string outputPath = GetEvaluatedProperty("OutputPath") ?? "";
return FileUtility.NormalizePath(Path.Combine(Path.Combine(Directory, outputPath), AssemblyName + GetExtension(OutputType)));
}
}
/// <summary>
/// The full path of the folder where the project's primary output files go.
/// </summary>
public string OutputFullPath {
get {
string outputPath = GetEvaluatedProperty("OutputPath");
// FileUtility.NormalizePath() cleans up any back references.
// e.g. C:\windows\system32\..\system becomes C:\windows\system
return FileUtility.NormalizePath(Path.Combine(Directory, outputPath));
}
}
[Browsable(false)]
public OutputType OutputType {
get {
try {
return (OutputType)Enum.Parse(typeof(OutputType), GetEvaluatedProperty("OutputType") ?? "Exe", true);
} catch (ArgumentException) {
return OutputType.Exe;
}
}
set {
SetProperty("OutputType", value.ToString());
}
}
protected override void OnActiveConfigurationChanged(EventArgs e)
{
base.OnActiveConfigurationChanged(e);
if (!isLoading) {
Reparse(true, true);
}
}
protected override void OnPropertyChanged(ProjectPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.PropertyName == "TargetFrameworkVersion")
CreateItemsListFromMSBuild();
if (!isLoading) {
bool reparseReferences = reparseReferencesSensitiveProperties.Contains(e.PropertyName);
bool reparseCode = reparseCodeSensitiveProperties.Contains(e.PropertyName);
Reparse(reparseReferences, reparseCode);
}
}
void Reparse(bool references, bool code)
{
lock (SyncRoot) {
if (projectContentContainer == null)
return; // parsing hasn't started yet; no need to re-parse
projectContentContainer.SetAssemblyName(this.AssemblyName);
projectContentContainer.SetLocation(this.OutputAssemblyFullPath);
if (references) {
projectContentContainer.ReparseReferences();
}
if (code) {
projectContentContainer.SetCompilerSettings(CreateCompilerSettings());
projectContentContainer.ReparseCode();
}
}
}
[Browsable(false)]
public override string TypeGuid {
get {
return ProjectBindingService.GetCodonPerLanguageName(Language).Guid;
}
set {
throw new NotSupportedException();
}
}
public StartAction StartAction {
get {
try {
return (StartAction)Enum.Parse(typeof(StartAction), GetEvaluatedProperty("StartAction") ?? "Project");
} catch (ArgumentException) {
return StartAction.Project;
}
}
set {
SetProperty("StartAction", value.ToString());
}
}
protected override ProjectBehavior CreateDefaultBehavior()
{
return new DotNetStartBehavior(this, base.CreateDefaultBehavior());
}
public override void Dispose()
{
lock (SyncRoot) {
if (projectContentContainer != null)
projectContentContainer.Dispose();
}
base.Dispose();
}
#region IUpgradableProject
[Browsable(false)]
public virtual bool UpgradeDesired {
get {
return MinimumSolutionVersion < ISolution.SolutionVersionVS2010;
}
}
public virtual CompilerVersion CurrentCompilerVersion {
get { return GetOrCreateBehavior().CurrentCompilerVersion; }
}
public virtual TargetFramework CurrentTargetFramework {
get { return GetOrCreateBehavior().CurrentTargetFramework; }
}
public virtual IEnumerable<CompilerVersion> GetAvailableCompilerVersions()
{
return GetOrCreateBehavior().GetAvailableCompilerVersions();
}
public virtual IEnumerable<TargetFramework> GetAvailableTargetFrameworks()
{
return GetOrCreateBehavior().GetAvailableTargetFrameworks();
}
public virtual void UpgradeProject(CompilerVersion newVersion, TargetFramework newFramework)
{
if (!IsReadOnly)
GetOrCreateBehavior().UpgradeProject(newVersion, newFramework);
}
public static FileName GetAppConfigFile(IProject project, bool createIfNotExists)
{
FileName appConfigFileName = Core.FileName.Create(Path.Combine(project.Directory, "app.config"));
if (!File.Exists(appConfigFileName)) {
if (createIfNotExists) {
File.WriteAllText(appConfigFileName,
"<?xml version=\"1.0\"?>" + Environment.NewLine +
"<configuration>" + Environment.NewLine
+ "</configuration>");
} else {
return null;
}
}
if (!project.IsFileInProject(appConfigFileName)) {
FileProjectItem fpi = new FileProjectItem(project, ItemType.None, "app.config");
ProjectService.AddProjectItem(project, fpi);
FileService.FireFileCreated(appConfigFileName, false);
ProjectBrowserPad.RefreshViewAsync();
}
return appConfigFileName;
}
#endregion
#region Type System
volatile ProjectContentContainer projectContentContainer;
ITypeDefinitionModelCollection typeDefinitionModels;
protected void InitializeProjectContent(IProjectContent initialProjectContent)
{
lock (SyncRoot) {
if (projectContentContainer != null)
throw new InvalidOperationException("Already initialized.");
projectContentContainer = new ProjectContentContainer(this, initialProjectContent);
projectContentContainer.SetCompilerSettings(CreateCompilerSettings());
}
}
protected virtual object CreateCompilerSettings()
{
return null;
}
public override IProjectContent ProjectContent {
get {
var c = projectContentContainer;
return c != null ? c.ProjectContent : null;
}
}
public override ITypeDefinitionModelCollection TypeDefinitionModels {
get {
SD.MainThread.VerifyAccess();
if (typeDefinitionModels == null) {
typeDefinitionModels = SD.GetRequiredService<IModelFactory>().CreateTopLevelTypeDefinitionCollection(new ProjectEntityModelContext(this, ".cs"));
var pc = ProjectContent;
if (pc != null) {
// Add the already loaded files into the model
foreach (var file in pc.Files) {
typeDefinitionModels.Update(null, file);
}
}
}
return typeDefinitionModels;
}
}
public override void OnParseInformationUpdated(ParseInformationEventArgs args)
{
var c = projectContentContainer;
if (c != null)
c.ParseInformationUpdated(args.OldUnresolvedFile, args.NewUnresolvedFile);
// OnParseInformationUpdated is called inside a lock, but we don't want to raise the event inside that lock.
// To ensure events are raised in the same order, we always invoke on the main thread.
SD.MainThread.InvokeAsyncAndForget(delegate {
if (typeDefinitionModels != null) {
typeDefinitionModels.Update(args.OldUnresolvedFile, args.NewUnresolvedFile);
}
ParseInformationUpdated(null, args);
});
}
public override event EventHandler<ParseInformationEventArgs> ParseInformationUpdated = delegate {};
#endregion
}
}