//
// Author:
// Marek Sieradzki (marek.sieradzki@gmail.com)
+// Ankit Jain (jankit@novell.com)
//
// (C) 2006 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
#if NET_2_0
using System;
+using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Xml;
using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
using Mono.XBuild.Utilities;
namespace Microsoft.Build.BuildEngine {
static string PathSeparatorAsString = Path.PathSeparator.ToString ();
internal Import (XmlElement importElement, Project project, ImportedProject originalProject)
+ : this (importElement, null, project, originalProject)
+ {}
+
+ // if @alternateProjectPath is available then that it used as the EvaluatedProjectPath!
+ internal Import (XmlElement importElement, string alternateProjectPath, Project project, ImportedProject originalProject)
{
if (importElement == null)
throw new ArgumentNullException ("importElement");
throw new InvalidProjectFileException ("The required attribute \"Project\" is missing from element <Import>.");
if (ConditionParser.ParseAndEvaluate (Condition, project)) {
- evaluatedProjectPath = EvaluateProjectPath (ProjectPath);
+ evaluatedProjectPath = String.IsNullOrEmpty (alternateProjectPath) ? EvaluateProjectPath (ProjectPath) : alternateProjectPath;
+
evaluatedProjectPath = GetFullPath ();
if (EvaluatedProjectPath == String.Empty)
throw new InvalidProjectFileException ("The required attribute \"Project\" is missing from element <Import>.");
{
string filename = evaluatedProjectPath;
// NOTE: it's a hack to transform Microsoft.CSharp.Targets to Microsoft.CSharp.targets
- if (Path.HasExtension (filename))
- filename = Path.ChangeExtension (filename, Path.GetExtension (filename));
-
+ if (!File.Exists (filename) && Path.GetFileName (filename) == "Microsoft.CSharp.Targets")
+ filename = Path.ChangeExtension (filename, ".targets");
+
if (!File.Exists (filename)) {
if (ignoreMissingImports) {
project.LogWarning (project.FullFileName, "Could not find project file {0}, to import. Ignoring.", filename);
string EvaluateProjectPath (string file)
{
- string ret;
- if (EvaluateAsMSBuildExtensionsPath (file, "MSBuildExtensionsPath", out ret) ||
- EvaluateAsMSBuildExtensionsPath (file, "MSBuildExtensionsPath32", out ret) ||
- EvaluateAsMSBuildExtensionsPath (file, "MSBuildExtensionsPath64", out ret))
- return ret;
+ return Expression.ParseAs<string> (file, ParseOptions.Split, project);
+ }
+
+ string GetFullPath ()
+ {
+ string file = EvaluatedProjectPath;
+ if (!Path.IsPathRooted (file) && !String.IsNullOrEmpty (ContainedInProjectFileName))
+ file = Path.Combine (Path.GetDirectoryName (ContainedInProjectFileName), file);
+
+ return MSBuildUtils.FromMSBuildPath (file);
+ }
+
+ // For every extension path, in order, finds suitable
+ // import filename(s) matching the Import, and calls
+ // @func with them
+ //
+ // func: bool func(importPath, from_source_msg)
+ //
+ // If for an extension path, atleast one file gets imported,
+ // then it stops at that.
+ // So, in case imports like "$(MSBuildExtensionsPath)\foo\*",
+ // for every extension path, it will try to import the "foo\*",
+ // and if atleast one file gets successfully imported, then it
+ // stops at that
+ internal static void ForEachExtensionPathTillFound (XmlElement xmlElement, Project project, ImportedProject importingProject,
+ Func<string, string, bool> func)
+ {
+ string project_attribute = xmlElement.GetAttribute ("Project");
+ string condition_attribute = xmlElement.GetAttribute ("Condition");
+
+ bool has_extn_ref = project_attribute.IndexOf ("$(MSBuildExtensionsPath)") >= 0 ||
+ project_attribute.IndexOf ("$(MSBuildExtensionsPath32)") >= 0 ||
+ project_attribute.IndexOf ("$(MSBuildExtensionsPath64)") >= 0;
+
+ string importingFile = importingProject != null ? importingProject.FullFileName : project.FullFileName;
+ DirectoryInfo base_dir_info = null;
+ if (!String.IsNullOrEmpty (importingFile))
+ base_dir_info = new DirectoryInfo (Path.GetDirectoryName (importingFile));
+ else
+ base_dir_info = new DirectoryInfo (Directory.GetCurrentDirectory ());
+
+ IEnumerable<string> extn_paths = has_extn_ref ? GetExtensionPaths (project) : new string [] {null};
+ try {
+ foreach (string path in extn_paths) {
+ string extn_msg = null;
+ if (has_extn_ref) {
+ project.SetExtensionsPathProperties (path);
+ extn_msg = "from extension path " + path;
+ }
+
+ // do this after setting new Extension properties, as condition might
+ // reference it
+ if (!ConditionParser.ParseAndEvaluate (condition_attribute, project)) {
+ project.ParentEngine.LogMessage (MessageImportance.Low,
+ "{0}: Not importing '{1}' project as the condition '{2}' is false",
+ importingFile, project_attribute, condition_attribute);
+ continue;
+ }
- return EvaluatePath (file);
+ // We stop if atleast one file got imported.
+ // Remaining extension paths are *not* tried
+ bool atleast_one = false;
+ foreach (string importPath in GetImportPathsFromString (project_attribute, project, base_dir_info)) {
+ try {
+ if (func (importPath, extn_msg))
+ atleast_one = true;
+ } catch (Exception e) {
+ throw new InvalidProjectFileException (String.Format (
+ "{0}: Project file could not be imported, it was being imported by " +
+ "{1}: {2}", importPath, importingFile, e.Message), e);
+ }
+ }
+
+ if (atleast_one)
+ return;
+ }
+ } finally {
+ if (has_extn_ref)
+ project.SetExtensionsPathProperties (Project.DefaultExtensionsPath);
+ }
}
- bool EvaluateAsMSBuildExtensionsPath (string file, string property_name, out string epath)
+ // Parses the Project attribute from an Import,
+ // and returns the import filenames that match.
+ // This handles wildcards also
+ static IEnumerable<string> GetImportPathsFromString (string import_string, Project project, DirectoryInfo base_dir_info)
{
- epath = null;
- string property_ref = String.Format ("$({0})", property_name);
- if (file.IndexOf (property_ref) < 0)
- return false;
+ string parsed_import = Expression.ParseAs<string> (import_string, ParseOptions.AllowItemsNoMetadataAndSplit, project);
+ if (parsed_import != null)
+ parsed_import = parsed_import.Trim ();
+
+ if (String.IsNullOrEmpty (parsed_import))
+ throw new InvalidProjectFileException ("The required attribute \"Project\" in Import is empty");
+
+#if NET_4_0
+ if (DirectoryScanner.HasWildcard (parsed_import)) {
+ var directoryScanner = new DirectoryScanner () {
+ Includes = new ITaskItem [] { new TaskItem (parsed_import) },
+ BaseDirectory = base_dir_info
+ };
+ directoryScanner.Scan ();
+
+ foreach (ITaskItem matchedItem in directoryScanner.MatchedItems)
+ yield return matchedItem.ItemSpec;
+ } else
+#endif
+ yield return parsed_import;
+ }
+ // Gives a list of extensions paths to try for $(MSBuildExtensionsPath),
+ // *in-order*
+ static IEnumerable<string> GetExtensionPaths (Project project)
+ {
// This is a *HACK* to support multiple paths for
// MSBuildExtensionsPath property. Normally it would
// get resolved to a single value, but here we special
- // case it and try ~/.config/xbuild/tasks and any
- // paths specified in the env var $MSBuildExtensionsPath .
+ // case it and try various paths, see the code below
//
// The property itself will resolve to the default
- // location though, so you get in any other part of the
+ // location though, so you get that in any other part of the
// project.
- string envvar = Environment.GetEnvironmentVariable (property_name);
+ string envvar = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath");
envvar = String.Join (PathSeparatorAsString, new string [] {
(envvar ?? String.Empty),
// For mac osx, look in the 'External' dir on macosx,
// see bug #663180
MSBuildUtils.RunningOnMac ? MacOSXExternalXBuildDir : String.Empty,
- DotConfigExtensionsPath});
+ DotConfigExtensionsPath,
+ Project.DefaultExtensionsPath});
- string [] paths = envvar.Split (new char [] {Path.PathSeparator}, StringSplitOptions.RemoveEmptyEntries);
- foreach (string path in paths) {
- if (!Directory.Exists (path)) {
- project.ParentEngine.LogMessage (MessageImportance.Low, "Extension path '{0}' not found, ignoring.", path);
+ var pathsTable = new Dictionary<string, string> ();
+ foreach (string extn_path in envvar.Split (new char [] {Path.PathSeparator}, StringSplitOptions.RemoveEmptyEntries)) {
+ if (pathsTable.ContainsKey (extn_path))
continue;
- }
-
- string pfile = Path.GetFullPath (file.Replace ("\\", "/").Replace (
- property_ref, path + Path.DirectorySeparatorChar));
- var evaluated_path = EvaluatePath (pfile);
- if (File.Exists (evaluated_path)) {
- project.ParentEngine.LogMessage (MessageImportance.Low,
- "{0}: Importing project {1} from extension path {2}", project.FullFileName, evaluated_path, path);
- epath = pfile;
- return true;
+ if (!Directory.Exists (extn_path)) {
+ project.ParentEngine.LogMessage (MessageImportance.Low, "Extension path '{0}' not found, ignoring.", extn_path);
+ continue;
}
- project.ParentEngine.LogMessage (MessageImportance.Low,
- "{0}: Couldn't find project {1} for extension path {2}", project.FullFileName, evaluated_path, path);
- }
-
- return false;
- }
-
- string EvaluatePath (string path)
- {
- var exp = new Expression ();
- exp.Parse (path, ParseOptions.Split);
- return (string) exp.ConvertTo (project, typeof (string));
- }
- string GetFullPath ()
- {
- string file = EvaluatedProjectPath;
-
- if (!Path.IsPathRooted (EvaluatedProjectPath)) {
- string dir = null;
- if (originalProject == null) {
- if (project.FullFileName != String.Empty) // Path.GetDirectoryName throws exception on String.Empty
- dir = Path.GetDirectoryName (project.FullFileName);
- } else {
- if (originalProject.FullFileName != String.Empty)
- dir = Path.GetDirectoryName (originalProject.FullFileName);
- }
- if (dir != null)
- file = Path.Combine (dir, EvaluatedProjectPath);
+ pathsTable [extn_path] = extn_path;
+ yield return extn_path;
}
-
- return MSBuildUtils.FromMSBuildPath (file);
}
-
+
public string Condition {
get {
string s = importElement.GetAttribute ("Condition");
public string ProjectPath {
get { return importElement.GetAttribute ("Project"); }
}
+
+ internal string ContainedInProjectFileName {
+ get { return originalProject != null ? originalProject.FullFileName : project.FullFileName; }
+ }
}
}
//
// 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
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,
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);
AddUsingTask (xe, ip);
break;
case "Import":
- AddImport (xe, ip);
+ AddImport (xe, ip, true);
break;
case "ItemGroup":
AddItemGroup (xe, ip);
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));
- evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath", ExtensionsPath, PropertyType.Reserved));
- evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath32", ExtensionsPath, PropertyType.Reserved));
- evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath64", ExtensionsPath, PropertyType.Reserved));
+ SetExtensionsPathProperties (DefaultExtensionsPath);
evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets", DefaultTargets, PropertyType.Reserved));
evaluatedProperties.AddProperty (new BuildProperty ("OS", OS, PropertyType.Environment));
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
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;
+ string project_attribute = xmlElement.GetAttribute ("Project");
+ if (String.IsNullOrEmpty (project_attribute))
+ throw new InvalidProjectFileException ("The required attribute \"Project\" is missing from element <Import>.");
- 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);
+ Import.ForEachExtensionPathTillFound (xmlElement, this, importingProject,
+ (importPath, from_source_msg) => AddSingleImport (xmlElement, importPath, importingProject, from_source_msg));
+ }
- return;
+ 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);
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