//
// 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;
XmlDocument xmlDocument;
bool unloaded;
bool initialTargetsBuilt;
- List<string> builtTargetKeys;
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;
encoding = null;
- builtTargetKeys = new List<string> ();
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)
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,
{
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;
BuildSettings buildFlags)
{
CheckUnloaded ();
- if (buildFlags == BuildSettings.None)
+ 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) {
}
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 () + ":" + GlobalPropertiesToString (GlobalProperties);
+ return fullFileName + ":" + target_name.ToLower () +
+ (include_global_properties ? (":" + GlobalPropertiesToString (GlobalProperties))
+ : String.Empty);
}
string GlobalPropertiesToString (BuildPropertyGroup bgp)
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)
{
"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);
targets = new TargetCollection (this);
last_item_group_containing = new Dictionary <string, BuildItemGroup> ();
- string effective_tools_version = GetToolsVersionToUse ();
+ string effective_tools_version = GetToolsVersionToUse (false);
taskDatabase = new TaskDatabase ();
taskDatabase.CopyTasks (ParentEngine.GetDefaultTasks (effective_tools_version));
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 (String.Format ("Invalid element '{0}' in project file.", xe.Name));
// Removes entries of all earlier built targets for this project
void RemoveBuiltTargets ()
{
- foreach (string key in builtTargetKeys)
- ParentEngine.BuiltTargetsOutputByName.Remove (key);
+ 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)
// add properties that we dont have from parent engine's
// global properties
foreach (BuildProperty gp in ParentEngine.GlobalProperties) {
- if (EvaluatedProperties [gp.Name] == null) {
+ if (evaluatedProperties [gp.Name] == null) {
bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
- EvaluatedProperties.AddProperty (bp);
+ 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 ("MSBuildProjectFile", Path.GetFileName (fullFileName),
+ evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFile", Path.GetFileName (fullFileName),
PropertyType.Reserved));
- EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectName",
+ 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 ("MSBuildToolsVersion", effective_tools_version, PropertyType.Reserved));
- EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath", ExtensionsPath, PropertyType.Reserved));
- EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath32", ExtensionsPath, PropertyType.Reserved));
- EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets", DefaultTargets, PropertyType.Reserved));
- EvaluatedProperties.AddProperty (new BuildProperty ("OS", OS, PropertyType.Environment));
+ 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 ()
+ string GetToolsVersionToUse (bool emitWarning)
{
if (!String.IsNullOrEmpty (ToolsVersion))
return ToolsVersion;
return parentEngine.DefaultToolsVersion;
if (parentEngine.Toolsets [DefaultToolsVersion] == null) {
- LogWarning (FullFileName, "Project has unknown ToolsVersion '{0}'. Using the default tools version '{1}' instead.",
+ if (emitWarning)
+ LogWarning (FullFileName, "Project has unknown ToolsVersion '{0}'. Using the default tools version '{1}' instead.",
DefaultToolsVersion, parentEngine.DefaultToolsVersion);
return parentEngine.DefaultToolsVersion;
}
usingTask = new UsingTask (xmlElement, this, importedProject);
UsingTasks.Add (usingTask);
}
-
- void AddImport (XmlElement xmlElement, ImportedProject importingProject)
+
+ void AddImport (XmlElement xmlElement, ImportedProject importingProject, bool evaluate_properties)
{
// eval all the properties etc till the import
- groupingCollection.Evaluate (EvaluationType.Property);
+ if (evaluate_properties)
+ groupingCollection.Evaluate (EvaluationType.Property);
- Import import = new Import (xmlElement, this, importingProject);
- if (!ConditionParser.ParseAndEvaluate (import.Condition, this))
- return;
+ try {
+ PushThisFileProperty (importingProject != null ? importingProject.FullFileName : FullFileName);
- 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);
+ string project_attribute = xmlElement.GetAttribute ("Project");
+ if (String.IsNullOrEmpty (project_attribute))
+ throw new InvalidProjectFileException ("The required attribute \"Project\" is missing from element <Import>.");
- return;
+ 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 false;
}
if (String.Compare (fullFileName, import.EvaluatedProjectPath) == 0) {
"The main project file was imported here, which creates a circular " +
"reference. Ignoring this import.");
- return;
+ 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);
}
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 (
ParentEngine.EventSource.FireErrorRaised (this, beea);
}
- static string ExtensionsPath {
+ internal static string DefaultExtensionsPath {
get {
if (extensions_path == null) {
// NOTE: code from mcs/tools/gacutil/driver.cs
get; internal set;
}
- internal List<string> BuiltTargetKeys {
- get { return builtTargetKeys; }
- }
-
internal Dictionary <string, BuildItemGroup> LastItemGroupContaining {
get { return last_item_group_containing; }
}