bool isValidated;
BuildItemGroupCollection itemGroups;
ImportCollection imports;
- string[] initialTargets;
+ List<string> initialTargets;
Dictionary <string, BuildItemGroup> last_item_group_containing;
bool needToReevaluate;
Engine parentEngine;
List<string> builtTargetKeys;
bool building;
BuildSettings current_settings;
+ Stack<Batch> batches;
+ ProjectLoadSettings project_load_settings;
+
static string extensions_path;
static XmlNamespaceManager manager;
{
}
- 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 ();
fullFileName = String.Empty;
timeOfLastDirty = DateTime.Now;
current_settings = BuildSettings.None;
+ project_load_settings = ProjectLoadSettings.None;
+
+ encoding = null;
builtTargetKeys = new List<string> ();
+ initialTargets = new List<string> ();
+ defaultTargets = new string [0];
+ batches = new Stack<Batch> ();
globalProperties = new BuildPropertyGroup (null, this, null, false);
foreach (BuildProperty bp in parentEngine.GlobalProperties)
GlobalProperties.AddProperty (bp.Clone (true));
ProcessXml ();
+
}
[MonoTODO ("Not tested")]
return false;
}
- if (!initialTargetsBuilt && initialTargets != null && initialTargets.Length > 0) {
+ if (!initialTargetsBuilt) {
foreach (string target in initialTargets) {
if (!BuildTarget (target.Trim (), targetOutputs))
return false;
throw new ArgumentException ("targetNames cannot contain null strings");
if (!targets.Exists (target_name)) {
- //FIXME: Log this!
- Console.WriteLine ("Target named '{0}' not found in the project.", target_name);
+ LogError (fullFileName, "Target named '{0}' not found in the project.", target_name);
return false;
}
if (!targets [target_name].Build (key))
return false;
- ITaskItem[] outputs = ParentEngine.BuiltTargetsOutputByName [key];
- if (targetOutputs != null)
- targetOutputs.Add (target_name, outputs);
+ ITaskItem[] outputs;
+ if (ParentEngine.BuiltTargetsOutputByName.TryGetValue (key, out outputs)) {
+ if (targetOutputs != null)
+ targetOutputs.Add (target_name, outputs);
+ }
return true;
}
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));
string filename = fullFileName;
[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 ();
try {
ParentEngine.RemoveLoadedProject (this);
- XmlReaderSettings settings = new XmlReaderSettings ();
-
- if (SchemaFile != null) {
- settings.Schemas.Add (null, SchemaFile);
- settings.ValidationType = ValidationType.Schema;
- settings.ValidationEventHandler += new ValidationEventHandler (ValidationCallBack);
- }
-
- XmlReader xmlReader = XmlReader.Create (textReader, settings);
- xmlDocument.Load (xmlReader);
+ xmlDocument.Load (textReader);
if (xmlDocument.DocumentElement.Name == "VisualStudioProject")
throw new InvalidProjectFileException (String.Format (
"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));
last_item_group_containing = new Dictionary <string, BuildItemGroup> ();
taskDatabase = new TaskDatabase ();
- if (ParentEngine.DefaultTasksRegistered)
- taskDatabase.CopyTasks (ParentEngine.DefaultTasks);
+ taskDatabase.CopyTasks (ParentEngine.GetDefaultTasks (GetToolsVersionToUse ()));
- if (xmlDocument.DocumentElement.GetAttributeNode ("DefaultTargets") != null)
- defaultTargets = xmlDocument.DocumentElement.GetAttribute ("DefaultTargets").Split (';');
- else
- defaultTargets = new string [0];
-
- ProcessProjectAttributes (xmlDocument.DocumentElement.Attributes);
+ initialTargets = new List<string> ();
+ defaultTargets = new string [0];
+ PrepareForEvaluate ();
ProcessElements (xmlDocument.DocumentElement, null);
isDirty = false;
foreach (XmlAttribute attr in attributes) {
switch (attr.Name) {
case "InitialTargets":
- initialTargets = attr.Value.Split (new char [] {';'},
- StringSplitOptions.RemoveEmptyEntries);
+ initialTargets.AddRange (attr.Value.Split (
+ new char [] {';', ' '},
+ StringSplitOptions.RemoveEmptyEntries));
break;
case "DefaultTargets":
- defaultTargets = attr.Value.Split (new char [] {';'},
+ // 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;
AddChoose (xe);
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 ()
{
evaluatedItems = new BuildItemGroup (null, this, null, true);
evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this, null, true);
RemoveBuiltTargets ();
InitializeProperties ();
+ }
+ void Evaluate ()
+ {
groupingCollection.Evaluate ();
//FIXME: UsingTasks aren't really evaluated. (shouldn't use expressions or anything)
Path.GetFileNameWithoutExtension (fullFileName),
PropertyType.Reserved));
EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildBinPath", parentEngine.BinPath, PropertyType.Reserved));
- EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsPath", parentEngine.BinPath, PropertyType.Reserved));
+ string toolsVersionToUse = GetToolsVersionToUse ();
+ string toolsPath = parentEngine.Toolsets [toolsVersionToUse].ToolsPath;
+ if (toolsPath == null)
+ throw new Exception ("Unknown toolsVersion: " + toolsVersionToUse);
+ EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsPath", toolsPath, PropertyType.Reserved));
+ EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsVersion", toolsVersionToUse, PropertyType.Reserved));
EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath", ExtensionsPath, PropertyType.Reserved));
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;
EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDirectory", projectDir, PropertyType.Reserved));
}
+
+ string GetToolsVersionToUse ()
+ {
+ if (String.IsNullOrEmpty (ToolsVersion)) {
+ if (HasToolsVersionAttribute)
+ return DefaultToolsVersion;
+ else
+ return parentEngine.DefaultToolsVersion;
+ } else {
+ return ToolsVersion;
+ }
+ }
void AddProjectExtensions (XmlElement xmlElement)
{
void AddImport (XmlElement xmlElement, ImportedProject importingProject)
{
- Import import;
-
- import = new Import (xmlElement, this, importingProject);
+ // eval all the properties etc till the import
+ groupingCollection.Evaluate (EvaluationType.Property);
+
+ Import import = new Import (xmlElement, this, importingProject);
+ if (!ConditionParser.ParseAndEvaluate (import.Condition, this))
+ return;
+
+ if (Imports.Contains (import)) {
+ LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
+ "A circular reference was found involving the import of {0}. Only" +
+ " the first import of this file will be used, ignoring others.",
+ import.ProjectPath);
+
+ return;
+ }
+
+ 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;
+ }
+
Imports.Add (import);
+ import.Evaluate (project_load_settings == ProjectLoadSettings.IgnoreMissingImports);
}
void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
public string DefaultTargets {
get {
- return xmlDocument.DocumentElement.GetAttribute ("DefaultTargets");
+ return String.Join ("; ", defaultTargets);
}
set {
xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
- defaultTargets = value.Split (new char [] {';'}, StringSplitOptions.RemoveEmptyEntries);
+ if (value != null)
+ defaultTargets = value.Split (new char [] {';', ' '},
+ StringSplitOptions.RemoveEmptyEntries);
}
}
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)
return default (T);
}
- void LogWarning (string filename, string message, params object[] messageArgs)
+ 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),
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);
+ }
+
static string ExtensionsPath {
get {
if (extensions_path == null) {
throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
globalProperties = value;
- NeedToReevaluate ();
}
}
public string InitialTargets {
get {
- return xmlDocument.DocumentElement.GetAttribute ("InitialTargets");
+ return String.Join ("; ", initialTargets.ToArray ());
}
set {
+ initialTargets.Clear ();
xmlDocument.DocumentElement.SetAttribute ("InitialTargets", value);
- initialTargets = value.Split (new char [] {';'}, StringSplitOptions.RemoveEmptyEntries);
+ if (value != null)
+ initialTargets.AddRange (value.Split (
+ new char [] {';', ' '}, StringSplitOptions.RemoveEmptyEntries));
}
}
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 List<string> BuiltTargetKeys {
get { return builtTargetKeys; }
}
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";
+ }
+ }
+ }
+
}
}