* Makefile.am:
[monodevelop.git] / main / src / addins / prj2make-sharp-lib / MsPrjHelper.cs
blobeddb4a18d92e62d6589e627ff1738b979504448e
1 using System;
2 using System.Collections;
3 using System.IO;
4 using System.Text;
5 using System.Text.RegularExpressions;
6 using System.Xml;
7 using System.Xml.Serialization;
9 using MonoDevelop.Prj2Make.Schema.Csproj;
10 using MonoDevelop.Projects;
11 using MonoDevelop.Projects.Text;
12 using MonoDevelop.Core;
13 using MonoDevelop.Ide.Gui;
15 using CSharpBinding;
17 namespace MonoDevelop.Prj2Make
19 public class SlnMaker
21 public static string slash;
22 Hashtable projNameInfo = new Hashtable();
23 Hashtable projGuidInfo = new Hashtable();
24 private string prjxFileName;
25 private string m_strSlnVer;
26 private string m_strCsprojVer;
27 private bool m_bIsUnix;
28 private bool m_bIsMcs;
29 private bool m_bIsUsingLib;
31 // Flag use to determine if the LIB variable will be used in
32 // the Makefile that prj2make generates
33 public bool IsUsingLib
35 get{ return m_bIsUsingLib; }
36 set{ m_bIsUsingLib = value; }
40 // Determines if the makefile is intended for nmake or gmake for urposes of path separator character
41 public bool IsUnix
43 get{ return m_bIsUnix; }
44 set{ m_bIsUnix = value; }
47 // Determines if using MCS or CSC
48 public bool IsMcs
50 get{ return m_bIsMcs; }
51 set{ m_bIsMcs = value; }
54 public string SlnVersion
56 get { return m_strSlnVer; }
57 set { m_strSlnVer = value; }
60 public string CsprojVersion
62 get { return m_strCsprojVer; }
63 set { m_strCsprojVer = value; }
66 // Shuld contain the file name
67 // of the most resent prjx generation
68 public string PrjxFileName {
69 get { return prjxFileName; }
72 // Default constructor
73 public SlnMaker()
75 m_bIsUnix = false;
76 m_bIsMcs = false;
77 m_bIsUsingLib = false;
80 // Utility function to determine the sln file version
81 protected string GetSlnFileVersion(string strInSlnFile)
83 string strVersion = null;
84 string strInput = null;
85 Match match;
86 FileStream fis = new FileStream(strInSlnFile, FileMode.Open, FileAccess.Read, FileShare.Read);
87 StreamReader reader = new StreamReader(fis);
88 strInput = reader.ReadLine();
90 match = SlnVersionRegex.Match(strInput);
91 if (match.Success)
93 strVersion = match.Groups[1].Value;
96 // Close the stream
97 reader.Close();
99 // Close the File Stream
100 fis.Close();
102 return strVersion;
105 // Utility function to determine the csproj file version
106 protected string GetCsprojFileVersion(string strInCsprojFile)
108 string strRetVal = null;
109 XmlDocument xmlDoc = new XmlDocument();
111 xmlDoc.Load(strInCsprojFile);
112 strRetVal = xmlDoc.SelectSingleNode("/VisualStudioProject/CSHARP/@ProductVersion").Value;
114 return strRetVal;
117 protected void ParseMsCsProj(string fname)
119 string projectName = System.IO.Path.GetFileNameWithoutExtension (fname);
120 string csprojPath = System.IO.Path.GetFileName (fname);
121 string projectGuid = "";
123 CsprojInfo pi = new CsprojInfo (m_bIsUnix, m_bIsMcs, projectName, projectGuid, csprojPath);
125 projNameInfo[projectName] = pi;
126 projGuidInfo[projectGuid] = pi;
129 protected void ParseSolution(string fname, IProgressMonitor monitor)
131 FileStream fis = new FileStream(fname,FileMode.Open, FileAccess.Read, FileShare.Read);
132 using (StreamReader reader = new StreamReader(fis)) {
133 while (true)
135 string s = reader.ReadLine();
136 Match match;
138 match = ProjectRegex.Match(s);
139 if (match.Success)
141 string projectName = match.Groups[2].Value;
142 string csprojPath = match.Groups[3].Value;
143 string projectGuid = match.Groups[4].Value;
145 try {
146 if (csprojPath.EndsWith (".csproj") && !csprojPath.StartsWith("http://"))
148 csprojPath = MapPath (Path.GetDirectoryName (fname), csprojPath);
149 CsprojInfo pi = new CsprojInfo (m_bIsUnix, m_bIsMcs, projectName, projectGuid, csprojPath);
150 projNameInfo[projectName] = pi;
151 projGuidInfo[projectGuid] = pi;
153 } catch (Exception ex) {
154 Console.WriteLine (GettextCatalog.GetString ("Could not import project:") + csprojPath);
155 Console.WriteLine (ex.ToString ());
156 monitor.ReportError (GettextCatalog.GetString ("Could not import project:") + csprojPath, ex);
157 throw;
161 if (s.StartsWith("Global"))
163 break;
169 public DotNetProject CreatePrjxFromCsproj (string csprojFileName, IProgressMonitor monitor)
171 try {
172 MonoDevelop.Prj2Make.Schema.Csproj.VisualStudioProject csprojObj = null;
174 monitor.BeginTask (GettextCatalog.GetString ("Importing project: ") + csprojFileName, 4);
176 DotNetProject prjxObj = new DotNetProject ("C#", null, null);
178 prjxFileName = String.Format ("{0}.mdp",
179 Path.Combine (Path.GetDirectoryName (csprojFileName),
180 Path.GetFileNameWithoutExtension (csprojFileName))
183 // Load the csproj
184 using (TextFileReader fsIn = new TextFileReader (csprojFileName)) {
185 XmlSerializer xmlDeSer = new XmlSerializer (typeof(VisualStudioProject));
186 csprojObj = (VisualStudioProject) xmlDeSer.Deserialize (fsIn);
189 monitor.Step (1);
191 // Begin prjxObj population
192 prjxObj.FileName = prjxFileName;
193 prjxObj.Name = Path.GetFileNameWithoutExtension(csprojFileName);
194 prjxObj.Description = "";
195 prjxObj.NewFileSearch = NewFileSearch.None;
196 prjxObj.DefaultNamespace = csprojObj.CSHARP.Build.Settings.RootNamespace;
198 GetContents (prjxObj, csprojObj.CSHARP.Files.Include, prjxObj.Files, monitor);
199 monitor.Step (1);
201 GetReferences (csprojObj.CSHARP.Build.References, prjxObj.References, monitor);
202 monitor.Step (1);
204 prjxObj.Configurations.Clear ();
205 foreach (Config cblock in csprojObj.CSHARP.Build.Settings.Config)
207 prjxObj.Configurations.Add (CreateConfigurationBlock (
208 prjxObj,
209 cblock,
210 csprojObj.CSHARP.Build.Settings.AssemblyName,
211 csprojObj.CSHARP.Build.Settings.OutputType,
212 monitor
215 monitor.Step (1);
216 return prjxObj;
218 } catch (Exception ex) {
219 monitor.ReportError (GettextCatalog.GetString ("Could not import project:") + csprojFileName, ex);
220 throw;
221 } finally {
222 monitor.EndTask ();
226 public Solution MsSlnToCmbxHelper (string slnFileName, IProgressMonitor monitor)
228 Solution solution = new Solution();
230 monitor.BeginTask (GettextCatalog.GetString ("Importing solution"), 2);
233 // We invoke the ParseSolution
234 // by passing the file obtained
235 ParseSolution (slnFileName, monitor);
237 // Create all of the prjx files form the csproj files
238 monitor.BeginTask (null, projNameInfo.Values.Count * 2);
240 foreach (CsprojInfo pi in projNameInfo.Values) {
241 string mappedPath = MapPath (Path.GetDirectoryName (slnFileName), pi.csprojpath);
242 if (mappedPath == null) {
243 monitor.Step (2);
244 monitor.ReportWarning (GettextCatalog.GetString ("Project file not found: ") + pi.csprojpath);
245 continue;
247 DotNetProject prj = CreatePrjxFromCsproj (mappedPath, monitor);
248 if (prj == null)
249 return null;
251 monitor.Step (1);
252 solution.RootFolder.Items.Add (prj);
253 monitor.Step (1);
256 monitor.EndTask ();
257 monitor.Step (1);
259 solution.SetLocation (Path.GetDirectoryName (slnFileName), Path.GetFileNameWithoutExtension(slnFileName));
261 monitor.Step (1);
262 return solution;
264 catch (Exception e)
266 monitor.ReportError (GettextCatalog.GetString ("The solution could not be imported."), e);
267 throw;
269 finally
271 monitor.EndTask ();
275 protected void GetReferences (MonoDevelop.Prj2Make.Schema.Csproj.Reference[] References, ProjectReferenceCollection references, IProgressMonitor monitor)
277 if (References == null || References.Length == 0)
278 return;
280 monitor.BeginTask (null, 5 + References.Length);
282 try {
283 // Get the GAC path
284 string strBasePathMono1_0 = GetPackageDirectory ("mono", "mono/1.0");
286 monitor.Step (1);
288 string strBasePathGtkSharp = GetPackageDirectory ("gtk-sharp", "mono/gtk-sharp");
290 monitor.Step (1);
292 string strBasePathGtkSharp2_0 = GetPackageDirectory ("gtk-sharp-2.0", "mono/gtk-sharp-2.0");
294 monitor.Step (1);
296 string strBasePathGeckoSharp = GetPackageDirectory ("gecko-sharp", "mono/gecko-sharp");
298 monitor.Step (1);
300 string strBasePathGeckoSharp2_0 = GetPackageDirectory ("gecko-sharp-2.0", "mono/gecko-sharp-2.0");
302 string[] monoLibs = new string [] {
303 strBasePathMono1_0,
304 strBasePathGtkSharp2_0,
305 strBasePathGtkSharp,
306 strBasePathGeckoSharp2_0,
307 strBasePathGeckoSharp
310 // Iterate through the reference collection of the csproj file
311 foreach (MonoDevelop.Prj2Make.Schema.Csproj.Reference rf in References)
313 monitor.Step (1);
315 ProjectReference rfOut = null;
317 if (rf.Package != null && rf.Package.Length != 0)
319 rfOut = new ProjectReference (MonoDevelop.Projects.ReferenceType.Project, Path.GetFileName (rf.Name));
320 rfOut.LocalCopy = true;
321 references.Add (rfOut);
323 else if (rf.AssemblyName != null)
325 string rname = rf.AssemblyName;
326 if (rname == "System.XML")
327 rname = "System.Xml";
329 string oref = Runtime.SystemAssemblyService.GetAssemblyFullName (rname);
330 if (oref == null) {
331 monitor.ReportWarning (GettextCatalog.GetString ("Assembly reference could not be imported: ") + rf.AssemblyName);
332 continue;
334 rfOut = new ProjectReference (MonoDevelop.Projects.ReferenceType.Gac, oref);
335 rfOut.LocalCopy = true;
336 references.Add (rfOut);
338 else if (rf.HintPath != null)
340 // HACK - under Unix filenames are case sensitive
341 // Under Windows there's no agreement on Xml vs XML ;-)
342 if (Path.GetFileName (rf.HintPath) == "System.XML.dll")
344 ProjectReference pref = GetMonoReferece (strBasePathMono1_0, "System.Xml.dll");
345 if (pref != null) {
346 references.Add (pref);
347 continue;
349 } else {
350 foreach (string libDir in monoLibs) {
351 if (libDir == null) continue;
352 if (rf.HintPath == null)
353 continue;
354 rfOut = GetMonoReferece (libDir, rf.HintPath);
355 if (rfOut != null)
356 break;
359 if (rfOut == null) {
360 rfOut = new ProjectReference (MonoDevelop.Projects.ReferenceType.Gac, Path.GetFileName (rf.HintPath));
361 rfOut.LocalCopy = true;
363 references.Add (rfOut);
366 else {
367 monitor.ReportWarning (GettextCatalog.GetString ("Assembly reference could not be imported: ") + rf.Name);
370 } finally {
371 monitor.EndTask ();
375 string GetPackageDirectory (string package, string subdir)
377 string dir = MonoDevelop.Prj2Make.PkgConfigInvoker.GetPkgVariableValue (package, "libdir");
378 return dir != null ? Path.Combine (dir.TrimEnd(), subdir) : null;
381 ProjectReference GetMonoReferece (string libPath, string reference)
383 string strRefFileName = Path.Combine (libPath, Path.GetFileName (reference));
385 // Test to see if file exist in GAC location
386 if (System.IO.File.Exists (strRefFileName)) {
387 ProjectReference rfOut = new ProjectReference (MonoDevelop.Projects.ReferenceType.Gac, Runtime.SystemAssemblyService.GetAssemblyFullName (strRefFileName));
388 rfOut.LocalCopy = true;
389 return rfOut;
391 return null;
394 protected void GetContents (MonoDevelop.Projects.Project project, MonoDevelop.Prj2Make.Schema.Csproj.File[] Include, ProjectFileCollection files, IProgressMonitor monitor)
396 if (Include == null || Include.Length == 0)
397 return;
399 // Iterate through the file collection of the csproj file
400 foreach(MonoDevelop.Prj2Make.Schema.Csproj.File fl in Include)
402 ProjectFile flOut = new ProjectFile ();
404 string name;
405 if ((fl.Link == null) || (fl.Link.Length == 0)) {
406 name = MapPath (project.BaseDirectory, fl.RelPath);
407 } else {
408 name = MapPath (null, fl.Link);
411 if (name == null) {
412 monitor.ReportWarning (GettextCatalog.GetString ("Can't import file: ") + fl.RelPath);
413 continue;
415 flOut.Name = name;
416 // Adding here as GetDefaultResourceIdInternal needs flOut.Project
417 files.Add (flOut);
419 switch (fl.SubType)
421 case "Code":
422 flOut.Subtype = Subtype.Code;
423 break;
426 switch (fl.BuildAction)
428 case MonoDevelop.Prj2Make.Schema.Csproj.FileBuildAction.Compile:
429 flOut.BuildAction = BuildAction.Compile;
430 break;
431 case MonoDevelop.Prj2Make.Schema.Csproj.FileBuildAction.Content:
432 flOut.BuildAction = BuildAction.Content;
433 break;
434 case MonoDevelop.Prj2Make.Schema.Csproj.FileBuildAction.EmbeddedResource:
435 flOut.BuildAction = BuildAction.EmbeddedResource;
436 break;
437 case MonoDevelop.Prj2Make.Schema.Csproj.FileBuildAction.None:
438 flOut.BuildAction = BuildAction.None;
439 break;
441 // DependentUpon is relative to flOut
442 flOut.DependsOn = MapPath (Path.GetDirectoryName (flOut.Name), fl.DependentUpon);
443 flOut.Data = "";
447 protected SolutionItemConfiguration CreateConfigurationBlock (MonoDevelop.Projects.DotNetProject project, Config ConfigBlock, string AssemblyName, string OuputType, IProgressMonitor monitor)
449 DotNetProjectConfiguration confObj = project.CreateConfiguration (ConfigBlock.Name) as DotNetProjectConfiguration;
451 confObj.RunWithWarnings = false;
452 confObj.DebugMode = ConfigBlock.DebugSymbols;
453 project.CompileTarget = (CompileTarget) Enum.Parse (typeof(CompileTarget), OuputType, true);
455 string dir = MapPath (project.BaseDirectory, ConfigBlock.OutputPath);
456 if (dir == null) {
457 dir = "bin/" + ConfigBlock.Name;
458 monitor.ReportWarning (string.Format (GettextCatalog.GetString ("Output directory '{0}' can't be mapped to a local directory. The directory '{1}' will be used instead"), ConfigBlock.OutputPath, dir));
460 confObj.OutputDirectory = dir;
461 confObj.OutputAssembly = AssemblyName;
463 CSharpCompilerParameters compilerParams = new CSharpCompilerParameters ();
464 compilerParams.WarningLevel = ConfigBlock.WarningLevel;
465 compilerParams.NoWarnings = "";
466 compilerParams.Optimize = ConfigBlock.Optimize;
467 compilerParams.DefineSymbols = ConfigBlock.DefineConstants;
468 compilerParams.UnsafeCode = ConfigBlock.AllowUnsafeBlocks;
469 compilerParams.GenerateOverflowChecks = ConfigBlock.CheckForOverflowUnderflow;
471 return confObj;
474 internal static string MapPath (string basePath, string relPath)
476 if (relPath == null || relPath.Length == 0)
477 return null;
479 string path = relPath;
480 if (Path.DirectorySeparatorChar != '\\')
481 path = path.Replace ("\\", "/");
483 if (char.IsLetter (path [0]) && path.Length > 1 && path[1] == ':')
484 return null;
486 if (basePath != null)
487 path = Path.Combine (basePath, path);
489 if (System.IO.File.Exists (path)){
490 return Path.GetFullPath (path);
493 if (Path.IsPathRooted (path)) {
495 // Windows paths are case-insensitive. When mapping an absolute path
496 // we can try to find the correct case for the path.
498 string[] names = path.Substring (1).Split ('/');
499 string part = "/";
501 for (int n=0; n<names.Length; n++) {
502 string[] entries;
504 if (names [n] == ".."){
505 part = Path.GetFullPath (part + "/..");
506 continue;
509 entries = Directory.GetFileSystemEntries (part);
511 string fpath = null;
512 foreach (string e in entries) {
513 if (string.Compare (Path.GetFileName (e), names[n], true) == 0) {
514 fpath = e;
515 break;
518 if (fpath == null) {
519 // Part of the path does not exist. Can't do any more checking.
520 part = Path.GetFullPath (part);
521 for (; n < names.Length; n++)
522 part += "/" + names[n];
523 return part;
526 part = fpath;
528 return Path.GetFullPath (part);
529 } else {
530 return Path.GetFullPath (path);
534 // static regexes
535 static Regex projectRegex = null;
536 internal static Regex ProjectRegex {
537 get {
538 if (projectRegex == null)
539 projectRegex = new Regex(@"Project\(""(\{[^}]*\})""\) = ""(.*)"", ""(.*)"", ""(\{[^{]*\})""");
540 return projectRegex;
544 static Regex slnVersionRegex = null;
545 internal static Regex SlnVersionRegex {
546 get {
547 if (slnVersionRegex == null)
548 slnVersionRegex = new Regex (@"Microsoft Visual Studio Solution File, Format Version (\d?\d.\d\d)");
549 return slnVersionRegex;