using System; using System.Collections; using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Serialization; using MonoDevelop.Prj2Make.Schema.Csproj; using MonoDevelop.Projects; using MonoDevelop.Projects.Text; using MonoDevelop.Core; using MonoDevelop.Ide.Gui; using MonoDevelop.Core.Assemblies; using MonoDevelop.CSharp.Project; namespace MonoDevelop.Prj2Make { public class SlnMaker { public static string slash; Hashtable projNameInfo = new Hashtable(); Hashtable projGuidInfo = new Hashtable(); private string prjxFileName; private string m_strSlnVer; private string m_strCsprojVer; private bool m_bIsUnix; private bool m_bIsMcs; private bool m_bIsUsingLib; TargetFramework fx = Runtime.SystemAssemblyService.GetTargetFramework (TargetFrameworkMoniker.NET_1_1); // Flag use to determine if the LIB variable will be used in // the Makefile that prj2make generates public bool IsUsingLib { get{ return m_bIsUsingLib; } set{ m_bIsUsingLib = value; } } // Determines if the makefile is intended for nmake or gmake for urposes of path separator character public bool IsUnix { get{ return m_bIsUnix; } set{ m_bIsUnix = value; } } // Determines if using MCS or CSC public bool IsMcs { get{ return m_bIsMcs; } set{ m_bIsMcs = value; } } public string SlnVersion { get { return m_strSlnVer; } set { m_strSlnVer = value; } } public string CsprojVersion { get { return m_strCsprojVer; } set { m_strCsprojVer = value; } } // Shuld contain the file name // of the most resent prjx generation public string PrjxFileName { get { return prjxFileName; } } // Default constructor public SlnMaker() { m_bIsUnix = false; m_bIsMcs = false; m_bIsUsingLib = false; } // Utility function to determine the sln file version protected string GetSlnFileVersion(string strInSlnFile) { string strVersion = null; string strInput = null; Match match; FileStream fis = new FileStream(strInSlnFile, FileMode.Open, FileAccess.Read, FileShare.Read); StreamReader reader = new StreamReader(fis); strInput = reader.ReadLine(); match = SlnVersionRegex.Match(strInput); if (match.Success) { strVersion = match.Groups[1].Value; } // Close the stream reader.Close(); // Close the File Stream fis.Close(); return strVersion; } // Utility function to determine the csproj file version protected string GetCsprojFileVersion(string strInCsprojFile) { string strRetVal = null; XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(strInCsprojFile); strRetVal = xmlDoc.SelectSingleNode("/VisualStudioProject/CSHARP/@ProductVersion").Value; return strRetVal; } protected void ParseMsCsProj(string fname) { string projectName = System.IO.Path.GetFileNameWithoutExtension (fname); string csprojPath = System.IO.Path.GetFileName (fname); string projectGuid = ""; CsprojInfo pi = new CsprojInfo (m_bIsUnix, m_bIsMcs, projectName, projectGuid, csprojPath); projNameInfo[projectName] = pi; projGuidInfo[projectGuid] = pi; } protected void ParseSolution(string fname, IProgressMonitor monitor) { FileStream fis = new FileStream(fname,FileMode.Open, FileAccess.Read, FileShare.Read); using (StreamReader reader = new StreamReader(fis)) { while (true) { string s = reader.ReadLine(); Match match; match = ProjectRegex.Match(s); if (match.Success) { string projectName = match.Groups[2].Value; string csprojPath = match.Groups[3].Value; string projectGuid = match.Groups[4].Value; try { if (csprojPath.EndsWith (".csproj") && !csprojPath.StartsWith("http://")) { csprojPath = MapPath (Path.GetDirectoryName (fname), csprojPath); CsprojInfo pi = new CsprojInfo (m_bIsUnix, m_bIsMcs, projectName, projectGuid, csprojPath); projNameInfo[projectName] = pi; projGuidInfo[projectGuid] = pi; } } catch (Exception ex) { Console.WriteLine (GettextCatalog.GetString ("Could not import project:") + csprojPath); Console.WriteLine (ex.ToString ()); monitor.ReportError (GettextCatalog.GetString ("Could not import project:") + csprojPath, ex); throw; } } if (s.StartsWith("Global")) { break; } } } } public SolutionEntityItem CreatePrjxFromCsproj (string csprojFileName, IProgressMonitor monitor) { try { FileFormat format = Services.ProjectService.FileFormats.GetFileFormatForFile (csprojFileName, typeof(SolutionEntityItem)); if (format != null && format.Id != "VS2003ProjectFileFormat") return (SolutionEntityItem) Services.ProjectService.ReadSolutionItem (monitor, csprojFileName); MonoDevelop.Prj2Make.Schema.Csproj.VisualStudioProject csprojObj = null; monitor.BeginTask (GettextCatalog.GetString ("Importing project: ") + csprojFileName, 4); DotNetAssemblyProject prjxObj = new DotNetAssemblyProject ("C#", null, null); prjxFileName = String.Format ("{0}.mdp", Path.Combine (Path.GetDirectoryName (csprojFileName), Path.GetFileNameWithoutExtension (csprojFileName)) ); // Load the csproj using (TextFileReader fsIn = new TextFileReader (csprojFileName)) { XmlSerializer xmlDeSer = new XmlSerializer (typeof(VisualStudioProject)); csprojObj = (VisualStudioProject) xmlDeSer.Deserialize (fsIn); } monitor.Step (1); // Begin prjxObj population prjxObj.FileName = prjxFileName; prjxObj.Name = Path.GetFileNameWithoutExtension(csprojFileName); prjxObj.Description = ""; prjxObj.NewFileSearch = NewFileSearch.None; prjxObj.DefaultNamespace = csprojObj.CSHARP.Build.Settings.RootNamespace; GetContents (prjxObj, csprojObj.CSHARP.Files.Include, prjxObj.Files, monitor); monitor.Step (1); GetReferences (prjxObj, csprojObj.CSHARP.Build.References, prjxObj.References, monitor); monitor.Step (1); prjxObj.Configurations.Clear (); foreach (Config cblock in csprojObj.CSHARP.Build.Settings.Config) { prjxObj.Configurations.Add (CreateConfigurationBlock ( prjxObj, cblock, csprojObj.CSHARP.Build.Settings.AssemblyName, csprojObj.CSHARP.Build.Settings.OutputType, monitor )); } monitor.Step (1); return prjxObj; } catch (Exception ex) { monitor.ReportError (GettextCatalog.GetString ("Could not import project:") + csprojFileName, ex); throw; } finally { monitor.EndTask (); } } public Solution MsSlnToCmbxHelper (string slnFileName, IProgressMonitor monitor) { Solution solution = new Solution(); monitor.BeginTask (GettextCatalog.GetString ("Importing solution"), 2); try { // We invoke the ParseSolution // by passing the file obtained ParseSolution (slnFileName, monitor); // Create all of the prjx files form the csproj files monitor.BeginTask (null, projNameInfo.Values.Count * 2); foreach (CsprojInfo pi in projNameInfo.Values) { string mappedPath = MapPath (Path.GetDirectoryName (slnFileName), pi.csprojpath); if (mappedPath == null) { monitor.Step (2); monitor.ReportWarning (GettextCatalog.GetString ("Project file not found: ") + pi.csprojpath); continue; } SolutionEntityItem prj; if (pi.NeedsConversion) prj = CreatePrjxFromCsproj (mappedPath, monitor); else prj = (DotNetProject) Services.ProjectService.ReadSolutionItem (monitor, mappedPath); if (prj == null) return null; monitor.Step (1); solution.RootFolder.Items.Add (prj); foreach (ItemConfiguration conf in prj.Configurations) { if (!solution.GetConfigurations ().Contains (conf.Id)) solution.AddConfiguration (conf.Id, false); } monitor.Step (1); } monitor.EndTask (); monitor.Step (1); solution.SetLocation (Path.GetDirectoryName (slnFileName), Path.GetFileNameWithoutExtension(slnFileName)); monitor.Step (1); return solution; } catch (Exception e) { monitor.ReportError (GettextCatalog.GetString ("The solution could not be imported."), e); throw; } finally { monitor.EndTask (); } } protected void GetReferences (Project project, MonoDevelop.Prj2Make.Schema.Csproj.Reference[] References, ProjectReferenceCollection references, IProgressMonitor monitor) { if (References == null || References.Length == 0) return; monitor.BeginTask (null, 5 + References.Length); try { // Get the GAC path string strBasePathMono1_0 = GetPackageDirectory ("mono", "mono/1.0"); monitor.Step (1); string strBasePathGtkSharp = GetPackageDirectory ("gtk-sharp", "mono/gtk-sharp"); monitor.Step (1); string strBasePathGtkSharp2_0 = GetPackageDirectory ("gtk-sharp-2.0", "mono/gtk-sharp-2.0"); monitor.Step (1); string strBasePathGeckoSharp = GetPackageDirectory ("gecko-sharp", "mono/gecko-sharp"); monitor.Step (1); string strBasePathGeckoSharp2_0 = GetPackageDirectory ("gecko-sharp-2.0", "mono/gecko-sharp-2.0"); string[] monoLibs = new string [] { strBasePathMono1_0, strBasePathGtkSharp2_0, strBasePathGtkSharp, strBasePathGeckoSharp2_0, strBasePathGeckoSharp }; // Iterate through the reference collection of the csproj file foreach (MonoDevelop.Prj2Make.Schema.Csproj.Reference rf in References) { monitor.Step (1); ProjectReference rfOut = null; if (rf.Package != null && rf.Package.Length != 0) { rfOut = new ProjectReference (MonoDevelop.Projects.ReferenceType.Project, Path.GetFileName (rf.Name)); rfOut.LocalCopy = true; references.Add (rfOut); } else if (rf.AssemblyName != null) { string rname = rf.AssemblyName; if (rname == "System.XML") rname = "System.Xml"; string oref = Runtime.SystemAssemblyService.DefaultAssemblyContext.GetAssemblyFullName (rname, fx); if (oref == null) { if (rf.HintPath != null) { string asm = MapPath (project.ItemDirectory, rf.HintPath); if (!System.IO.File.Exists (asm)) monitor.ReportWarning (GettextCatalog.GetString ("Referenced assembly not found: ") + asm); ProjectReference aref = new ProjectReference (MonoDevelop.Projects.ReferenceType.Assembly, asm); references.Add (aref); continue; } monitor.ReportWarning (GettextCatalog.GetString ("Assembly reference could not be imported: ") + rf.AssemblyName); continue; } rfOut = new ProjectReference (MonoDevelop.Projects.ReferenceType.Package, oref); rfOut.LocalCopy = true; references.Add (rfOut); } else if (rf.HintPath != null) { // HACK - under Unix filenames are case sensitive // Under Windows there's no agreement on Xml vs XML ;-) if (Path.GetFileName (rf.HintPath) == "System.XML.dll") { ProjectReference pref = GetMonoReferece (strBasePathMono1_0, "System.Xml.dll"); if (pref != null) { references.Add (pref); continue; } } else { foreach (string libDir in monoLibs) { if (libDir == null) continue; if (rf.HintPath == null) continue; rfOut = GetMonoReferece (libDir, rf.HintPath); if (rfOut != null) break; } if (rfOut == null) { rfOut = new ProjectReference (MonoDevelop.Projects.ReferenceType.Package, Path.GetFileName (rf.HintPath)); rfOut.LocalCopy = true; } references.Add (rfOut); } } else { monitor.ReportWarning (GettextCatalog.GetString ("Assembly reference could not be imported: ") + rf.Name); } } } finally { monitor.EndTask (); } } string GetPackageDirectory (string package, string subdir) { string dir = MonoDevelop.Prj2Make.PkgConfigInvoker.GetPkgVariableValue (package, "libdir"); return dir != null ? Path.Combine (dir.TrimEnd(), subdir) : null; } ProjectReference GetMonoReferece (string libPath, string reference) { string strRefFileName = Path.Combine (libPath, Path.GetFileName (reference)); // Test to see if file exist in GAC location if (System.IO.File.Exists (strRefFileName)) { ProjectReference rfOut = new ProjectReference (MonoDevelop.Projects.ReferenceType.Package, Runtime.SystemAssemblyService.DefaultAssemblyContext.GetAssemblyFullName (strRefFileName, fx)); rfOut.LocalCopy = true; return rfOut; } return null; } protected void GetContents (MonoDevelop.Projects.Project project, MonoDevelop.Prj2Make.Schema.Csproj.File[] Include, ProjectFileCollection files, IProgressMonitor monitor) { if (Include == null || Include.Length == 0) return; // Iterate through the file collection of the csproj file foreach(MonoDevelop.Prj2Make.Schema.Csproj.File fl in Include) { ProjectFile flOut = new ProjectFile (); string name; if ((fl.Link == null) || (fl.Link.Length == 0)) { name = MapPath (project.BaseDirectory, fl.RelPath); } else { name = MapPath (null, fl.Link); } if (name == null) { monitor.ReportWarning (GettextCatalog.GetString ("Can't import file: ") + fl.RelPath); continue; } flOut.Name = name; // Adding here as GetDefaultResourceIdInternal needs flOut.Project files.Add (flOut); switch (fl.SubType) { case "Code": flOut.Subtype = Subtype.Code; break; } switch (fl.BuildAction) { case MonoDevelop.Prj2Make.Schema.Csproj.FileBuildAction.Compile: flOut.BuildAction = BuildAction.Compile; break; case MonoDevelop.Prj2Make.Schema.Csproj.FileBuildAction.Content: flOut.BuildAction = BuildAction.Content; break; case MonoDevelop.Prj2Make.Schema.Csproj.FileBuildAction.EmbeddedResource: flOut.BuildAction = BuildAction.EmbeddedResource; break; case MonoDevelop.Prj2Make.Schema.Csproj.FileBuildAction.None: flOut.BuildAction = BuildAction.None; break; } // DependentUpon is relative to flOut flOut.DependsOn = MapPath (Path.GetDirectoryName (flOut.Name), fl.DependentUpon); flOut.Data = ""; } } protected SolutionItemConfiguration CreateConfigurationBlock (MonoDevelop.Projects.DotNetProject project, Config ConfigBlock, string AssemblyName, string OuputType, IProgressMonitor monitor) { DotNetProjectConfiguration confObj = project.CreateConfiguration (ConfigBlock.Name) as DotNetProjectConfiguration; confObj.RunWithWarnings = false; confObj.DebugMode = ConfigBlock.DebugSymbols; project.CompileTarget = (CompileTarget) Enum.Parse (typeof(CompileTarget), OuputType, true); string dir = MapPath (project.BaseDirectory, ConfigBlock.OutputPath); if (dir == null) { dir = Path.Combine ("bin", ConfigBlock.Name); 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)); } confObj.OutputDirectory = dir; confObj.OutputAssembly = AssemblyName; CSharpCompilerParameters compilerParams = new CSharpCompilerParameters (); compilerParams.WarningLevel = ConfigBlock.WarningLevel; compilerParams.NoWarnings = ""; compilerParams.Optimize = ConfigBlock.Optimize; compilerParams.DefineSymbols = ConfigBlock.DefineConstants; compilerParams.UnsafeCode = ConfigBlock.AllowUnsafeBlocks; compilerParams.GenerateOverflowChecks = ConfigBlock.CheckForOverflowUnderflow; return confObj; } internal static string MapPath (string basePath, string relPath) { if (relPath == null || relPath.Length == 0) return null; string path = relPath; if (Path.DirectorySeparatorChar != '\\') path = path.Replace ("\\", "/"); if (char.IsLetter (path [0]) && path.Length > 1 && path[1] == ':') return null; if (basePath != null) path = Path.Combine (basePath, path); if (System.IO.File.Exists (path)){ return Path.GetFullPath (path); } if (Path.IsPathRooted (path)) { // Windows paths are case-insensitive. When mapping an absolute path // we can try to find the correct case for the path. string[] names = path.Substring (1).Split ('/'); string part = "/"; for (int n=0; n