PageRenderTime 25ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/main/src/addins/MonoDevelop.Autotools/SolutionMakefileHandler.cs

https://github.com/jfcantin/monodevelop
C# | 334 lines | 247 code | 52 blank | 35 comment | 51 complexity | 9867d9a0392900b6aeecb5d5b83790ce MD5 | raw file
  1. /*
  2. Copyright (C) 2006 Matthias Braun <matze@braunis.de>
  3. Scott Ellington <scott.ellington@gmail.com>
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with this library; if not, write to the
  14. Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  15. Boston, MA 02111-1307, USA.
  16. */
  17. using System;
  18. using System.IO;
  19. using System.Text;
  20. using System.Collections;
  21. using System.Collections.Generic;
  22. using MonoDevelop.Projects;
  23. using MonoDevelop.Core;
  24. namespace MonoDevelop.Autotools
  25. {
  26. public class SolutionMakefileHandler : IMakefileHandler
  27. {
  28. bool generateAutotools = true;
  29. // Recurses into children and tests if they are deployable.
  30. public bool CanDeploy (SolutionItem entry, MakefileType type)
  31. {
  32. return entry is SolutionFolder;
  33. }
  34. public Makefile Deploy (AutotoolsContext ctx, SolutionItem entry, IProgressMonitor monitor)
  35. {
  36. generateAutotools = ctx.MakefileType == MakefileType.AutotoolsMakefile;
  37. monitor.BeginTask ( GettextCatalog.GetString (
  38. "Creating {0} for Solution {1}",
  39. generateAutotools ? "Makefile.am" : "Makefile", entry.Name), 1 );
  40. Makefile solutionMakefile = new Makefile ();
  41. StringBuilder solutionTop = new StringBuilder ();
  42. try
  43. {
  44. SolutionFolder solutionFolder = (SolutionFolder) entry;
  45. string targetDirectory = solutionFolder.BaseDirectory;
  46. StringBuilder subdirs = new StringBuilder();
  47. subdirs.Append ("#Warning: This is an automatically generated file, do not edit!\n");
  48. if (!generateAutotools) {
  49. solutionTop.AppendFormat ("top_srcdir={0}\n", FileService.AbsoluteToRelativePath (
  50. entry.BaseDirectory, ctx.TargetSolution.BaseDirectory));
  51. solutionTop.Append ("include $(top_srcdir)/config.make\n");
  52. solutionTop.Append ("include $(top_srcdir)/Makefile.include\n");
  53. solutionTop.Append ("include $(top_srcdir)/rules.make\n\n");
  54. solutionTop.Append ("#include $(top_srcdir)/custom-hooks.make\n\n");
  55. }
  56. ArrayList children = new ArrayList ();
  57. foreach ( SolutionConfiguration config in solutionFolder.ParentSolution.Configurations )
  58. {
  59. if ( !ctx.IsSupportedConfiguration ( config.Id ) ) continue;
  60. if (generateAutotools)
  61. subdirs.AppendFormat ( "if {0}\n", "ENABLE_" + ctx.EscapeAndUpperConfigName (config.Id));
  62. else
  63. subdirs.AppendFormat ( "ifeq ($(CONFIG),{0})\n", ctx.EscapeAndUpperConfigName (config.Id));
  64. subdirs.Append (" SUBDIRS = ");
  65. foreach (SolutionItem ce in CalculateSubDirOrder (ctx, solutionFolder, config))
  66. {
  67. string baseDirectory;
  68. if (!(ce is SolutionEntityItem) && !(ce is SolutionFolder))
  69. continue;
  70. // Ignore projects which can't be deployed
  71. IMakefileHandler handler = AutotoolsContext.GetMakefileHandler (ce, ctx.MakefileType);
  72. if (handler == null)
  73. continue;
  74. baseDirectory = ce.BaseDirectory;
  75. if (solutionFolder.BaseDirectory == baseDirectory) {
  76. subdirs.Append (" . ");
  77. } else {
  78. if (!baseDirectory.StartsWith (solutionFolder.BaseDirectory) )
  79. throw new Exception ( GettextCatalog.GetString (
  80. "Child projects must be in sub-directories of their parent") );
  81. // add the subdirectory to the list
  82. string path = FileService.AbsoluteToRelativePath (targetDirectory, baseDirectory);
  83. if (path.StartsWith ("." + Path.DirectorySeparatorChar) )
  84. path = path.Substring (2);
  85. AutotoolsContext.CheckSpaces (path);
  86. subdirs.Append (" ");
  87. subdirs.Append ( AutotoolsContext.EscapeStringForAutomake (path) );
  88. }
  89. if (!children.Contains (ce))
  90. children.Add ( ce );
  91. }
  92. subdirs.Append ( "\nendif\n" );
  93. }
  94. solutionTop.Append ( subdirs.ToString () );
  95. string includedProject = null;
  96. // deploy recursively
  97. foreach (SolutionItem ce in children)
  98. {
  99. IMakefileHandler handler = AutotoolsContext.GetMakefileHandler ( ce, ctx.MakefileType );
  100. Makefile makefile;
  101. string outpath;
  102. if ( handler != null && handler.CanDeploy ( ce, ctx.MakefileType ) )
  103. {
  104. ctx.RegisterBuiltProject (ce);
  105. makefile = handler.Deploy ( ctx, ce, monitor );
  106. if (targetDirectory == ce.BaseDirectory)
  107. {
  108. if (includedProject != null)
  109. throw new Exception ( GettextCatalog.GetString (
  110. "More than 1 project in the same directory as the top-level solution is not supported."));
  111. // project is in the solution directory
  112. string projectMakefileName = ce.Name + ".make";
  113. includedProject = String.Format ("include {0}", projectMakefileName);
  114. outpath = Path.Combine (targetDirectory, projectMakefileName);
  115. ctx.AddGeneratedFile (outpath);
  116. if (!generateAutotools)
  117. solutionMakefile.SetVariable ("EXTRA_DIST", projectMakefileName);
  118. } else {
  119. makefile.AppendToVariable ("EXTRA_DIST", generateAutotools ? String.Empty : "Makefile");
  120. outpath = Path.Combine (ce.BaseDirectory, "Makefile");
  121. if (generateAutotools) {
  122. ctx.AddAutoconfFile (outpath);
  123. outpath = outpath + ".am";
  124. } else {
  125. makefile.Append ("install: install-local\nuninstall: uninstall-local\nclean: clean-local\n");
  126. if (ce is SolutionFolder)
  127. //non TargetCombine
  128. makefile.Append ("dist-local: dist-local-recursive\n");
  129. else
  130. makefile.Append ("include $(top_srcdir)/rules.make\n");
  131. }
  132. ctx.AddGeneratedFile (outpath);
  133. }
  134. StreamWriter writer = new StreamWriter (outpath);
  135. makefile.Write ( writer );
  136. writer.Close ();
  137. }
  138. else {
  139. monitor.Log .WriteLine("Project '{0}' skipped.", ce.Name);
  140. }
  141. }
  142. if (includedProject != null) {
  143. solutionTop.Append (GettextCatalog.GetString ("\n# Include project specific makefile\n"));
  144. solutionTop.Append (includedProject);
  145. }
  146. if (generateAutotools) {
  147. solutionMakefile.Append (solutionTop.ToString ());
  148. } else {
  149. TemplateEngine templateEngine = new TemplateEngine ();
  150. templateEngine.Variables ["MAKEFILE_SOLUTION_TOP"] = solutionTop.ToString ();
  151. Stream stream = ctx.GetTemplateStream ("Makefile.solution.template");
  152. StreamReader reader = new StreamReader (stream);
  153. StringWriter sw = new StringWriter ();
  154. templateEngine.Process (reader, sw);
  155. reader.Close ();
  156. solutionMakefile.Append (sw.ToString ());
  157. if (solutionFolder.IsRoot) {
  158. // Emit dist and distcheck targets only for TargetCombine
  159. reader = new StreamReader (Path.Combine (ctx.TemplateDir, "make-dist.targets"));
  160. solutionMakefile.Append (reader.ReadToEnd ());
  161. reader.Close ();
  162. }
  163. }
  164. monitor.Step (1);
  165. }
  166. finally
  167. {
  168. monitor.EndTask ();
  169. }
  170. return solutionMakefile;
  171. }
  172. // utility function for finding the correct order to process directories
  173. List<SolutionItem> CalculateSubDirOrder (AutotoolsContext ctx, SolutionFolder folder, SolutionConfiguration config)
  174. {
  175. List<SolutionItem> resultOrder = new List<SolutionItem>();
  176. Set<SolutionItem> dependenciesMet = new Set<SolutionItem>();
  177. Set<SolutionItem> inResult = new Set<SolutionItem>();
  178. // We don't have to worry about projects built in parent combines
  179. dependenciesMet.Union (ctx.GetBuiltProjects ());
  180. bool added;
  181. string notMet;
  182. do
  183. {
  184. added = false;
  185. notMet = null;
  186. List<SolutionItem> items = new List<SolutionItem> ();
  187. GetSubItems (items, folder);
  188. foreach (SolutionItem item in items)
  189. {
  190. Set<SolutionItem> references, provides;
  191. if (inResult.Contains (item))
  192. continue;
  193. if (item is SolutionEntityItem)
  194. {
  195. SolutionEntityItem entry = (SolutionEntityItem) item;
  196. if (!config.BuildEnabledForItem (entry))
  197. continue;
  198. references = new Set<SolutionItem> ();
  199. provides = new Set<SolutionItem>();
  200. references.Union (entry.GetReferencedItems (config.Selector));
  201. provides.Add (entry);
  202. }
  203. else if (item is SolutionFolder) {
  204. GetAllProjects ((SolutionFolder) item, config, out provides, out references);
  205. }
  206. else
  207. continue;
  208. if (dependenciesMet.ContainsSet (references) )
  209. {
  210. resultOrder.Add (item);
  211. dependenciesMet.Union(provides);
  212. inResult.Add(item);
  213. added = true;
  214. }
  215. else notMet = item.Name;
  216. }
  217. } while (added);
  218. if (notMet != null)
  219. throw new Exception("Impossible to find a solution order that satisfies project references for '" + notMet + "'");
  220. return resultOrder;
  221. }
  222. void GetSubItems (List<SolutionItem> list, SolutionFolder folder)
  223. {
  224. // This method returns the subitems of a folder.
  225. // If a folder does not match a phisical folder, it will be ignored.
  226. foreach (SolutionItem item in folder.Items) {
  227. if (item is SolutionFolder) {
  228. if (item.BaseDirectory != folder.BaseDirectory)
  229. list.Add (item);
  230. else
  231. GetSubItems (list, (SolutionFolder) item);
  232. }
  233. else
  234. list.Add (item);
  235. }
  236. }
  237. // cache references
  238. Hashtable combineProjects = new Hashtable();
  239. Hashtable combineReferences = new Hashtable();
  240. /**
  241. * returns a set of projects that a combine contains and a set of projects
  242. * that are referenced from combine projects but not part of the combine
  243. */
  244. void GetAllProjects (SolutionFolder folder, SolutionConfiguration config, out Set<SolutionItem> projects, out Set<SolutionItem> references)
  245. {
  246. List<SolutionItem> subitems = new List<SolutionItem> ();
  247. GetSubItems (subitems, folder);
  248. projects = (Set<SolutionItem>) combineProjects [folder];
  249. if (projects != null) {
  250. references = (Set<SolutionItem>) combineReferences [folder];
  251. return;
  252. }
  253. projects = new Set<SolutionItem>();
  254. references = new Set<SolutionItem>();
  255. foreach (SolutionItem item in subitems)
  256. {
  257. if (item is SolutionEntityItem)
  258. {
  259. SolutionEntityItem entry = (SolutionEntityItem) item;
  260. if (!config.BuildEnabledForItem (entry))
  261. continue;
  262. projects.Add (entry);
  263. references.Union (entry.GetReferencedItems (config.Selector));
  264. }
  265. else if (item is SolutionFolder)
  266. {
  267. Set<SolutionItem> subProjects;
  268. Set<SolutionItem> subReferences;
  269. GetAllProjects ((SolutionFolder)item, config, out subProjects, out subReferences);
  270. projects.Union (subProjects);
  271. references.Union (subReferences);
  272. }
  273. }
  274. references.Without (projects);
  275. combineProjects [folder] = projects;
  276. combineReferences [folder] = references;
  277. }
  278. }
  279. }