3c1f479b9d
Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
1669 lines
50 KiB
C#
1669 lines
50 KiB
C#
//
|
|
// Project.cs: Project class
|
|
//
|
|
// Author:
|
|
// Marek Sieradzki (marek.sieradzki@gmail.com)
|
|
// Ankit Jain (jankit@novell.com)
|
|
//
|
|
// (C) 2005 Marek Sieradzki
|
|
// Copyright 2011 Novell, Inc (http://www.novell.com)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Xml;
|
|
using System.Xml.Schema;
|
|
using Microsoft.Build.Framework;
|
|
using Microsoft.Build.Utilities;
|
|
using Mono.XBuild.Framework;
|
|
using Mono.XBuild.CommandLine;
|
|
|
|
namespace Microsoft.Build.BuildEngine {
|
|
public class Project {
|
|
|
|
bool buildEnabled;
|
|
Dictionary <string, List <string>> conditionedProperties;
|
|
string[] defaultTargets;
|
|
Encoding encoding;
|
|
BuildItemGroup evaluatedItems;
|
|
BuildItemGroup evaluatedItemsIgnoringCondition;
|
|
Dictionary <string, BuildItemGroup> evaluatedItemsByName;
|
|
Dictionary <string, BuildItemGroup> evaluatedItemsByNameIgnoringCondition;
|
|
BuildPropertyGroup evaluatedProperties;
|
|
string firstTargetName;
|
|
string fullFileName;
|
|
BuildPropertyGroup globalProperties;
|
|
GroupingCollection groupingCollection;
|
|
bool isDirty;
|
|
bool isValidated;
|
|
BuildItemGroupCollection itemGroups;
|
|
ImportCollection imports;
|
|
List<string> initialTargets;
|
|
Dictionary <string, BuildItemGroup> last_item_group_containing;
|
|
bool needToReevaluate;
|
|
Engine parentEngine;
|
|
BuildPropertyGroupCollection propertyGroups;
|
|
string schemaFile;
|
|
TaskDatabase taskDatabase;
|
|
TargetCollection targets;
|
|
DateTime timeOfLastDirty;
|
|
UsingTaskCollection usingTasks;
|
|
XmlDocument xmlDocument;
|
|
bool unloaded;
|
|
bool initialTargetsBuilt;
|
|
bool building;
|
|
BuildSettings current_settings;
|
|
Stack<Batch> batches;
|
|
|
|
// This is used to keep track of "current" file,
|
|
// which is then used to set the reserved properties
|
|
// $(MSBuildThisFile*)
|
|
Stack<string> this_file_property_stack;
|
|
ProjectLoadSettings project_load_settings;
|
|
|
|
|
|
static string extensions_path;
|
|
static XmlNamespaceManager manager;
|
|
static string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
|
|
|
|
public Project ()
|
|
: this (Engine.GlobalEngine)
|
|
{
|
|
}
|
|
|
|
public Project (Engine engine) : this (engine, null)
|
|
{
|
|
}
|
|
|
|
public Project (Engine engine, string toolsVersion)
|
|
{
|
|
parentEngine = engine;
|
|
ToolsVersion = toolsVersion;
|
|
|
|
buildEnabled = ParentEngine.BuildEnabled;
|
|
xmlDocument = new XmlDocument ();
|
|
xmlDocument.PreserveWhitespace = false;
|
|
xmlDocument.AppendChild (xmlDocument.CreateElement ("Project", XmlNamespace));
|
|
xmlDocument.DocumentElement.SetAttribute ("xmlns", ns);
|
|
|
|
fullFileName = String.Empty;
|
|
timeOfLastDirty = DateTime.Now;
|
|
current_settings = BuildSettings.None;
|
|
project_load_settings = ProjectLoadSettings.None;
|
|
|
|
encoding = null;
|
|
|
|
initialTargets = new List<string> ();
|
|
defaultTargets = new string [0];
|
|
batches = new Stack<Batch> ();
|
|
this_file_property_stack = new Stack<string> ();
|
|
|
|
globalProperties = new BuildPropertyGroup (null, this, null, false);
|
|
foreach (BuildProperty bp in parentEngine.GlobalProperties)
|
|
GlobalProperties.AddProperty (bp.Clone (true));
|
|
|
|
ProcessXml ();
|
|
|
|
}
|
|
|
|
[MonoTODO ("Not tested")]
|
|
public void AddNewImport (string importLocation,
|
|
string importCondition)
|
|
{
|
|
if (importLocation == null)
|
|
throw new ArgumentNullException ("importLocation");
|
|
|
|
XmlElement importElement = xmlDocument.CreateElement ("Import", XmlNamespace);
|
|
xmlDocument.DocumentElement.AppendChild (importElement);
|
|
importElement.SetAttribute ("Project", importLocation);
|
|
if (!String.IsNullOrEmpty (importCondition))
|
|
importElement.SetAttribute ("Condition", importCondition);
|
|
|
|
AddImport (importElement, null, false);
|
|
MarkProjectAsDirty ();
|
|
NeedToReevaluate ();
|
|
}
|
|
|
|
public BuildItem AddNewItem (string itemName,
|
|
string itemInclude)
|
|
{
|
|
return AddNewItem (itemName, itemInclude, false);
|
|
}
|
|
|
|
[MonoTODO ("Adds item not in the same place as MS")]
|
|
public BuildItem AddNewItem (string itemName,
|
|
string itemInclude,
|
|
bool treatItemIncludeAsLiteral)
|
|
{
|
|
BuildItemGroup big;
|
|
|
|
if (itemGroups.Count == 0)
|
|
big = AddNewItemGroup ();
|
|
else {
|
|
if (last_item_group_containing.ContainsKey (itemName)) {
|
|
big = last_item_group_containing [itemName];
|
|
} else {
|
|
// FIXME: not tested
|
|
BuildItemGroup [] groups = new BuildItemGroup [itemGroups.Count];
|
|
itemGroups.CopyTo (groups, 0);
|
|
big = groups [0];
|
|
}
|
|
}
|
|
|
|
BuildItem item = big.AddNewItem (itemName, itemInclude, treatItemIncludeAsLiteral);
|
|
|
|
MarkProjectAsDirty ();
|
|
NeedToReevaluate ();
|
|
|
|
return item;
|
|
}
|
|
|
|
[MonoTODO ("Not tested")]
|
|
public BuildItemGroup AddNewItemGroup ()
|
|
{
|
|
XmlElement element = xmlDocument.CreateElement ("ItemGroup", XmlNamespace);
|
|
xmlDocument.DocumentElement.AppendChild (element);
|
|
|
|
BuildItemGroup big = new BuildItemGroup (element, this, null, false);
|
|
itemGroups.Add (big);
|
|
MarkProjectAsDirty ();
|
|
NeedToReevaluate ();
|
|
|
|
return big;
|
|
}
|
|
|
|
[MonoTODO ("Ignores insertAtEndOfProject")]
|
|
public BuildPropertyGroup AddNewPropertyGroup (bool insertAtEndOfProject)
|
|
{
|
|
XmlElement element = xmlDocument.CreateElement ("PropertyGroup", XmlNamespace);
|
|
xmlDocument.DocumentElement.AppendChild (element);
|
|
|
|
BuildPropertyGroup bpg = new BuildPropertyGroup (element, this, null, false);
|
|
propertyGroups.Add (bpg);
|
|
MarkProjectAsDirty ();
|
|
NeedToReevaluate ();
|
|
|
|
return bpg;
|
|
}
|
|
|
|
[MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
|
|
public void AddNewUsingTaskFromAssemblyFile (string taskName,
|
|
string assemblyFile)
|
|
{
|
|
if (taskName == null)
|
|
throw new ArgumentNullException ("taskName");
|
|
if (assemblyFile == null)
|
|
throw new ArgumentNullException ("assemblyFile");
|
|
|
|
XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
|
|
xmlDocument.DocumentElement.AppendChild (element);
|
|
element.SetAttribute ("TaskName", taskName);
|
|
element.SetAttribute ("AssemblyFile", assemblyFile);
|
|
|
|
UsingTask ut = new UsingTask (element, this, null);
|
|
usingTasks.Add (ut);
|
|
MarkProjectAsDirty ();
|
|
}
|
|
|
|
[MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
|
|
public void AddNewUsingTaskFromAssemblyName (string taskName,
|
|
string assemblyName)
|
|
{
|
|
if (taskName == null)
|
|
throw new ArgumentNullException ("taskName");
|
|
if (assemblyName == null)
|
|
throw new ArgumentNullException ("assemblyName");
|
|
|
|
XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
|
|
xmlDocument.DocumentElement.AppendChild (element);
|
|
element.SetAttribute ("TaskName", taskName);
|
|
element.SetAttribute ("AssemblyName", assemblyName);
|
|
|
|
UsingTask ut = new UsingTask (element, this, null);
|
|
usingTasks.Add (ut);
|
|
MarkProjectAsDirty ();
|
|
}
|
|
|
|
[MonoTODO ("Not tested")]
|
|
public bool Build ()
|
|
{
|
|
return Build (new string [0]);
|
|
}
|
|
|
|
[MonoTODO ("Not tested")]
|
|
public bool Build (string targetName)
|
|
{
|
|
if (targetName == null)
|
|
return Build ((string[]) null);
|
|
else
|
|
return Build (new string [1] { targetName });
|
|
}
|
|
|
|
[MonoTODO ("Not tested")]
|
|
public bool Build (string [] targetNames)
|
|
{
|
|
return Build (targetNames, null);
|
|
}
|
|
|
|
[MonoTODO ("Not tested")]
|
|
public bool Build (string [] targetNames,
|
|
IDictionary targetOutputs)
|
|
{
|
|
return Build (targetNames, targetOutputs, BuildSettings.None);
|
|
}
|
|
|
|
[MonoTODO ("Not tested")]
|
|
public bool Build (string [] targetNames,
|
|
IDictionary targetOutputs,
|
|
BuildSettings buildFlags)
|
|
|
|
{
|
|
bool result = false;
|
|
ParentEngine.StartProjectBuild (this, targetNames);
|
|
|
|
// Invoking this to emit a warning in case of unsupported
|
|
// ToolsVersion
|
|
GetToolsVersionToUse (true);
|
|
|
|
string current_directory = Environment.CurrentDirectory;
|
|
try {
|
|
current_settings = buildFlags;
|
|
if (!String.IsNullOrEmpty (fullFileName))
|
|
Directory.SetCurrentDirectory (Path.GetDirectoryName (fullFileName));
|
|
building = true;
|
|
result = BuildInternal (targetNames, targetOutputs, buildFlags);
|
|
} catch (InvalidProjectFileException ie) {
|
|
ParentEngine.LogErrorWithFilename (fullFileName, ie.Message);
|
|
ParentEngine.LogMessage (MessageImportance.Low, String.Format ("{0}: {1}", fullFileName, ie.ToString ()));
|
|
} catch (Exception e) {
|
|
ParentEngine.LogErrorWithFilename (fullFileName, e.Message);
|
|
ParentEngine.LogMessage (MessageImportance.Low, String.Format ("{0}: {1}", fullFileName, e.ToString ()));
|
|
throw;
|
|
} finally {
|
|
ParentEngine.EndProjectBuild (this, result);
|
|
current_settings = BuildSettings.None;
|
|
Directory.SetCurrentDirectory (current_directory);
|
|
building = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool BuildInternal (string [] targetNames,
|
|
IDictionary targetOutputs,
|
|
BuildSettings buildFlags)
|
|
{
|
|
CheckUnloaded ();
|
|
if (buildFlags == BuildSettings.None) {
|
|
needToReevaluate = false;
|
|
Reevaluate ();
|
|
}
|
|
|
|
ProcessBeforeAndAfterTargets ();
|
|
|
|
if (targetNames == null || targetNames.Length == 0) {
|
|
if (defaultTargets != null && defaultTargets.Length != 0) {
|
|
targetNames = defaultTargets;
|
|
} else if (firstTargetName != null) {
|
|
targetNames = new string [1] { firstTargetName};
|
|
} else {
|
|
if (targets == null || targets.Count == 0) {
|
|
LogError (fullFileName, "No target found in the project");
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!initialTargetsBuilt) {
|
|
foreach (string target in initialTargets) {
|
|
if (!BuildTarget (target.Trim (), targetOutputs))
|
|
return false;
|
|
}
|
|
initialTargetsBuilt = true;
|
|
}
|
|
|
|
foreach (string target in targetNames) {
|
|
if (target == null)
|
|
throw new ArgumentNullException ("Target name cannot be null");
|
|
|
|
if (!BuildTarget (target.Trim (), targetOutputs))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BuildTarget (string target_name, IDictionary targetOutputs)
|
|
{
|
|
if (target_name == null)
|
|
throw new ArgumentException ("targetNames cannot contain null strings");
|
|
|
|
if (!targets.Exists (target_name)) {
|
|
LogError (fullFileName, "Target named '{0}' not found in the project.", target_name);
|
|
return false;
|
|
}
|
|
|
|
string key = GetKeyForTarget (target_name);
|
|
if (!targets [target_name].Build (key))
|
|
return false;
|
|
|
|
ITaskItem[] outputs;
|
|
if (ParentEngine.BuiltTargetsOutputByName.TryGetValue (key, out outputs)) {
|
|
if (targetOutputs != null)
|
|
targetOutputs.Add (target_name, outputs);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
internal string GetKeyForTarget (string target_name)
|
|
{
|
|
return GetKeyForTarget (target_name, true);
|
|
}
|
|
|
|
internal string GetKeyForTarget (string target_name, bool include_global_properties)
|
|
{
|
|
// target name is case insensitive
|
|
return fullFileName + ":" + target_name.ToLowerInvariant () +
|
|
(include_global_properties ? (":" + GlobalPropertiesToString (GlobalProperties))
|
|
: String.Empty);
|
|
}
|
|
|
|
string GlobalPropertiesToString (BuildPropertyGroup bgp)
|
|
{
|
|
StringBuilder sb = new StringBuilder ();
|
|
foreach (BuildProperty bp in bgp)
|
|
sb.AppendFormat (" {0}:{1}", bp.Name, bp.FinalValue);
|
|
return sb.ToString ();
|
|
}
|
|
|
|
void ProcessBeforeAndAfterTargets ()
|
|
{
|
|
var beforeTable = Targets.AsIEnumerable ()
|
|
.SelectMany (target => GetTargetNamesFromString (target.BeforeTargets),
|
|
(target, before_target) => new {before_target, name = target.Name})
|
|
.ToLookup (x => x.before_target, x => x.name)
|
|
.ToDictionary (x => x.Key, x => x.Distinct ().ToList ());
|
|
|
|
foreach (var pair in beforeTable) {
|
|
if (targets.Exists (pair.Key))
|
|
targets [pair.Key].BeforeThisTargets = pair.Value;
|
|
else
|
|
LogWarning (FullFileName, "Target '{0}', not found in the project", pair.Key);
|
|
}
|
|
|
|
var afterTable = Targets.AsIEnumerable ()
|
|
.SelectMany (target => GetTargetNamesFromString (target.AfterTargets),
|
|
(target, after_target) => new {after_target, name = target.Name})
|
|
.ToLookup (x => x.after_target, x => x.name)
|
|
.ToDictionary (x => x.Key, x => x.Distinct ().ToList ());
|
|
|
|
foreach (var pair in afterTable) {
|
|
if (targets.Exists (pair.Key))
|
|
targets [pair.Key].AfterThisTargets = pair.Value;
|
|
else
|
|
LogWarning (FullFileName, "Target '{0}', not found in the project", pair.Key);
|
|
}
|
|
}
|
|
|
|
string[] GetTargetNamesFromString (string targets)
|
|
{
|
|
Expression expr = new Expression ();
|
|
expr.Parse (targets, ParseOptions.AllowItemsNoMetadataAndSplit);
|
|
return (string []) expr.ConvertTo (this, typeof (string []));
|
|
}
|
|
|
|
[MonoTODO]
|
|
public string [] GetConditionedPropertyValues (string propertyName)
|
|
{
|
|
if (conditionedProperties.ContainsKey (propertyName))
|
|
return conditionedProperties [propertyName].ToArray ();
|
|
else
|
|
return new string [0];
|
|
}
|
|
|
|
public BuildItemGroup GetEvaluatedItemsByName (string itemName)
|
|
{
|
|
if (needToReevaluate) {
|
|
needToReevaluate = false;
|
|
Reevaluate ();
|
|
}
|
|
|
|
if (evaluatedItemsByName.ContainsKey (itemName))
|
|
return evaluatedItemsByName [itemName];
|
|
else
|
|
return new BuildItemGroup (this);
|
|
}
|
|
|
|
public BuildItemGroup GetEvaluatedItemsByNameIgnoringCondition (string itemName)
|
|
{
|
|
if (needToReevaluate) {
|
|
needToReevaluate = false;
|
|
Reevaluate ();
|
|
}
|
|
|
|
if (evaluatedItemsByNameIgnoringCondition.ContainsKey (itemName))
|
|
return evaluatedItemsByNameIgnoringCondition [itemName];
|
|
else
|
|
return new BuildItemGroup (this);
|
|
}
|
|
|
|
public string GetEvaluatedProperty (string propertyName)
|
|
{
|
|
if (needToReevaluate) {
|
|
needToReevaluate = false;
|
|
Reevaluate ();
|
|
}
|
|
|
|
if (propertyName == null)
|
|
throw new ArgumentNullException ("propertyName");
|
|
|
|
BuildProperty bp = evaluatedProperties [propertyName];
|
|
|
|
return bp == null ? null : (string) bp;
|
|
}
|
|
|
|
[MonoTODO ("We should remember that node and not use XPath to get it")]
|
|
public string GetProjectExtensions (string id)
|
|
{
|
|
if (id == null || id == String.Empty)
|
|
return String.Empty;
|
|
|
|
XmlNode node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
|
|
|
|
if (node == null)
|
|
return String.Empty;
|
|
else
|
|
return node.InnerXml;
|
|
}
|
|
|
|
|
|
public void Load (string projectFileName)
|
|
{
|
|
Load (projectFileName, ProjectLoadSettings.None);
|
|
}
|
|
|
|
public void Load (string projectFileName, ProjectLoadSettings settings)
|
|
{
|
|
project_load_settings = settings;
|
|
if (String.IsNullOrEmpty (projectFileName))
|
|
throw new ArgumentNullException ("projectFileName");
|
|
|
|
if (!File.Exists (projectFileName))
|
|
throw new ArgumentException (String.Format ("Project file {0} not found", projectFileName),
|
|
"projectFileName");
|
|
|
|
this.fullFileName = Utilities.FromMSBuildPath (Path.GetFullPath (projectFileName));
|
|
PushThisFileProperty (fullFileName);
|
|
|
|
string filename = fullFileName;
|
|
if (String.Compare (Path.GetExtension (fullFileName), ".sln", true) == 0) {
|
|
Project tmp_project = ParentEngine.CreateNewProject ();
|
|
tmp_project.FullFileName = filename;
|
|
SolutionParser sln_parser = new SolutionParser ();
|
|
sln_parser.ParseSolution (fullFileName, tmp_project, delegate (int errorNumber, string message) {
|
|
LogWarning (filename, message);
|
|
});
|
|
filename = fullFileName + ".proj";
|
|
try {
|
|
tmp_project.Save (filename);
|
|
ParentEngine.RemoveLoadedProject (tmp_project);
|
|
DoLoad (new StreamReader (filename));
|
|
} finally {
|
|
if (Environment.GetEnvironmentVariable ("XBUILD_EMIT_SOLUTION") == null)
|
|
File.Delete (filename);
|
|
}
|
|
} else {
|
|
DoLoad (new StreamReader (filename));
|
|
}
|
|
}
|
|
|
|
[MonoTODO ("Not tested")]
|
|
public void Load (TextReader textReader)
|
|
{
|
|
Load (textReader, ProjectLoadSettings.None);
|
|
}
|
|
|
|
public void Load (TextReader textReader, ProjectLoadSettings projectLoadSettings)
|
|
{
|
|
project_load_settings = projectLoadSettings;
|
|
if (!string.IsNullOrEmpty (fullFileName))
|
|
PushThisFileProperty (fullFileName);
|
|
DoLoad (textReader);
|
|
}
|
|
|
|
public void LoadXml (string projectXml)
|
|
{
|
|
LoadXml (projectXml, ProjectLoadSettings.None);
|
|
}
|
|
|
|
public void LoadXml (string projectXml, ProjectLoadSettings projectLoadSettings)
|
|
{
|
|
project_load_settings = projectLoadSettings;
|
|
if (!string.IsNullOrEmpty (fullFileName))
|
|
PushThisFileProperty (fullFileName);
|
|
DoLoad (new StringReader (projectXml));
|
|
MarkProjectAsDirty ();
|
|
}
|
|
|
|
|
|
public void MarkProjectAsDirty ()
|
|
{
|
|
isDirty = true;
|
|
timeOfLastDirty = DateTime.Now;
|
|
}
|
|
|
|
[MonoTODO ("Not tested")]
|
|
public void RemoveAllItemGroups ()
|
|
{
|
|
int length = ItemGroups.Count;
|
|
BuildItemGroup [] groups = new BuildItemGroup [length];
|
|
ItemGroups.CopyTo (groups, 0);
|
|
|
|
for (int i = 0; i < length; i++)
|
|
RemoveItemGroup (groups [i]);
|
|
|
|
MarkProjectAsDirty ();
|
|
NeedToReevaluate ();
|
|
}
|
|
|
|
[MonoTODO ("Not tested")]
|
|
public void RemoveAllPropertyGroups ()
|
|
{
|
|
int length = PropertyGroups.Count;
|
|
BuildPropertyGroup [] groups = new BuildPropertyGroup [length];
|
|
PropertyGroups.CopyTo (groups, 0);
|
|
|
|
for (int i = 0; i < length; i++)
|
|
RemovePropertyGroup (groups [i]);
|
|
|
|
MarkProjectAsDirty ();
|
|
NeedToReevaluate ();
|
|
}
|
|
|
|
[MonoTODO]
|
|
public void RemoveItem (BuildItem itemToRemove)
|
|
{
|
|
if (itemToRemove == null)
|
|
throw new ArgumentNullException ("itemToRemove");
|
|
|
|
if (!itemToRemove.FromXml && !itemToRemove.HasParentItem)
|
|
throw new InvalidOperationException ("The object passed in is not part of the project.");
|
|
|
|
BuildItemGroup big = itemToRemove.ParentItemGroup;
|
|
|
|
if (big.Count == 1) {
|
|
// ParentItemGroup for items from xml and that have parent is the same
|
|
groupingCollection.Remove (big);
|
|
} else {
|
|
if (big.ParentProject != this)
|
|
throw new InvalidOperationException ("The object passed in is not part of the project.");
|
|
|
|
if (itemToRemove.FromXml)
|
|
big.RemoveItem (itemToRemove);
|
|
else
|
|
big.RemoveItem (itemToRemove.ParentItem);
|
|
}
|
|
|
|
MarkProjectAsDirty ();
|
|
NeedToReevaluate ();
|
|
}
|
|
|
|
[MonoTODO ("Not tested")]
|
|
public void RemoveItemGroup (BuildItemGroup itemGroupToRemove)
|
|
{
|
|
if (itemGroupToRemove == null)
|
|
throw new ArgumentNullException ("itemGroupToRemove");
|
|
|
|
groupingCollection.Remove (itemGroupToRemove);
|
|
MarkProjectAsDirty ();
|
|
}
|
|
|
|
[MonoTODO]
|
|
// NOTE: does not modify imported projects
|
|
public void RemoveItemGroupsWithMatchingCondition (string matchingCondition)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
[MonoTODO]
|
|
public void RemoveItemsByName (string itemName)
|
|
{
|
|
if (itemName == null)
|
|
throw new ArgumentNullException ("itemName");
|
|
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
[MonoTODO ("Not tested")]
|
|
public void RemovePropertyGroup (BuildPropertyGroup propertyGroupToRemove)
|
|
{
|
|
if (propertyGroupToRemove == null)
|
|
throw new ArgumentNullException ("propertyGroupToRemove");
|
|
|
|
groupingCollection.Remove (propertyGroupToRemove);
|
|
MarkProjectAsDirty ();
|
|
}
|
|
|
|
[MonoTODO]
|
|
// NOTE: does not modify imported projects
|
|
public void RemovePropertyGroupsWithMatchingCondition (string matchCondition)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
[MonoTODO]
|
|
public void ResetBuildStatus ()
|
|
{
|
|
// hack to allow built targets to be removed
|
|
building = true;
|
|
Reevaluate ();
|
|
building = false;
|
|
}
|
|
|
|
public void Save (string projectFileName)
|
|
{
|
|
Save (projectFileName, Encoding.Default);
|
|
isDirty = false;
|
|
}
|
|
|
|
[MonoTODO ("Ignores encoding")]
|
|
public void Save (string projectFileName, Encoding encoding)
|
|
{
|
|
xmlDocument.Save (projectFileName);
|
|
isDirty = false;
|
|
}
|
|
|
|
public void Save (TextWriter outTextWriter)
|
|
{
|
|
xmlDocument.Save (outTextWriter);
|
|
isDirty = false;
|
|
}
|
|
|
|
public void SetImportedProperty (string propertyName,
|
|
string propertyValue,
|
|
string condition,
|
|
Project importProject)
|
|
{
|
|
SetImportedProperty (propertyName, propertyValue, condition, importProject,
|
|
PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
|
|
}
|
|
|
|
public void SetImportedProperty (string propertyName,
|
|
string propertyValue,
|
|
string condition,
|
|
Project importedProject,
|
|
PropertyPosition position)
|
|
{
|
|
SetImportedProperty (propertyName, propertyValue, condition, importedProject,
|
|
PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
|
|
}
|
|
|
|
[MonoTODO]
|
|
public void SetImportedProperty (string propertyName,
|
|
string propertyValue,
|
|
string condition,
|
|
Project importedProject,
|
|
PropertyPosition position,
|
|
bool treatPropertyValueAsLiteral)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
public void SetProjectExtensions (string id, string xmlText)
|
|
{
|
|
if (id == null)
|
|
throw new ArgumentNullException ("id");
|
|
if (xmlText == null)
|
|
throw new ArgumentNullException ("xmlText");
|
|
|
|
XmlNode projectExtensions, node;
|
|
|
|
projectExtensions = xmlDocument.SelectSingleNode ("/tns:Project/tns:ProjectExtensions", XmlNamespaceManager);
|
|
|
|
if (projectExtensions == null) {
|
|
projectExtensions = xmlDocument.CreateElement ("ProjectExtensions", XmlNamespace);
|
|
xmlDocument.DocumentElement.AppendChild (projectExtensions);
|
|
|
|
node = xmlDocument.CreateElement (id, XmlNamespace);
|
|
node.InnerXml = xmlText;
|
|
projectExtensions.AppendChild (node);
|
|
} else {
|
|
node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
|
|
|
|
if (node == null) {
|
|
node = xmlDocument.CreateElement (id, XmlNamespace);
|
|
projectExtensions.AppendChild (node);
|
|
}
|
|
|
|
node.InnerXml = xmlText;
|
|
|
|
}
|
|
|
|
MarkProjectAsDirty ();
|
|
}
|
|
|
|
public void SetProperty (string propertyName,
|
|
string propertyValue)
|
|
{
|
|
SetProperty (propertyName, propertyValue, "true",
|
|
PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
|
|
}
|
|
|
|
public void SetProperty (string propertyName,
|
|
string propertyValue,
|
|
string condition)
|
|
{
|
|
SetProperty (propertyName, propertyValue, condition,
|
|
PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
|
|
}
|
|
|
|
public void SetProperty (string propertyName,
|
|
string propertyValue,
|
|
string condition,
|
|
PropertyPosition position)
|
|
{
|
|
SetProperty (propertyName, propertyValue, condition,
|
|
PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
|
|
}
|
|
|
|
[MonoTODO]
|
|
public void SetProperty (string propertyName,
|
|
string propertyValue,
|
|
string condition,
|
|
PropertyPosition position,
|
|
bool treatPropertyValueAsLiteral)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
internal void Unload ()
|
|
{
|
|
unloaded = true;
|
|
}
|
|
|
|
internal void CheckUnloaded ()
|
|
{
|
|
if (unloaded)
|
|
throw new InvalidOperationException ("This project object has been unloaded from the MSBuild engine and is no longer valid.");
|
|
}
|
|
|
|
internal void NeedToReevaluate ()
|
|
{
|
|
needToReevaluate = true;
|
|
}
|
|
|
|
// Does the actual loading.
|
|
void DoLoad (TextReader textReader)
|
|
{
|
|
try {
|
|
ParentEngine.RemoveLoadedProject (this);
|
|
|
|
xmlDocument.Load (textReader);
|
|
|
|
if (xmlDocument.DocumentElement.Name == "VisualStudioProject")
|
|
throw new InvalidProjectFileException (String.Format (
|
|
"Project file '{0}' is a VS2003 project, which is not " +
|
|
"supported by xbuild. You need to convert it to msbuild " +
|
|
"format to build with xbuild.", fullFileName));
|
|
|
|
if (SchemaFile != null) {
|
|
xmlDocument.Schemas.Add (XmlSchema.Read (
|
|
new StreamReader (SchemaFile), ValidationCallBack));
|
|
xmlDocument.Validate (ValidationCallBack);
|
|
}
|
|
|
|
if (xmlDocument.DocumentElement.Name != "Project") {
|
|
throw new InvalidProjectFileException (String.Format (
|
|
"The element <{0}> is unrecognized, or not supported in this context.", xmlDocument.DocumentElement.Name));
|
|
}
|
|
|
|
if (xmlDocument.DocumentElement.GetAttribute ("xmlns") != ns) {
|
|
throw new InvalidProjectFileException (
|
|
@"The default XML namespace of the project must be the MSBuild XML namespace." +
|
|
" If the project is authored in the MSBuild 2003 format, please add " +
|
|
"xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" to the <Project> element. " +
|
|
"If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format. ");
|
|
}
|
|
ProcessXml ();
|
|
ParentEngine.AddLoadedProject (this);
|
|
} catch (Exception e) {
|
|
throw new InvalidProjectFileException (String.Format ("{0}: {1}",
|
|
fullFileName, e.Message), e);
|
|
} finally {
|
|
if (textReader != null)
|
|
textReader.Close ();
|
|
}
|
|
}
|
|
|
|
void Reevaluate ()
|
|
{
|
|
ProcessXml ();
|
|
}
|
|
|
|
void ProcessXml ()
|
|
{
|
|
groupingCollection = new GroupingCollection (this);
|
|
imports = new ImportCollection (groupingCollection);
|
|
usingTasks = new UsingTaskCollection (this);
|
|
itemGroups = new BuildItemGroupCollection (groupingCollection);
|
|
propertyGroups = new BuildPropertyGroupCollection (groupingCollection);
|
|
targets = new TargetCollection (this);
|
|
last_item_group_containing = new Dictionary <string, BuildItemGroup> ();
|
|
|
|
string effective_tools_version = GetToolsVersionToUse (false);
|
|
taskDatabase = new TaskDatabase ();
|
|
taskDatabase.CopyTasks (ParentEngine.GetDefaultTasks (effective_tools_version));
|
|
|
|
initialTargets = new List<string> ();
|
|
defaultTargets = new string [0];
|
|
PrepareForEvaluate (effective_tools_version);
|
|
ProcessElements (xmlDocument.DocumentElement, null);
|
|
|
|
isDirty = false;
|
|
Evaluate ();
|
|
}
|
|
|
|
void ProcessProjectAttributes (XmlAttributeCollection attributes)
|
|
{
|
|
foreach (XmlAttribute attr in attributes) {
|
|
switch (attr.Name) {
|
|
case "InitialTargets":
|
|
initialTargets.AddRange (attr.Value.Split (
|
|
new char [] {';', ' '},
|
|
StringSplitOptions.RemoveEmptyEntries));
|
|
break;
|
|
case "DefaultTargets":
|
|
// first non-empty DefaultTargets found is used
|
|
if (defaultTargets == null || defaultTargets.Length == 0)
|
|
defaultTargets = attr.Value.Split (new char [] {';', ' '},
|
|
StringSplitOptions.RemoveEmptyEntries);
|
|
EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets",
|
|
DefaultTargets, PropertyType.Reserved));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void ProcessElements (XmlElement rootElement, ImportedProject ip)
|
|
{
|
|
ProcessProjectAttributes (rootElement.Attributes);
|
|
foreach (XmlNode xn in rootElement.ChildNodes) {
|
|
if (xn is XmlElement) {
|
|
XmlElement xe = (XmlElement) xn;
|
|
switch (xe.Name) {
|
|
case "ProjectExtensions":
|
|
AddProjectExtensions (xe);
|
|
break;
|
|
case "Warning":
|
|
case "Message":
|
|
case "Error":
|
|
AddMessage (xe);
|
|
break;
|
|
case "Target":
|
|
AddTarget (xe, ip);
|
|
break;
|
|
case "UsingTask":
|
|
AddUsingTask (xe, ip);
|
|
break;
|
|
case "Import":
|
|
AddImport (xe, ip, true);
|
|
break;
|
|
case "ImportGroup":
|
|
AddImportGroup (xe, ip, true);
|
|
break;
|
|
case "ItemGroup":
|
|
AddItemGroup (xe, ip);
|
|
break;
|
|
case "PropertyGroup":
|
|
AddPropertyGroup (xe, ip);
|
|
break;
|
|
case "Choose":
|
|
AddChoose (xe, ip);
|
|
break;
|
|
case "ItemDefinitionGroup":
|
|
AddItemDefinitionGroup (xe);
|
|
break;
|
|
default:
|
|
var pf = ip == null ? null : string.Format (" '{0}'", ip.FullFileName);
|
|
throw new InvalidProjectFileException (String.Format ("Invalid element '{0}' in project file{1}.", xe.Name, pf));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PrepareForEvaluate (string effective_tools_version)
|
|
{
|
|
evaluatedItems = new BuildItemGroup (null, this, null, true);
|
|
evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this, null, true);
|
|
evaluatedItemsByName = new Dictionary <string, BuildItemGroup> (StringComparer.OrdinalIgnoreCase);
|
|
evaluatedItemsByNameIgnoringCondition = new Dictionary <string, BuildItemGroup> (StringComparer.OrdinalIgnoreCase);
|
|
if (building && current_settings == BuildSettings.None)
|
|
RemoveBuiltTargets ();
|
|
|
|
InitializeProperties (effective_tools_version);
|
|
}
|
|
|
|
void Evaluate ()
|
|
{
|
|
groupingCollection.Evaluate ();
|
|
|
|
//FIXME: UsingTasks aren't really evaluated. (shouldn't use expressions or anything)
|
|
foreach (UsingTask usingTask in UsingTasks)
|
|
usingTask.Evaluate ();
|
|
}
|
|
|
|
// Removes entries of all earlier built targets for this project
|
|
void RemoveBuiltTargets ()
|
|
{
|
|
ParentEngine.ClearBuiltTargetsForProject (this);
|
|
}
|
|
|
|
void InitializeProperties (string effective_tools_version)
|
|
{
|
|
BuildProperty bp;
|
|
|
|
evaluatedProperties = new BuildPropertyGroup (null, null, null, true);
|
|
conditionedProperties = new Dictionary<string, List<string>> ();
|
|
|
|
foreach (BuildProperty gp in GlobalProperties) {
|
|
bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
|
|
evaluatedProperties.AddProperty (bp);
|
|
}
|
|
|
|
foreach (BuildProperty gp in GlobalProperties)
|
|
ParentEngine.GlobalProperties.AddProperty (gp);
|
|
|
|
// add properties that we dont have from parent engine's
|
|
// global properties
|
|
foreach (BuildProperty gp in ParentEngine.GlobalProperties) {
|
|
if (evaluatedProperties [gp.Name] == null) {
|
|
bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
|
|
evaluatedProperties.AddProperty (bp);
|
|
}
|
|
}
|
|
|
|
foreach (DictionaryEntry de in Environment.GetEnvironmentVariables ()) {
|
|
bp = new BuildProperty ((string) de.Key, (string) de.Value, PropertyType.Environment);
|
|
evaluatedProperties.AddProperty (bp);
|
|
}
|
|
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFile", Path.GetFileName (fullFileName),
|
|
PropertyType.Reserved));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFullPath", fullFileName, PropertyType.Reserved));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectName",
|
|
Path.GetFileNameWithoutExtension (fullFileName),
|
|
PropertyType.Reserved));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectExtension",
|
|
Path.GetExtension (fullFileName),
|
|
PropertyType.Reserved));
|
|
|
|
string toolsPath = parentEngine.Toolsets [effective_tools_version].ToolsPath;
|
|
if (toolsPath == null)
|
|
throw new Exception (String.Format ("Invalid tools version '{0}', no tools path set for this.", effective_tools_version));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildBinPath", toolsPath, PropertyType.Reserved));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsPath", toolsPath, PropertyType.Reserved));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsRoot", Path.GetDirectoryName (toolsPath), PropertyType.Reserved));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsVersion", effective_tools_version, PropertyType.Reserved));
|
|
SetExtensionsPathProperties (DefaultExtensionsPath);
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets", DefaultTargets, PropertyType.Reserved));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("OS", OS, PropertyType.Environment));
|
|
#if XBUILD_12
|
|
// see http://msdn.microsoft.com/en-us/library/vstudio/hh162058(v=vs.120).aspx
|
|
if (effective_tools_version == "12.0") {
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsPath32", toolsPath, PropertyType.Reserved));
|
|
|
|
var frameworkToolsPath = ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version451);
|
|
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildFrameworkToolsPath", frameworkToolsPath, PropertyType.Reserved));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildFrameworkToolsPath32", frameworkToolsPath, PropertyType.Reserved));
|
|
}
|
|
#endif
|
|
// FIXME: make some internal method that will work like GetDirectoryName but output String.Empty on null/String.Empty
|
|
string projectDir;
|
|
if (FullFileName == String.Empty)
|
|
projectDir = Environment.CurrentDirectory;
|
|
else
|
|
projectDir = Path.GetDirectoryName (FullFileName);
|
|
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDirectory", projectDir, PropertyType.Reserved));
|
|
|
|
if (this_file_property_stack.Count > 0)
|
|
// Just re-inited the properties, but according to the stack,
|
|
// we should have a MSBuild*This* property set
|
|
SetMSBuildThisFileProperties (this_file_property_stack.Peek ());
|
|
}
|
|
|
|
internal void SetExtensionsPathProperties (string extn_path)
|
|
{
|
|
if (!String.IsNullOrEmpty (extn_path)) {
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath", extn_path, PropertyType.Reserved));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath32", extn_path, PropertyType.Reserved));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath64", extn_path, PropertyType.Reserved));
|
|
}
|
|
}
|
|
|
|
// precedence:
|
|
// ToolsVersion property
|
|
// ToolsVersion attribute on the project
|
|
// parentEngine's DefaultToolsVersion
|
|
string GetToolsVersionToUse (bool emitWarning)
|
|
{
|
|
if (!String.IsNullOrEmpty (ToolsVersion))
|
|
return ToolsVersion;
|
|
|
|
if (!HasToolsVersionAttribute)
|
|
return parentEngine.DefaultToolsVersion;
|
|
|
|
if (parentEngine.Toolsets [DefaultToolsVersion] == null) {
|
|
if (emitWarning)
|
|
LogWarning (FullFileName, "Project has unknown ToolsVersion '{0}'. Using the default tools version '{1}' instead.",
|
|
DefaultToolsVersion, parentEngine.DefaultToolsVersion);
|
|
return parentEngine.DefaultToolsVersion;
|
|
}
|
|
|
|
return DefaultToolsVersion;
|
|
}
|
|
|
|
void AddProjectExtensions (XmlElement xmlElement)
|
|
{
|
|
}
|
|
|
|
void AddMessage (XmlElement xmlElement)
|
|
{
|
|
}
|
|
|
|
void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
|
|
{
|
|
Target target = new Target (xmlElement, this, importedProject);
|
|
targets.AddTarget (target);
|
|
|
|
if (firstTargetName == null)
|
|
firstTargetName = target.Name;
|
|
}
|
|
|
|
void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
|
|
{
|
|
UsingTask usingTask;
|
|
|
|
usingTask = new UsingTask (xmlElement, this, importedProject);
|
|
UsingTasks.Add (usingTask);
|
|
}
|
|
|
|
void AddImport (XmlElement xmlElement, ImportedProject importingProject, bool evaluate_properties)
|
|
{
|
|
// eval all the properties etc till the import
|
|
if (evaluate_properties) {
|
|
groupingCollection.Evaluate (EvaluationType.Property | EvaluationType.Choose);
|
|
}
|
|
try {
|
|
PushThisFileProperty (importingProject != null ? importingProject.FullFileName : FullFileName);
|
|
|
|
string project_attribute = xmlElement.GetAttribute ("Project");
|
|
if (String.IsNullOrEmpty (project_attribute))
|
|
throw new InvalidProjectFileException ("The required attribute \"Project\" is missing from element <Import>.");
|
|
|
|
Import.ForEachExtensionPathTillFound (xmlElement, this, importingProject,
|
|
(importPath, from_source_msg) => AddSingleImport (xmlElement, importPath, importingProject, from_source_msg));
|
|
} finally {
|
|
PopThisFileProperty ();
|
|
}
|
|
}
|
|
|
|
void AddImportGroup (XmlElement xmlElement, ImportedProject importedProject, bool evaluate_properties)
|
|
{
|
|
// eval all the properties etc till the import group
|
|
if (evaluate_properties) {
|
|
groupingCollection.Evaluate (EvaluationType.Property | EvaluationType.Choose);
|
|
}
|
|
string condition_attribute = xmlElement.GetAttribute ("Condition");
|
|
if (!ConditionParser.ParseAndEvaluate (condition_attribute, this))
|
|
return;
|
|
foreach (XmlNode xn in xmlElement.ChildNodes) {
|
|
if (xn is XmlElement) {
|
|
XmlElement xe = (XmlElement) xn;
|
|
switch (xe.Name) {
|
|
case "Import":
|
|
AddImport (xe, importedProject, evaluate_properties);
|
|
break;
|
|
default:
|
|
throw new InvalidProjectFileException(String.Format("Invalid element '{0}' inside ImportGroup in project file '{1}'.", xe.Name, importedProject.FullFileName));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AddItemDefinitionGroup (XmlElement xmlElement)
|
|
{
|
|
string condition_attribute = xmlElement.GetAttribute ("Condition");
|
|
if (!ConditionParser.ParseAndEvaluate (condition_attribute, this))
|
|
return;
|
|
|
|
foreach (XmlNode xn in xmlElement.ChildNodes) {
|
|
// TODO: Add all nodes to some internal dictionary?
|
|
}
|
|
}
|
|
|
|
bool AddSingleImport (XmlElement xmlElement, string projectPath, ImportedProject importingProject, string from_source_msg)
|
|
{
|
|
Import import = new Import (xmlElement, projectPath, this, importingProject);
|
|
if (!ConditionParser.ParseAndEvaluate (import.Condition, this)) {
|
|
ParentEngine.LogMessage (MessageImportance.Low,
|
|
"Not importing project '{0}' as the condition '{1}' is false",
|
|
import.ProjectPath, import.Condition);
|
|
return false;
|
|
}
|
|
|
|
Import existingImport;
|
|
if (Imports.TryGetImport (import, out existingImport)) {
|
|
if (importingProject == null)
|
|
LogWarning (fullFileName,
|
|
"Cannot import project '{0}' again. It was already imported by " +
|
|
"'{1}'. Ignoring.",
|
|
projectPath, existingImport.ContainedInProjectFileName);
|
|
else
|
|
LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
|
|
"A circular reference was found involving the import of '{0}'. " +
|
|
"It was earlier imported by '{1}'. Only " +
|
|
"the first import of this file will be used, ignoring others.",
|
|
import.EvaluatedProjectPath, existingImport.ContainedInProjectFileName);
|
|
|
|
return true;
|
|
}
|
|
|
|
if (String.Compare (fullFileName, import.EvaluatedProjectPath) == 0) {
|
|
LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
|
|
"The main project file was imported here, which creates a circular " +
|
|
"reference. Ignoring this import.");
|
|
|
|
return true;
|
|
}
|
|
|
|
if (project_load_settings != ProjectLoadSettings.IgnoreMissingImports &&
|
|
!import.CheckEvaluatedProjectPathExists ())
|
|
return false;
|
|
|
|
Imports.Add (import);
|
|
string importingFile = importingProject != null ? importingProject.FullFileName : FullFileName;
|
|
ParentEngine.LogMessage (MessageImportance.Low,
|
|
"{0}: Importing project {1} {2}",
|
|
importingFile, import.EvaluatedProjectPath, from_source_msg);
|
|
|
|
import.Evaluate (project_load_settings == ProjectLoadSettings.IgnoreMissingImports);
|
|
return true;
|
|
}
|
|
|
|
void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
|
|
{
|
|
BuildItemGroup big = new BuildItemGroup (xmlElement, this, importedProject, false);
|
|
ItemGroups.Add (big);
|
|
}
|
|
|
|
void AddPropertyGroup (XmlElement xmlElement, ImportedProject importedProject)
|
|
{
|
|
BuildPropertyGroup bpg = new BuildPropertyGroup (xmlElement, this, importedProject, false);
|
|
PropertyGroups.Add (bpg);
|
|
}
|
|
|
|
void AddChoose (XmlElement xmlElement, ImportedProject importedProject)
|
|
{
|
|
BuildChoose bc = new BuildChoose (xmlElement, this, importedProject);
|
|
groupingCollection.Add (bc);
|
|
}
|
|
|
|
static void ValidationCallBack (object sender, ValidationEventArgs e)
|
|
{
|
|
Console.WriteLine ("Validation Error: {0}", e.Message);
|
|
}
|
|
|
|
public bool BuildEnabled {
|
|
get {
|
|
return buildEnabled;
|
|
}
|
|
set {
|
|
buildEnabled = value;
|
|
}
|
|
}
|
|
|
|
[MonoTODO]
|
|
public Encoding Encoding {
|
|
get { return encoding; }
|
|
}
|
|
|
|
public string DefaultTargets {
|
|
get {
|
|
return String.Join ("; ", defaultTargets);
|
|
}
|
|
set {
|
|
xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
|
|
if (value != null)
|
|
defaultTargets = value.Split (new char [] {';', ' '},
|
|
StringSplitOptions.RemoveEmptyEntries);
|
|
}
|
|
}
|
|
|
|
public BuildItemGroup EvaluatedItems {
|
|
get {
|
|
if (needToReevaluate) {
|
|
needToReevaluate = false;
|
|
Reevaluate ();
|
|
}
|
|
return evaluatedItems;
|
|
}
|
|
}
|
|
|
|
public BuildItemGroup EvaluatedItemsIgnoringCondition {
|
|
get {
|
|
if (needToReevaluate) {
|
|
needToReevaluate = false;
|
|
Reevaluate ();
|
|
}
|
|
return evaluatedItemsIgnoringCondition;
|
|
}
|
|
}
|
|
|
|
internal IDictionary <string, BuildItemGroup> EvaluatedItemsByName {
|
|
get {
|
|
// FIXME: do we need to do this here?
|
|
if (needToReevaluate) {
|
|
needToReevaluate = false;
|
|
Reevaluate ();
|
|
}
|
|
return evaluatedItemsByName;
|
|
}
|
|
}
|
|
|
|
internal IEnumerable EvaluatedItemsByNameAsDictionaryEntries {
|
|
get {
|
|
if (EvaluatedItemsByName.Count == 0)
|
|
yield break;
|
|
|
|
foreach (KeyValuePair<string, BuildItemGroup> pair in EvaluatedItemsByName) {
|
|
foreach (BuildItem bi in pair.Value)
|
|
yield return new DictionaryEntry (pair.Key, bi.ConvertToITaskItem (null, ExpressionOptions.ExpandItemRefs));
|
|
}
|
|
}
|
|
}
|
|
|
|
internal IDictionary <string, BuildItemGroup> EvaluatedItemsByNameIgnoringCondition {
|
|
get {
|
|
// FIXME: do we need to do this here?
|
|
if (needToReevaluate) {
|
|
needToReevaluate = false;
|
|
Reevaluate ();
|
|
}
|
|
return evaluatedItemsByNameIgnoringCondition;
|
|
}
|
|
}
|
|
|
|
// For batching implementation
|
|
Dictionary<string, BuildItemGroup> perBatchItemsByName;
|
|
Dictionary<string, BuildItemGroup> commonItemsByName;
|
|
|
|
struct Batch {
|
|
public Dictionary<string, BuildItemGroup> perBatchItemsByName;
|
|
public Dictionary<string, BuildItemGroup> commonItemsByName;
|
|
|
|
public Batch (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
|
|
{
|
|
this.perBatchItemsByName = perBatchItemsByName;
|
|
this.commonItemsByName = commonItemsByName;
|
|
}
|
|
}
|
|
|
|
Stack<Batch> Batches {
|
|
get { return batches; }
|
|
}
|
|
|
|
internal void PushBatch (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
|
|
{
|
|
batches.Push (new Batch (perBatchItemsByName, commonItemsByName));
|
|
SetBatchedItems (perBatchItemsByName, commonItemsByName);
|
|
}
|
|
|
|
internal void PopBatch ()
|
|
{
|
|
batches.Pop ();
|
|
if (batches.Count > 0) {
|
|
Batch b = batches.Peek ();
|
|
SetBatchedItems (b.perBatchItemsByName, b.commonItemsByName);
|
|
} else {
|
|
SetBatchedItems (null, null);
|
|
}
|
|
}
|
|
|
|
void SetBatchedItems (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
|
|
{
|
|
this.perBatchItemsByName = perBatchItemsByName;
|
|
this.commonItemsByName = commonItemsByName;
|
|
}
|
|
|
|
// Honors batching
|
|
internal bool TryGetEvaluatedItemByNameBatched (string itemName, out BuildItemGroup group)
|
|
{
|
|
if (perBatchItemsByName != null && perBatchItemsByName.TryGetValue (itemName, out group))
|
|
return true;
|
|
|
|
if (commonItemsByName != null && commonItemsByName.TryGetValue (itemName, out group))
|
|
return true;
|
|
|
|
group = null;
|
|
return EvaluatedItemsByName.TryGetValue (itemName, out group);
|
|
}
|
|
|
|
internal string GetMetadataBatched (string itemName, string metadataName)
|
|
{
|
|
BuildItemGroup group = null;
|
|
if (itemName == null) {
|
|
//unqualified, all items in a batch(bucket) have the
|
|
//same metadata values
|
|
group = GetFirst<BuildItemGroup> (perBatchItemsByName.Values);
|
|
if (group == null)
|
|
group = GetFirst<BuildItemGroup> (commonItemsByName.Values);
|
|
} else {
|
|
//qualified
|
|
TryGetEvaluatedItemByNameBatched (itemName, out group);
|
|
}
|
|
|
|
if (group != null) {
|
|
foreach (BuildItem item in group) {
|
|
if (item.HasMetadata (metadataName))
|
|
return item.GetEvaluatedMetadata (metadataName);
|
|
}
|
|
}
|
|
return String.Empty;
|
|
}
|
|
|
|
internal IEnumerable<BuildItemGroup> GetAllItemGroups ()
|
|
{
|
|
if (perBatchItemsByName == null && commonItemsByName == null)
|
|
foreach (BuildItemGroup group in EvaluatedItemsByName.Values)
|
|
yield return group;
|
|
|
|
if (perBatchItemsByName != null)
|
|
foreach (BuildItemGroup group in perBatchItemsByName.Values)
|
|
yield return group;
|
|
|
|
if (commonItemsByName != null)
|
|
foreach (BuildItemGroup group in commonItemsByName.Values)
|
|
yield return group;
|
|
}
|
|
|
|
T GetFirst<T> (ICollection<T> list)
|
|
{
|
|
if (list == null)
|
|
return default (T);
|
|
|
|
foreach (T t in list)
|
|
return t;
|
|
|
|
return default (T);
|
|
}
|
|
|
|
internal string ThisFileFullPath {
|
|
get { return this_file_property_stack.Peek (); }
|
|
}
|
|
|
|
// Used for MSBuild*This* set of properties
|
|
internal void PushThisFileProperty (string full_filename)
|
|
{
|
|
string last_file = this_file_property_stack.Count == 0 ? String.Empty : this_file_property_stack.Peek ();
|
|
this_file_property_stack.Push (full_filename);
|
|
if (last_file != full_filename)
|
|
// first time, or different from previous one
|
|
SetMSBuildThisFileProperties (full_filename);
|
|
}
|
|
|
|
internal void PopThisFileProperty ()
|
|
{
|
|
string last_file = this_file_property_stack.Pop ();
|
|
if (this_file_property_stack.Count > 0 && last_file != this_file_property_stack.Peek ())
|
|
SetMSBuildThisFileProperties (this_file_property_stack.Peek ());
|
|
}
|
|
|
|
void SetMSBuildThisFileProperties (string full_filename)
|
|
{
|
|
if (String.IsNullOrEmpty (full_filename))
|
|
return;
|
|
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFile", Path.GetFileName (full_filename), PropertyType.Reserved));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileFullPath", full_filename, PropertyType.Reserved));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileName", Path.GetFileNameWithoutExtension (full_filename), PropertyType.Reserved));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileExtension", Path.GetExtension (full_filename), PropertyType.Reserved));
|
|
|
|
string project_dir = Path.GetDirectoryName (full_filename) + Path.DirectorySeparatorChar;
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileDirectory", project_dir, PropertyType.Reserved));
|
|
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileDirectoryNoRoot",
|
|
project_dir.Substring (Path.GetPathRoot (project_dir).Length),
|
|
PropertyType.Reserved));
|
|
}
|
|
|
|
|
|
internal void LogWarning (string filename, string message, params object[] messageArgs)
|
|
{
|
|
BuildWarningEventArgs bwea = new BuildWarningEventArgs (
|
|
null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
|
|
null, null);
|
|
ParentEngine.EventSource.FireWarningRaised (this, bwea);
|
|
}
|
|
|
|
internal void LogError (string filename, string message,
|
|
params object[] messageArgs)
|
|
{
|
|
BuildErrorEventArgs beea = new BuildErrorEventArgs (
|
|
null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
|
|
null, null);
|
|
ParentEngine.EventSource.FireErrorRaised (this, beea);
|
|
}
|
|
|
|
internal static string DefaultExtensionsPath {
|
|
get {
|
|
if (extensions_path == null) {
|
|
// NOTE: code from mcs/tools/gacutil/driver.cs
|
|
PropertyInfo gac = typeof (System.Environment).GetProperty (
|
|
"GacPath", BindingFlags.Static | BindingFlags.NonPublic);
|
|
|
|
if (gac != null) {
|
|
MethodInfo get_gac = gac.GetGetMethod (true);
|
|
string gac_path = (string) get_gac.Invoke (null, null);
|
|
extensions_path = Path.GetFullPath (Path.Combine (
|
|
gac_path, Path.Combine ("..", "xbuild")));
|
|
}
|
|
}
|
|
return extensions_path;
|
|
}
|
|
}
|
|
|
|
public BuildPropertyGroup EvaluatedProperties {
|
|
get {
|
|
if (needToReevaluate) {
|
|
needToReevaluate = false;
|
|
Reevaluate ();
|
|
}
|
|
return evaluatedProperties;
|
|
}
|
|
}
|
|
|
|
internal IEnumerable EvaluatedPropertiesAsDictionaryEntries {
|
|
get {
|
|
foreach (BuildProperty bp in EvaluatedProperties)
|
|
yield return new DictionaryEntry (bp.Name, bp.Value);
|
|
}
|
|
}
|
|
|
|
public string FullFileName {
|
|
get { return fullFileName; }
|
|
set { fullFileName = value; }
|
|
}
|
|
|
|
public BuildPropertyGroup GlobalProperties {
|
|
get { return globalProperties; }
|
|
set {
|
|
if (value == null)
|
|
throw new ArgumentNullException ("value");
|
|
|
|
if (value.FromXml)
|
|
throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
|
|
|
|
globalProperties = value;
|
|
}
|
|
}
|
|
|
|
public bool IsDirty {
|
|
get { return isDirty; }
|
|
}
|
|
|
|
public bool IsValidated {
|
|
get { return isValidated; }
|
|
set { isValidated = value; }
|
|
}
|
|
|
|
public BuildItemGroupCollection ItemGroups {
|
|
get { return itemGroups; }
|
|
}
|
|
|
|
public ImportCollection Imports {
|
|
get { return imports; }
|
|
}
|
|
|
|
public string InitialTargets {
|
|
get {
|
|
return String.Join ("; ", initialTargets.ToArray ());
|
|
}
|
|
set {
|
|
initialTargets.Clear ();
|
|
xmlDocument.DocumentElement.SetAttribute ("InitialTargets", value);
|
|
if (value != null)
|
|
initialTargets.AddRange (value.Split (
|
|
new char [] {';', ' '}, StringSplitOptions.RemoveEmptyEntries));
|
|
}
|
|
}
|
|
|
|
public Engine ParentEngine {
|
|
get { return parentEngine; }
|
|
}
|
|
|
|
public BuildPropertyGroupCollection PropertyGroups {
|
|
get { return propertyGroups; }
|
|
}
|
|
|
|
public string SchemaFile {
|
|
get { return schemaFile; }
|
|
set { schemaFile = value; }
|
|
}
|
|
|
|
public TargetCollection Targets {
|
|
get { return targets; }
|
|
}
|
|
|
|
public DateTime TimeOfLastDirty {
|
|
get { return timeOfLastDirty; }
|
|
}
|
|
|
|
public UsingTaskCollection UsingTasks {
|
|
get { return usingTasks; }
|
|
}
|
|
|
|
[MonoTODO]
|
|
public string Xml {
|
|
get { return xmlDocument.InnerXml; }
|
|
}
|
|
|
|
// corresponds to the xml attribute
|
|
public string DefaultToolsVersion {
|
|
get {
|
|
if (xmlDocument != null)
|
|
return xmlDocument.DocumentElement.GetAttribute ("ToolsVersion");
|
|
return null;
|
|
}
|
|
set {
|
|
if (xmlDocument != null)
|
|
xmlDocument.DocumentElement.SetAttribute ("ToolsVersion", value);
|
|
}
|
|
}
|
|
|
|
public bool HasToolsVersionAttribute {
|
|
get {
|
|
return xmlDocument != null && xmlDocument.DocumentElement.HasAttribute ("ToolsVersion");
|
|
}
|
|
}
|
|
|
|
public string ToolsVersion {
|
|
get; internal set;
|
|
}
|
|
|
|
internal Dictionary <string, BuildItemGroup> LastItemGroupContaining {
|
|
get { return last_item_group_containing; }
|
|
}
|
|
|
|
internal ProjectLoadSettings ProjectLoadSettings {
|
|
get { return project_load_settings; }
|
|
set { project_load_settings = value; }
|
|
}
|
|
|
|
internal static XmlNamespaceManager XmlNamespaceManager {
|
|
get {
|
|
if (manager == null) {
|
|
manager = new XmlNamespaceManager (new NameTable ());
|
|
manager.AddNamespace ("tns", ns);
|
|
}
|
|
|
|
return manager;
|
|
}
|
|
}
|
|
|
|
internal TaskDatabase TaskDatabase {
|
|
get { return taskDatabase; }
|
|
}
|
|
|
|
internal XmlDocument XmlDocument {
|
|
get { return xmlDocument; }
|
|
}
|
|
|
|
internal static string XmlNamespace {
|
|
get { return ns; }
|
|
}
|
|
|
|
static string OS {
|
|
get {
|
|
PlatformID pid = Environment.OSVersion.Platform;
|
|
switch ((int)pid) {
|
|
case 128:
|
|
case 4:
|
|
return "Unix";
|
|
case 6:
|
|
return "OSX";
|
|
default:
|
|
return "Windows_NT";
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|