X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FMicrosoft.Build.Engine%2FMicrosoft.Build.BuildEngine%2FImport.cs;h=f9c0e52bbc1439d29e50e28237bcb40d84a52c76;hb=613b532987c3e6ec1482be145b23fe8832da4b14;hp=ff187b3eb3816d250944091e3d778313a98be1ad;hpb=dd91824dcef690c0d7f50993fdd7139a8bb23af1;p=mono.git diff --git a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Import.cs b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Import.cs index ff187b3eb38..f9c0e52bbc1 100644 --- a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Import.cs +++ b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Import.cs @@ -3,8 +3,10 @@ // // 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 @@ -28,17 +30,33 @@ #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 { public class Import { XmlElement importElement; Project project; ImportedProject originalProject; string evaluatedProjectPath; + + static string DotConfigExtensionsPath = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData), + Path.Combine ("xbuild", "tasks")); + const string MacOSXExternalXBuildDir = "/Library/Frameworks/Mono.framework/External/xbuild"; + 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"); @@ -50,38 +68,188 @@ namespace Microsoft.Build.BuildEngine { this.originalProject = originalProject; if (ProjectPath == String.Empty) - throw new InvalidProjectFileException ("Project required attribute \"Project\" is missing from element ."); - } + throw new InvalidProjectFileException ("The required attribute \"Project\" is missing from element ."); - internal void Evaluate () - { - string file; - OldExpression exp; + if (ConditionParser.ParseAndEvaluate (Condition, project)) { + evaluatedProjectPath = String.IsNullOrEmpty (alternateProjectPath) ? EvaluateProjectPath (ProjectPath) : alternateProjectPath; - exp = new OldExpression (project); - exp.ParseSource (ProjectPath); - - file = evaluatedProjectPath = (string) exp.ConvertTo (typeof (string)); - - if (!Path.IsPathRooted (EvaluatedProjectPath) && project.FullFileName != String.Empty) { - string dir; - if (originalProject == null) - dir = Path.GetDirectoryName (project.FullFileName); - else - dir = Path.GetDirectoryName (originalProject.FullFileName); - file = Path.Combine (dir, EvaluatedProjectPath); + evaluatedProjectPath = GetFullPath (); + if (EvaluatedProjectPath == String.Empty) + throw new InvalidProjectFileException ("The required attribute \"Project\" is missing from element ."); } + } + + // FIXME: condition + internal void Evaluate (bool ignoreMissingImports) + { + string filename = evaluatedProjectPath; + // NOTE: it's a hack to transform Microsoft.CSharp.Targets to Microsoft.CSharp.targets + if (!File.Exists (filename) && Path.GetFileName (filename) == "Microsoft.CSharp.Targets") + filename = Path.ChangeExtension (filename, ".targets"); - if (!File.Exists (file)) { - throw new InvalidProjectFileException (String.Format ("Imported project: \"{0}\" does not exist.", file)); + if (!File.Exists (filename)) { + if (ignoreMissingImports) { + project.LogWarning (project.FullFileName, "Could not find project file {0}, to import. Ignoring.", filename); + return; + } else { + throw new InvalidProjectFileException (String.Format ("Imported project: \"{0}\" does not exist.", filename)); + } } ImportedProject importedProject = new ImportedProject (); - importedProject.Load (file); - // FIXME: UGLY HACK + importedProject.Load (filename); + project.ProcessElements (importedProject.XmlDocument.DocumentElement, importedProject); } - + + string EvaluateProjectPath (string file) + { + return Expression.ParseAs (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 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 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; + } + + // 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); + } + } + + // Parses the Project attribute from an Import, + // and returns the import filenames that match. + // This handles wildcards also + static IEnumerable GetImportPathsFromString (string import_string, Project project, DirectoryInfo base_dir_info) + { + string parsed_import = Expression.ParseAs (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 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 various paths, see the code below + // + // The property itself will resolve to the default + // location though, so you get that in any other part of the + // project. + + 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, + Project.DefaultExtensionsPath}); + + var pathsTable = new Dictionary (); + foreach (string extn_path in envvar.Split (new char [] {Path.PathSeparator}, StringSplitOptions.RemoveEmptyEntries)) { + if (pathsTable.ContainsKey (extn_path)) + continue; + + if (!Directory.Exists (extn_path)) { + project.ParentEngine.LogMessage (MessageImportance.Low, "Extension path '{0}' not found, ignoring.", extn_path); + continue; + } + + pathsTable [extn_path] = extn_path; + yield return extn_path; + } + } + public string Condition { get { string s = importElement.GetAttribute ("Condition"); @@ -100,6 +268,10 @@ namespace Microsoft.Build.BuildEngine { public string ProjectPath { get { return importElement.GetAttribute ("Project"); } } + + internal string ContainedInProjectFileName { + get { return originalProject != null ? originalProject.FullFileName : project.FullFileName; } + } } }