//
// 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
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 Mono.XBuild.Framework;
+using Mono.XBuild.CommandLine;
namespace Microsoft.Build.BuildEngine {
public class Project {
bool isValidated;
BuildItemGroupCollection itemGroups;
ImportCollection imports;
- string initialTargets;
+ List<string> initialTargets;
Dictionary <string, BuildItemGroup> last_item_group_containing;
bool needToReevaluate;
Engine parentEngine;
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 (Engine engine)
+ public Project (Engine engine) : this (engine, null)
+ {
+ }
+
+ public Project (Engine engine, string toolsVersion)
{
parentEngine = engine;
+ ToolsVersion = toolsVersion;
buildEnabled = ParentEngine.BuildEnabled;
xmlDocument = new XmlDocument ();
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")]
if (!String.IsNullOrEmpty (importCondition))
importElement.SetAttribute ("Condition", importCondition);
- Import import = new Import (importElement, this, null);
- imports.Add (import);
+ AddImport (importElement, null, false);
MarkProjectAsDirty ();
NeedToReevaluate ();
}
{
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 ();
- ParentEngine.StartBuild ();
- NeedToReevaluate ();
-
+ if (buildFlags == BuildSettings.None) {
+ needToReevaluate = false;
+ Reevaluate ();
+ }
+
+#if NET_4_0
+ ProcessBeforeAndAfterTargets ();
+#endif
+
if (targetNames == null || targetNames.Length == 0) {
- if (defaultTargets != null && defaultTargets.Length != 0)
+ if (defaultTargets != null && defaultTargets.Length != 0) {
targetNames = defaultTargets;
- else if (firstTargetName != null)
+ } else if (firstTargetName != null) {
targetNames = new string [1] { firstTargetName};
- else
+ } 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;
}
- if (targetNames == null || targetNames.Length == 0)
- return false;
-
foreach (string target in targetNames) {
if (target == null)
- throw new ArgumentException ("targetNames cannot contain null strings");
+ throw new ArgumentNullException ("Target name cannot be null");
- if (!targets.Exists (target))
- // FIXME: test if it's logged
+ if (!BuildTarget (target.Trim (), targetOutputs))
return false;
+ }
- if (!targets [target].Build ())
- 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, targets [target].Outputs);
+ 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.ToLower () +
+ (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 ();
+ }
+
+#if NET_4_0
+ 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 []));
+ }
+#endif
+
[MonoTODO]
public string [] GetConditionedPropertyValues (string propertyName)
{
if (evaluatedItemsByName.ContainsKey (itemName))
return evaluatedItemsByName [itemName];
else
- return new BuildItemGroup ();
+ return new BuildItemGroup (this);
}
public BuildItemGroup GetEvaluatedItemsByNameIgnoringCondition (string itemName)
if (evaluatedItemsByNameIgnoringCondition.ContainsKey (itemName))
return evaluatedItemsByNameIgnoringCondition [itemName];
else
- return new BuildItemGroup ();
+ return new BuildItemGroup (this);
}
public string GetEvaluatedProperty (string propertyName)
public void Load (string projectFileName)
{
- this.fullFileName = Path.GetFullPath (projectFileName);
- DoLoad (new StreamReader (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;
fullFileName = String.Empty;
DoLoad (textReader);
}
public void LoadXml (string projectXml)
{
+ LoadXml (projectXml, ProjectLoadSettings.None);
+ }
+
+ public void LoadXml (string projectXml, ProjectLoadSettings projectLoadSettings)
+ {
+ project_load_settings = projectLoadSettings;
fullFileName = String.Empty;
DoLoad (new StringReader (projectXml));
MarkProjectAsDirty ();
if (itemToRemove == null)
throw new ArgumentNullException ("itemToRemove");
- if (!itemToRemove.FromXml && !itemToRemove.HasParent)
+ if (!itemToRemove.FromXml && !itemToRemove.HasParentItem)
throw new InvalidOperationException ("The object passed in is not part of the project.");
BuildItemGroup big = itemToRemove.ParentItemGroup;
[MonoTODO]
public void ResetBuildStatus ()
{
- throw new NotImplementedException ();
+ // hack to allow built targets to be removed
+ building = true;
+ Reevaluate ();
+ building = false;
}
public void Save (string projectFileName)
try {
ParentEngine.RemoveLoadedProject (this);
- XmlReaderSettings settings = new XmlReaderSettings ();
-
+ 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) {
- settings.Schemas.Add (null, SchemaFile);
- settings.ValidationType = ValidationType.Schema;
- settings.ValidationEventHandler += new ValidationEventHandler (ValidationCallBack);
+ xmlDocument.Schemas.Add (XmlSchema.Read (
+ new StreamReader (SchemaFile), ValidationCallBack));
+ xmlDocument.Validate (ValidationCallBack);
}
-
- XmlReader xmlReader = XmlReader.Create (textReader, settings);
- xmlDocument.Load (xmlReader);
if (xmlDocument.DocumentElement.Name != "Project") {
throw new InvalidProjectFileException (String.Format (
targets = new TargetCollection (this);
last_item_group_containing = new Dictionary <string, BuildItemGroup> ();
+ string effective_tools_version = GetToolsVersionToUse (false);
taskDatabase = new TaskDatabase ();
- if (ParentEngine.DefaultTasksRegistered)
- taskDatabase.CopyTasks (ParentEngine.DefaultTasks);
+ taskDatabase.CopyTasks (ParentEngine.GetDefaultTasks (effective_tools_version));
- if (xmlDocument.DocumentElement.GetAttributeNode ("DefaultTargets") != null)
- defaultTargets = xmlDocument.DocumentElement.GetAttribute ("DefaultTargets").Split (';');
- else
- defaultTargets = new string [0];
-
+ 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;
AddUsingTask (xe, ip);
break;
case "Import":
- AddImport (xe, ip);
+ AddImport (xe, ip, true);
break;
case "ItemGroup":
AddItemGroup (xe, ip);
AddPropertyGroup (xe, ip);
break;
case "Choose":
- AddChoose (xe);
+ AddChoose (xe, ip);
break;
default:
- throw new InvalidProjectFileException ("Invalid element in project file.");
+ throw new InvalidProjectFileException (String.Format ("Invalid element '{0}' in project file.", xe.Name));
}
}
}
}
- void Evaluate ()
+ 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.InvariantCultureIgnoreCase);
evaluatedItemsByNameIgnoringCondition = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
- evaluatedProperties = new BuildPropertyGroup (null, null, null, true);
+ if (building && current_settings == BuildSettings.None)
+ RemoveBuiltTargets ();
- InitializeProperties ();
+ InitializeProperties (effective_tools_version);
+ }
+ void Evaluate ()
+ {
groupingCollection.Evaluate ();
//FIXME: UsingTasks aren't really evaluated. (shouldn't use expressions or anything)
usingTask.Evaluate ();
}
- void InitializeProperties ()
+ // 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);
+ 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 (bp);
}
- EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildBinPath", parentEngine.BinPath, PropertyType.Reserved));
+ 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));
// FIXME: make some internal method that will work like GetDirectoryName but output String.Empty on null/String.Empty
string projectDir;
else
projectDir = Path.GetDirectoryName (FullFileName);
- EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDirectory", projectDir, PropertyType.Reserved));
+ 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)
usingTask = new UsingTask (xmlElement, this, importedProject);
UsingTasks.Add (usingTask);
}
-
- void AddImport (XmlElement xmlElement, ImportedProject importingProject)
+
+ void AddImport (XmlElement xmlElement, ImportedProject importingProject, bool evaluate_properties)
{
- Import import;
-
- import = new Import (xmlElement, this, importingProject);
+ // eval all the properties etc till the import
+ if (evaluate_properties)
+ groupingCollection.Evaluate (EvaluationType.Property);
+
+ 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 ();
+ }
+ }
+
+ 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);
PropertyGroups.Add (bpg);
}
- void AddChoose (XmlElement xmlElement)
+ void AddChoose (XmlElement xmlElement, ImportedProject importedProject)
{
- BuildChoose bc = new BuildChoose (xmlElement, this);
+ BuildChoose bc = new BuildChoose (xmlElement, this, importedProject);
groupingCollection.Add (bc);
}
public string DefaultTargets {
get {
- return xmlDocument.DocumentElement.GetAttribute ("DefaultTargets");
+ return String.Join ("; ", defaultTargets);
}
set {
xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
- defaultTargets = value.Split (';');
+ if (value != null)
+ defaultTargets = value.Split (new char [] {';', ' '},
+ StringSplitOptions.RemoveEmptyEntries);
}
}
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?
Dictionary<string, BuildItemGroup> perBatchItemsByName;
Dictionary<string, BuildItemGroup> commonItemsByName;
- internal void SetBatchedItems (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 && commonItemsByName == null)
- return EvaluatedItemsByName.TryGetValue (itemName, out group);
+ if (perBatchItemsByName != null && perBatchItemsByName.TryGetValue (itemName, out group))
+ return true;
- if (perBatchItemsByName != null)
- return perBatchItemsByName.TryGetValue (itemName, out group);
-
- if (commonItemsByName != null)
- return commonItemsByName.TryGetValue (itemName, out group);
+ if (commonItemsByName != null && commonItemsByName.TryGetValue (itemName, out group))
+ return true;
group = null;
- return false;
+ return EvaluatedItemsByName.TryGetValue (itemName, out group);
}
internal string GetMetadataBatched (string itemName, string metadataName)
if (group != null) {
foreach (BuildItem item in group) {
if (item.HasMetadata (metadataName))
- return item.GetMetadata (metadataName);
+ return item.GetEvaluatedMetadata (metadataName);
}
}
return String.Empty;
return default (T);
}
+ // 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) {
}
}
+ 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; }
throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
globalProperties = value;
- NeedToReevaluate ();
}
}
}
public string InitialTargets {
- get { return initialTargets; }
- set { initialTargets = value; }
+ 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 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 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";
+ }
+ }
+ }
+
}
}