1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
5
using System.Collections.Generic;
7
using System.Text.RegularExpressions;
9
using ICSharpCode.Core;
10
using Microsoft.Build.Construction;
11
using Microsoft.Build.Execution;
12
using Microsoft.Build.Framework;
13
using MSBuild = Microsoft.Build;
14
using ProjectCollection = Microsoft.Build.Evaluation.ProjectCollection;
16
namespace ICSharpCode.SharpDevelop.Project
19
/// Messing with MSBuild's internals.
21
public static class MSBuildInternals
24
/// SharpDevelop uses one project collection per solution.
25
/// Code accessing one of those collection (even if indirectly through MSBuild) should lock on
26
/// MSBuildInternals.SolutionProjectCollectionLock.
28
public readonly static object SolutionProjectCollectionLock = new object();
30
// TODO: I think MSBuild actually uses OrdinalIgnoreCase. SharpDevelop 3.x just used string.operator ==, so I'm keeping
31
// that setting until all code is ported to use PropertyNameComparer and we've verified what MSBuild is actually using.
32
public readonly static StringComparer PropertyNameComparer = StringComparer.Ordinal;
34
internal static void UnloadProject(MSBuild.Evaluation.ProjectCollection projectCollection, MSBuild.Evaluation.Project project)
36
lock (SolutionProjectCollectionLock) {
37
projectCollection.UnloadProject(project);
41
internal static MSBuild.Evaluation.Project LoadProject(MSBuild.Evaluation.ProjectCollection projectCollection, ProjectRootElement rootElement, IDictionary<string, string> globalProps)
43
lock (SolutionProjectCollectionLock) {
44
string toolsVersion = rootElement.ToolsVersion;
45
if (string.IsNullOrEmpty(toolsVersion))
46
toolsVersion = projectCollection.DefaultToolsVersion;
47
return new MSBuild.Evaluation.Project(rootElement, globalProps, toolsVersion, projectCollection);
51
internal static ProjectInstance LoadProjectInstance(MSBuild.Evaluation.ProjectCollection projectCollection, ProjectRootElement rootElement, IDictionary<string, string> globalProps)
53
lock (SolutionProjectCollectionLock) {
54
string toolsVersion = rootElement.ToolsVersion;
55
if (string.IsNullOrEmpty(toolsVersion))
56
toolsVersion = projectCollection.DefaultToolsVersion;
57
return new ProjectInstance(rootElement, globalProps, toolsVersion, projectCollection);
61
public const string MSBuildXmlNamespace = "http://schemas.microsoft.com/developer/msbuild/2003";
65
/// Escapes special MSBuild characters ( '%', '*', '?', '@', '$', '(', ')', ';', "'" ).
67
public static string Escape(string text)
69
return MSBuild.Evaluation.ProjectCollection.Escape(text);
73
/// Unescapes escaped MSBuild characters.
75
public static string Unescape(string text)
77
return MSBuild.Evaluation.ProjectCollection.Unescape(text);
82
/// This is a special case in MSBuild we need to take care of.
84
public static string FixPlatformNameForProject(string platformName)
86
if (platformName == "Any CPU") {
94
/// This is a special case in MSBuild we need to take care of.
95
/// Opposite of FixPlatformNameForProject
97
public static string FixPlatformNameForSolution(string platformName)
99
if (platformName == "AnyCPU") {
106
internal static PropertyStorageLocations GetLocationFromCondition(MSBuild.Construction.ProjectElement element)
108
while (element != null) {
109
if (!string.IsNullOrEmpty(element.Condition))
110
return GetLocationFromCondition(element.Condition);
111
element = element.Parent;
113
return PropertyStorageLocations.Base;
116
internal static PropertyStorageLocations GetLocationFromCondition(string condition)
118
if (string.IsNullOrEmpty(condition)) {
119
return PropertyStorageLocations.Base;
121
PropertyStorageLocations location = 0; // 0 is unknown
122
if (condition.Contains("$(Configuration)"))
123
location |= PropertyStorageLocations.ConfigurationSpecific;
124
if (condition.Contains("$(Platform)"))
125
location |= PropertyStorageLocations.PlatformSpecific;
129
readonly static Regex configurationRegEx = new Regex(@"'(?<property>[^']*)'\s*==\s*'(?<value>[^']*)'", RegexOptions.Compiled);
131
internal static void GetConfigurationAndPlatformFromCondition(string condition,
132
out string configuration,
135
Match match = configurationRegEx.Match(condition);
137
string conditionProperty = match.Result("${property}");
138
string conditionValue = match.Result("${value}");
139
if (conditionProperty == "$(Configuration)|$(Platform)") {
140
// configuration is ok
141
configuration = MSBuildBasedProject.GetConfigurationNameFromKey(conditionValue);
142
platform = MSBuildBasedProject.GetPlatformNameFromKey(conditionValue);
143
} else if (conditionProperty == "$(Configuration)") {
144
configuration = conditionValue;
146
} else if (conditionProperty == "$(Platform)") {
147
configuration = null;
148
platform = conditionValue;
150
configuration = null;
154
configuration = null;
160
/// Resolves the location of the reference files.
162
/// <param name="baseProject">The base project.</param>
163
/// <param name="referenceReplacements">A different set of references to use instead of those in the project.
164
/// Used by the GacReferencePanel.</param>
165
internal static void ResolveAssemblyReferences(MSBuildBasedProject baseProject, ReferenceProjectItem[] referenceReplacements)
167
ProjectInstance project = baseProject.CreateProjectInstance();
168
project.SetProperty("BuildingProject", "false");
170
List<ProjectItemInstance> references = (
171
from item in project.Items
172
where ItemType.ReferenceItemTypes.Contains(new ItemType(item.ItemType))
176
ReferenceProjectItem[] referenceProjectItems;
178
if (referenceReplacements == null) {
179
// Remove the "Private" meta data.
180
// This is necessary to detect the default value for "Private"
181
foreach (ProjectItemInstance reference in references) {
182
reference.RemoveMetadata("Private");
185
referenceProjectItems = baseProject.Items.OfType<ReferenceProjectItem>().ToArray();
187
foreach (ProjectItemInstance reference in references) {
188
project.RemoveItem(reference);
190
foreach (ReferenceProjectItem item in referenceReplacements) {
191
project.AddItem("Reference", item.Include);
193
referenceProjectItems = referenceReplacements;
196
string[] targets = { "ResolveAssemblyReferences" };
197
BuildRequestData requestData = new BuildRequestData(project, targets, new HostServices());
198
List<ILogger> loggers = new List<ILogger>();
199
if (referenceReplacements == null)
200
loggers.Add(new SimpleErrorLogger());
201
lock (SolutionProjectCollectionLock) {
202
BuildParameters parameters = new BuildParameters(baseProject.MSBuildProjectCollection);
203
parameters.Loggers = loggers;
205
LoggingService.Debug("Started build for ResolveAssemblyReferences");
206
BuildResult result = BuildManager.DefaultBuildManager.Build(parameters, requestData);
208
throw new InvalidOperationException("BuildResult is null");
209
LoggingService.Debug("Build for ResolveAssemblyReferences finished: " + result.OverallResult);
212
var referenceDict = new Dictionary<string, ReferenceProjectItem>();
213
foreach (ReferenceProjectItem item in referenceProjectItems) {
214
// references could be duplicate, so we cannot use referenceDict.Add or reference.ToDictionary
215
referenceDict[item.Include] = item;
219
foreach (ProjectItemInstance item in project.GetItems("_ResolveAssemblyReferenceResolvedFiles")) {
220
string originalInclude = item.GetMetadataValue("OriginalItemSpec");
221
ReferenceProjectItem reference;
222
if (referenceDict.TryGetValue(originalInclude, out reference)) {
223
reference.AssemblyName = new Dom.DomAssemblyName(item.GetMetadataValue("FusionName"));
224
//string fullPath = item.GetEvaluatedMetadata("FullPath"); is incorrect for relative paths
225
string fullPath = FileUtility.GetAbsolutePath(baseProject.Directory, item.GetMetadataValue("Identity"));
226
reference.FileName = fullPath;
227
reference.Redist = item.GetMetadataValue("Redist");
228
LoggingService.Debug("Got information about " + originalInclude + "; fullpath=" + fullPath);
229
reference.DefaultCopyLocalValue = bool.Parse(item.GetMetadataValue("CopyLocal"));
231
LoggingService.Warn("Unknown item " + originalInclude);
236
sealed class SimpleErrorLogger : ILogger
238
#region ILogger interface implementation
239
public LoggerVerbosity Verbosity { get; set; }
240
public string Parameters { get; set; }
242
public void Initialize(IEventSource eventSource)
244
eventSource.ErrorRaised += OnError;
245
eventSource.WarningRaised += OnWarning;
248
public void Shutdown()
253
void OnError(object sender, BuildErrorEventArgs e)
255
TaskService.BuildMessageViewCategory.AppendLine(e.Message);
258
void OnWarning(object sender, BuildWarningEventArgs e)
260
TaskService.BuildMessageViewCategory.AppendLine(e.Message);