[bitcode] Fix building -with-runtime-preset=bitcode. (#8969)
[mono-project.git] / msvc / scripts / genproj.cs
blob741dff4e06e32ba0bab7123464e277b6c986c8f0
1 //
2 // Consumes the order.xml file that contains a list of all the assemblies to build
3 // and produces a solution and the csproj files for it
4 //
5 // Currently this hardcodes a set of assemblies to build, the net-4.x series, but
6 // it can be extended to handle the command line tools.
7 //
8 // KNOWN ISSUES:
9 // * This fails to find matches for "System" and "System.xml" when processing the
10 // RabbitMQ executable, likely, because we do not process executables yet
12 // * Has not been tested in a while with the command line tools
14 using System;
15 using System.IO;
16 using System.Collections.Generic;
17 using System.Text;
18 using System.Globalization;
19 using System.Xml.Linq;
20 using System.Xml.XPath;
21 using System.Linq;
22 using System.Xml;
24 public enum Target {
25 Library, Exe, Module, WinExe
28 class SlnGenerator {
29 public static readonly string NewLine = "\r\n"; //Environment.NewLine; // "\n";
30 public SlnGenerator (string slnVersion)
32 Console.Error.WriteLine("// Requested sln version is {0}", slnVersion);
33 this.header = MakeHeader ("12.00", "15", "15.0.0.0");
36 const string project_start = "Project(\"{0}\") = \"{1}\", \"{2}\", \"{3}\""; // Note: No need to double up on {} around {2}
37 const string project_end = "EndProject";
39 public List<string> profiles = new List<string> {
40 "net_4_x",
41 "monodroid",
42 "monotouch",
43 "monotouch_tv",
44 "monotouch_watch",
45 "orbis",
46 "unreal",
47 "wasm",
48 "winaot",
49 "xammac",
52 public static readonly HashSet<string> observedProfiles = new HashSet<string> {
53 "net_4_x"
56 const string jay_vcxproj_guid = "{5D485D32-3B9F-4287-AB24-C8DA5B89F537}";
57 const string jay_sln_guid = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}";
59 public static Dictionary<string, HashSet<string>> profilesByGuid = new Dictionary<string, HashSet<string>> ();
60 public List<MsbuildGenerator.VsCsproj> libraries = new List<MsbuildGenerator.VsCsproj> ();
61 string header;
63 string MakeHeader (string formatVersion, string yearTag, string minimumVersion)
65 return string.Format (
66 "Microsoft Visual Studio Solution File, Format Version {0}" + NewLine +
67 "# Visual Studio {1}" + NewLine +
68 "MinimumVisualStudioVersion = {2}",
69 formatVersion, yearTag,
70 minimumVersion
74 public void Add (MsbuildGenerator.VsCsproj vsproj)
76 try {
77 libraries.Add (vsproj);
78 } catch (Exception ex) {
79 Console.Error.WriteLine ($"Error while adding library: {ex.Message}");
83 private void WriteProjectReference (StreamWriter sln, string prefixGuid, string library, string relativePath, string projectGuid, string[] dependencyGuids)
85 // HACK
86 library = library.Replace("-net_4_x", "");
87 sln.WriteLine (project_start, prefixGuid, library, relativePath, projectGuid);
89 if (dependencyGuids != null && dependencyGuids.Length > 0) {
90 sln.WriteLine ("\tProjectSection(ProjectDependencies) = postProject");
91 foreach (var guid in dependencyGuids)
92 sln.WriteLine ("\t\t{0} = {0}", guid);
93 sln.WriteLine ("\tEndProjectSection");
96 sln.WriteLine (project_end);
99 private void WriteProjectReference (StreamWriter sln, string slnFullPath, MsbuildGenerator.VsCsproj proj)
101 var unixProjFile = proj.csProjFilename.Replace ("\\", "/");
102 var fullProjPath = Path.GetFullPath (unixProjFile).Replace ("\\", "/");
103 var relativePath = MsbuildGenerator.GetRelativePath (slnFullPath, fullProjPath);
105 var dependencyGuids = new string[0];
106 if (proj.preBuildEvent.Contains ("jay"))
107 dependencyGuids = new [] { jay_vcxproj_guid };
109 foreach (var fd in MsbuildGenerator.fixed_dependencies) {
110 if (fullProjPath.EndsWith (fd.Item1)) {
111 dependencyGuids = dependencyGuids.Concat (fd.Item2).ToArray ();
115 if (dependencyGuids.Length > 0)
116 Console.WriteLine ($"Project {fullProjPath} has {dependencyGuids.Length} dependencies: {string.Join(", ", dependencyGuids)}");
118 WriteProjectReference(sln, "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}", proj.library, relativePath, proj.projectGuid, dependencyGuids);
121 private void WriteProjectConfigurationPlatforms (StreamWriter sln, string guid, string defaultPlatform)
123 var fallbackProfileNames = new List<string> ();
125 foreach (var profile in profiles) {
126 if (!observedProfiles.Contains (profile))
127 continue;
129 var platformToBuild = profile;
130 var isBuildEnabled = true;
132 HashSet<string> projectProfiles;
133 if (
134 !profilesByGuid.TryGetValue (guid, out projectProfiles) ||
135 !projectProfiles.Contains (platformToBuild)
137 fallbackProfileNames.Add (platformToBuild);
138 platformToBuild = defaultPlatform;
139 isBuildEnabled = false;
142 sln.WriteLine ("\t\t{0}.Debug|{1}.ActiveCfg = Debug|{2}", guid, profile, platformToBuild);
143 if (isBuildEnabled)
144 sln.WriteLine ("\t\t{0}.Debug|{1}.Build.0 = Debug|{2}", guid, profile, platformToBuild);
145 sln.WriteLine ("\t\t{0}.Release|{1}.ActiveCfg = Release|{2}", guid, profile, platformToBuild);
146 if (isBuildEnabled)
147 sln.WriteLine ("\t\t{0}.Release|{1}.Build.0 = Release|{2}", guid, profile, platformToBuild);
150 if (fallbackProfileNames.Count > 0)
151 Console.Error.WriteLine ($"// Project {guid} does not have profile(s) {string.Join(", ", fallbackProfileNames)} so using {defaultPlatform}");
154 public void Write (string filename)
156 var fullPath = Path.GetDirectoryName (filename) + "/";
158 using (var sln = new StreamWriter (filename)) {
159 sln.WriteLine ();
160 sln.WriteLine (header);
162 // Manually insert jay's vcxproj. We depend on jay.exe to perform build steps later.
163 WriteProjectReference (sln, jay_sln_guid, "jay", "mcs/jay/jay.vcxproj", jay_vcxproj_guid, null);
165 foreach (var proj in libraries) {
166 WriteProjectReference (sln, fullPath, proj);
169 sln.WriteLine ("Global");
171 sln.WriteLine ("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution");
172 foreach (var profile in profiles) {
173 if (!observedProfiles.Contains (profile))
174 continue;
176 sln.WriteLine ("\t\tDebug|{0} = Debug|{0}", profile, profile);
177 sln.WriteLine ("\t\tRelease|{0} = Release|{0}", profile, profile);
179 sln.WriteLine ("\tEndGlobalSection");
181 sln.WriteLine ("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution");
183 // Manually insert jay's configurations because they are different
184 WriteProjectConfigurationPlatforms (sln, jay_vcxproj_guid, "Win32");
186 foreach (var proj in libraries) {
187 WriteProjectConfigurationPlatforms (sln, proj.projectGuid, "net_4_x");
190 sln.WriteLine ("\tEndGlobalSection");
192 sln.WriteLine ("\tGlobalSection(SolutionProperties) = preSolution");
193 sln.WriteLine ("\t\tHideSolutionNode = FALSE");
194 sln.WriteLine ("\tEndGlobalSection");
196 sln.WriteLine ("EndGlobal");
200 internal bool ContainsProjectIdentifier (string projId)
202 return libraries.FindIndex (x => (x.library == projId)) >= 0;
205 public int Count { get { return libraries.Count; } }
208 class MsbuildGenerator {
209 static readonly string NewLine = SlnGenerator.NewLine;
210 static XmlNamespaceManager xmlns;
212 public const string profile_2_0 = "_2_0";
213 public const string profile_3_5 = "_3_5";
214 public const string profile_4_0 = "_4_0";
215 public const string profile_4_x = "_4_x";
217 public const string culevel_guid = "{E8E246BD-CD0C-4734-A3C2-7F44796EC47B}";
219 public static readonly (string, string)[] fixed_guids = new [] {
220 ("tools/culevel/culevel.csproj", culevel_guid)
223 public static readonly (string, string[])[] fixed_dependencies = new [] {
224 ("class/System.Web/System.Web.csproj", new [] { culevel_guid })
227 static void Usage ()
229 Console.Error.WriteLine ("// Invalid argument");
232 static string template;
233 static MsbuildGenerator ()
235 using (var input = new StreamReader ("csproj.tmpl")) {
236 template = input.ReadToEnd ();
239 xmlns = new XmlNamespaceManager (new NameTable ());
240 xmlns.AddNamespace ("x", "http://schemas.microsoft.com/developer/msbuild/2003");
243 // The directory as specified in order.xml
244 public string dir;
245 string library;
246 string projectGuid;
247 string fx_version;
249 XElement xproject;
250 public string CsprojFilename;
253 // Our base directory, this is relative to our exectution point mono/msvc/scripts
254 string base_dir;
255 string mcs_topdir;
257 static readonly Dictionary<string, string> GuidForCsprojCache = new Dictionary<string, string> ();
259 public string LibraryOutput, AbsoluteLibraryOutput;
261 public MsbuildGenerator (XElement xproject)
263 this.xproject = xproject;
264 dir = xproject.Attribute ("dir").Value;
265 library = xproject.Attribute ("library").Value;
266 // HACK:
267 var profileIndex = library.LastIndexOf("-");
268 var libraryWithoutProfile = library.Substring(0, profileIndex);
269 CsprojFilename = "..\\..\\mcs\\" + dir + "\\" + libraryWithoutProfile + ".csproj";
270 LibraryOutput = xproject.Element ("library_output").Value;
272 projectGuid = LookupOrGenerateGuid ();
273 fx_version = xproject.Element ("fx_version").Value;
274 Csproj = new VsCsproj () {
275 csProjFilename = this.CsprojFilename,
276 projectGuid = this.projectGuid,
277 library_output = this.LibraryOutput,
278 fx_version = double.Parse (fx_version),
279 library = this.library,
280 MsbuildGenerator = this
283 if (dir == "mcs") {
284 mcs_topdir = "../";
285 class_dir = "../class/";
286 base_dir = "../../mcs/mcs";
287 } else {
288 mcs_topdir = "../";
290 foreach (char c in dir) {
291 if (c == '/')
292 mcs_topdir = "..//" + mcs_topdir;
294 class_dir = mcs_topdir.Substring (3);
296 base_dir = Path.Combine ("..", "..", "mcs", dir);
298 AbsoluteLibraryOutput = Path.GetFullPath (Path.Combine (base_dir, LibraryOutput));
301 string LookupOrGenerateGuid ()
303 var projectFile = NativeName (CsprojFilename);
304 string guidKey = Path.GetFullPath (projectFile);
306 foreach (var fg in fixed_guids) {
307 if (guidKey.EndsWith (fg.Item1)) {
308 Console.WriteLine($"Using fixed guid {fg.Item2} for {fg.Item1}");
309 return fg.Item2;
313 string result;
314 GuidForCsprojCache.TryGetValue (projectFile, out result);
316 if (String.IsNullOrEmpty(result) && File.Exists (projectFile)){
317 try {
318 var doc = XDocument.Load (projectFile);
319 result = doc.XPathSelectElement ("x:Project/x:PropertyGroup/x:ProjectGuid", xmlns).Value;
320 } catch (Exception exc) {
321 Console.Error.WriteLine($"// Failed to parse guid from {projectFile}: {exc.Message}");
325 if (String.IsNullOrEmpty(result))
326 result = "{" + Guid.NewGuid ().ToString ().ToUpper () + "}";
328 GuidForCsprojCache[projectFile] = result;
329 return result;
332 // Currently used
333 bool Unsafe = false;
334 StringBuilder defines = new StringBuilder ();
335 bool Optimize = true;
336 bool want_debugging_support = false;
337 string main = null;
338 SortedDictionary<string, string> embedded_resources = new SortedDictionary<string, string> ();
339 List<string> warning_as_error = new List<string> ();
340 List<int> ignore_warning = new List<int> ();
341 bool load_default_config = true;
342 bool StdLib = true;
343 List<string> references = new List<string> ();
344 List<string> libs = new List<string> ();
345 List<string> reference_aliases = new List<string> ();
346 bool showWarnings = true;
348 // Currently unused
349 #pragma warning disable 0219, 0414
350 int WarningLevel = 4;
352 bool Checked = false;
353 bool WarningsAreErrors;
354 bool VerifyClsCompliance = true;
355 string win32IconFile;
356 string StrongNameKeyFile;
357 bool copyLocal = true;
358 Target Target = Target.Library;
359 string TargetExt = ".exe";
360 string OutputFile;
361 string StrongNameKeyContainer;
362 bool StrongNameDelaySign = false;
363 string LangVersion = "default";
364 string CodePage;
366 // Class directory, relative to
367 string class_dir;
368 #pragma warning restore 0219,414
370 readonly char [] argument_value_separator = new char [] { ';', ',' };
373 // This parses the -arg and /arg options to the compiler, even if the strings
374 // in the following text use "/arg" on the strings.
376 bool CSCParseOption (string option, ref string [] args)
378 int idx = option.IndexOf (':');
379 string arg, value;
381 if (idx == -1) {
382 arg = option;
383 value = "";
384 } else {
385 arg = option.Substring (0, idx);
387 value = option.Substring (idx + 1);
390 switch (arg.ToLower (CultureInfo.InvariantCulture)) {
391 case "/nologo":
392 return true;
394 case "/t":
395 case "/target":
396 switch (value) {
397 case "exe":
398 Target = Target.Exe;
399 break;
401 case "winexe":
402 Target = Target.WinExe;
403 break;
405 case "library":
406 Target = Target.Library;
407 TargetExt = ".dll";
408 break;
410 case "module":
411 Target = Target.Module;
412 TargetExt = ".netmodule";
413 break;
415 default:
416 return false;
418 return true;
420 case "/out":
421 if (value.Length == 0) {
422 Usage ();
423 Environment.Exit (1);
425 OutputFile = value;
426 return true;
428 case "/o":
429 case "/o+":
430 case "/optimize":
431 case "/optimize+":
432 Optimize = true;
433 return true;
435 case "/o-":
436 case "/optimize-":
437 Optimize = false;
438 return true;
440 case "/incremental":
441 case "/incremental+":
442 case "/incremental-":
443 // nothing.
444 return true;
446 case "/d":
447 case "/define": {
448 if (value.Length == 0) {
449 Usage ();
450 Environment.Exit (1);
453 foreach (string d in value.Split (argument_value_separator)) {
454 if (defines.Length != 0)
455 defines.Append (";");
456 defines.Append (d);
459 return true;
462 case "/bugreport":
464 // We should collect data, runtime, etc and store in the file specified
466 return true;
467 case "/linkres":
468 case "/linkresource":
469 case "/res":
470 case "/resource":
471 bool embeded = arg [1] == 'r' || arg [1] == 'R';
472 string [] s = value.Split (argument_value_separator);
473 switch (s.Length) {
474 case 1:
475 if (s [0].Length == 0)
476 goto default;
477 embedded_resources [s [0]] = Path.GetFileName (s [0]);
478 break;
479 case 2:
480 embedded_resources [s [0]] = s [1];
481 break;
482 case 3:
483 Console.Error.WriteLine ("// Does not support this method yet: {0}", arg);
484 Environment.Exit (1);
485 break;
486 default:
487 Console.Error.WriteLine ("// Wrong number of arguments for option `{0}'", option);
488 Environment.Exit (1);
489 break;
492 return true;
494 case "/recurse":
495 Console.Error.WriteLine ("// /recurse not supported");
496 Environment.Exit (1);
497 return true;
499 case "/r":
500 case "/reference": {
501 if (value.Length == 0) {
502 Console.Error.WriteLine ("// /reference requires an argument");
503 Environment.Exit (1);
506 string [] refs = value.Split (argument_value_separator);
507 foreach (string r in refs) {
508 string val = r;
509 int index = val.IndexOf ('=');
510 if (index > -1) {
511 reference_aliases.Add (r);
512 continue;
515 if (val.Length != 0)
516 references.Add (val);
518 return true;
520 case "/main":
521 main = value;
522 return true;
524 case "/m":
525 case "/addmodule":
526 case "/win32res":
527 case "/doc":
528 if (showWarnings)
529 Console.Error.WriteLine ("// {0} = not supported", arg);
530 return true;
532 case "/lib": {
533 libs.Add (value);
534 return true;
536 case "/win32icon": {
537 win32IconFile = value;
538 return true;
540 case "/debug-":
541 want_debugging_support = false;
542 return true;
544 case "/debug":
545 case "/debug+":
546 want_debugging_support = true;
547 return true;
549 case "/checked":
550 case "/checked+":
551 Checked = true;
552 return true;
554 case "/checked-":
555 Checked = false;
556 return true;
558 case "/clscheck":
559 case "/clscheck+":
560 return true;
562 case "/clscheck-":
563 VerifyClsCompliance = false;
564 return true;
566 case "/unsafe":
567 case "/unsafe+":
568 Unsafe = true;
569 return true;
571 case "/unsafe-":
572 Unsafe = false;
573 return true;
575 case "/warnaserror":
576 case "/warnaserror+":
577 if (value.Length == 0) {
578 WarningsAreErrors = true;
579 } else {
580 foreach (string wid in value.Split (argument_value_separator))
581 warning_as_error.Add (wid);
583 return true;
585 case "/-runtime":
586 // Console.WriteLine ("Warning ignoring /runtime:v4");
587 return true;
589 case "/warnaserror-":
590 if (value.Length == 0) {
591 WarningsAreErrors = false;
592 } else {
593 foreach (string wid in value.Split (argument_value_separator))
594 warning_as_error.Remove (wid);
596 return true;
598 case "/warn":
599 WarningLevel = Int32.Parse (value);
600 return true;
602 case "/nowarn": {
603 string [] warns;
605 if (value.Length == 0) {
606 Console.Error.WriteLine ("// /nowarn requires an argument");
607 Environment.Exit (1);
610 warns = value.Split (argument_value_separator);
611 foreach (string wc in warns) {
612 try {
613 if (wc.Trim ().Length == 0)
614 continue;
616 int warn = Int32.Parse (wc);
617 if (warn < 1) {
618 throw new ArgumentOutOfRangeException ("warn");
620 ignore_warning.Add (warn);
621 } catch {
622 Console.Error.WriteLine ($"// `{wc}' is not a valid warning number");
623 Environment.Exit (1);
626 return true;
629 case "/noconfig":
630 load_default_config = false;
631 return true;
633 case "/nostdlib":
634 case "/nostdlib+":
635 StdLib = false;
636 return true;
638 case "/nostdlib-":
639 StdLib = true;
640 return true;
642 case "/fullpaths":
643 return true;
645 case "/keyfile":
646 if (value == String.Empty) {
647 Console.Error.WriteLine ($"// {arg} requires an argument");
648 Environment.Exit (1);
650 StrongNameKeyFile = value;
651 return true;
652 case "/keycontainer":
653 if (value == String.Empty) {
654 Console.Error.WriteLine ($"// {arg} requires an argument");
655 Environment.Exit (1);
657 StrongNameKeyContainer = value;
658 return true;
659 case "/delaysign+":
660 case "/delaysign":
661 StrongNameDelaySign = true;
662 return true;
663 case "/delaysign-":
664 StrongNameDelaySign = false;
665 return true;
667 case "/langversion":
668 LangVersion = value;
669 return true;
671 case "/codepage":
672 CodePage = value;
673 return true;
675 case "/publicsign":
676 return true;
678 case "/deterministic":
679 return true;
681 case "/runtimemetadataversion":
682 return true;
684 case "/-getresourcestrings":
685 return true;
687 case "/features":
688 return true;
691 Console.Error.WriteLine ($"// Failing with : {arg}");
692 return false;
695 static string [] LoadArgs (string file)
697 StreamReader f;
698 var args = new List<string> ();
699 string line;
700 try {
701 f = new StreamReader (file);
702 } catch {
703 return null;
706 StringBuilder sb = new StringBuilder ();
708 while ((line = f.ReadLine ()) != null) {
709 int t = line.Length;
711 for (int i = 0; i < t; i++) {
712 char c = line [i];
714 if (c == '"' || c == '\'') {
715 char end = c;
717 for (i++; i < t; i++) {
718 c = line [i];
720 if (c == end)
721 break;
722 sb.Append (c);
724 } else if (c == ' ') {
725 if (sb.Length > 0) {
726 args.Add (sb.ToString ());
727 sb.Length = 0;
729 } else
730 sb.Append (c);
732 if (sb.Length > 0) {
733 args.Add (sb.ToString ());
734 sb.Length = 0;
738 string [] ret_value = new string [args.Count];
739 args.CopyTo (ret_value, 0);
741 return ret_value;
744 static string Load (string f)
746 var native = NativeName (f);
748 if (File.Exists (native)) {
749 using (var sr = new StreamReader (native)) {
750 return sr.ReadToEnd ();
752 } else
753 return "";
756 public static string NativeName (string path)
758 if (System.IO.Path.DirectorySeparatorChar == '/')
759 return path.Replace ("\\", "/");
760 else
761 return path.Replace ("/", "\\");
764 public class VsCsproj {
765 public string projectGuid;
766 public string output;
767 public string library_output;
768 public string csProjFilename;
769 public double fx_version;
770 public List<VsCsproj> projReferences = new List<VsCsproj> ();
771 public string library;
772 public MsbuildGenerator MsbuildGenerator;
773 public string preBuildEvent, postBuildEvent;
776 public VsCsproj Csproj;
778 void AppendResource (StringBuilder resources, string source, string logical)
780 resources.AppendFormat (" <EmbeddedResource Include=\"{0}\">" + NewLine, source);
781 resources.AppendFormat (" <LogicalName>{0}</LogicalName>" + NewLine, logical);
782 resources.AppendFormat (" </EmbeddedResource>" + NewLine);
785 internal string GetProjectFilename ()
787 return NativeName (Csproj.csProjFilename);
790 public void EraseExisting ()
792 var generatedProjFile = GetProjectFilename();
793 if (File.Exists(generatedProjFile))
794 File.Delete(generatedProjFile);
797 public VsCsproj Generate (string library_output, Dictionary<string,MsbuildGenerator> projects, out string profile, bool showWarnings = false)
799 var generatedProjFile = GetProjectFilename();
800 var updatingExistingProject = File.Exists(generatedProjFile);
802 Console.WriteLine (
803 "{0}: {1}", updatingExistingProject
804 ? "Updating"
805 : "Generating",
806 generatedProjFile
809 string boot, flags, output_name, built_sources, response, reskey;
811 boot = xproject.Element ("boot").Value;
812 flags = xproject.Element ("flags").Value;
813 output_name = xproject.Element ("output").Value;
814 if (output_name.EndsWith (".exe"))
815 Target = Target.Exe;
816 built_sources = xproject.Element ("built_sources").Value;
817 response = xproject.Element ("response").Value;
818 reskey = xproject.Element ("resources").Value;
820 profile = xproject.Element ("profile").Value;
821 if (string.IsNullOrEmpty (response)) {
822 // Address the issue where entries are missing the fx_version
823 // Should be fixed in the Makefile or elsewhere; this is a workaround
824 //<fx_version>basic</fx_version>
825 //<profile>./../build/deps/mcs.exe.sources.response</profile>
826 //<response></response>
827 response = profile;
828 profile = fx_version;
829 if (response.Contains ("build") || response.Contains ("basic") || response.Contains (profile_2_0)) {
830 fx_version = "2.0";
831 if (response.Contains (profile_2_0)) profile = "net_2_0";
832 } if (response.Contains ("build") || response.Contains ("basic") || response.Contains (profile_2_0)) {
833 fx_version = "2.0";
834 } else if (response.Contains (profile_3_5)) {
835 fx_version = "3.5";
836 profile = "net_3_5";
837 } else if (response.Contains (profile_4_0)) {
838 fx_version = "4.0";
839 profile = "net_4_0";
840 } else if (response.Contains (profile_4_x)) {
841 fx_version = "4.6.2";
842 profile = "net_4_x";
846 // Prebuild code, might be in inputs, check:
847 // inputs/LIBRARY.pre
849 string prebuild = GenerateStep (library, ".pre", "PreBuildEvent");
850 string postbuild = GenerateStep (library, ".post", "PostBuildEvent");
852 var all_args = new Queue<string []> ();
853 all_args.Enqueue (flags.Split ());
854 while (all_args.Count > 0) {
855 string [] f = all_args.Dequeue ();
857 for (int i = 0; i < f.Length; i++) {
858 if (f [i].Length > 0 && f [i][0] == '-')
859 f [i] = "/" + f [i].Substring (1);
861 if (f [i] [0] == '@') {
862 string [] extra_args;
863 string response_file = f [i].Substring (1);
865 var resp_file_full = Path.Combine (base_dir, response_file);
866 extra_args = LoadArgs (resp_file_full);
867 if (extra_args == null) {
868 Console.Error.WriteLine ($"// {library_output}: Unable to open response file: {resp_file_full}");
869 Environment.Exit (1);
872 all_args.Enqueue (extra_args);
873 continue;
876 if (CSCParseOption (f [i], ref f))
877 continue;
878 Console.Error.WriteLine ($"// {library_output}: Failure with {f [i]}");
879 Environment.Exit (1);
883 string [] source_files;
884 using (var reader = new StreamReader (NativeName (base_dir + "\\" + response))) {
885 source_files = reader.ReadToEnd ().Split ();
888 Array.Sort (source_files);
890 var groupConditional = $"Condition=\" '$(Platform)' == '{profile}' \"";
892 StringBuilder sources = new StringBuilder ();
893 sources.Append ($" <ItemGroup {groupConditional}>{NewLine}");
895 foreach (string s in source_files) {
896 if (s.Length == 0)
897 continue;
899 string src = s.Replace ("/", "\\");
900 if (src.StartsWith (@"Test\..\"))
901 src = src.Substring (8, src.Length - 8);
903 sources.AppendFormat (" <Compile Include=\"{0}\" />" + NewLine, src);
906 source_files = built_sources.Split ();
907 Array.Sort (source_files);
909 foreach (string s in source_files) {
910 if (s.Length == 0)
911 continue;
913 string src = s.Replace ("/", "\\");
914 if (src.StartsWith (@"Test\..\"))
915 src = src.Substring (8, src.Length - 8);
917 sources.AppendFormat (" <Compile Include=\"{0}\" />" + NewLine, src);
920 sources.Append (" </ItemGroup>");
922 //if (library == "corlib-build") // otherwise, does not compile on fx_version == 4.0
924 // references.Add("System.dll");
925 // references.Add("System.Xml.dll");
928 //if (library == "System.Core-build") // otherwise, slow compile. May be a transient need.
930 // this.ignore_warning.Add(1685);
931 // this.ignore_warning.Add(0436);
934 var refs = new StringBuilder ();
936 refs.Append ($" <ItemGroup {groupConditional}>{NewLine}");
938 if (response.Contains ("_test")) {
939 refs.Append ($@" <Reference Include=""nunitlite"">{NewLine}");
940 refs.Append ($@" <HintPath>..\lib\{profile}\nunitlite.dll</HintPath>{NewLine}");
941 refs.Append ($@" <Private>False</Private>{NewLine}");
942 refs.Append ($@" </Reference>{NewLine}");
947 // Generate resource referenced from the command line
949 var resources = new StringBuilder ();
950 if (embedded_resources.Count > 0) {
951 foreach (var dk in embedded_resources) {
952 var source = dk.Key;
953 if (source.EndsWith (".resources"))
954 source = source.Replace (".resources", ".resx");
956 // try to find a pre-built resource, and use that instead of trying to build it
957 if (source.EndsWith (".resx")) {
958 var probe_prebuilt = Path.Combine (base_dir, source.Replace (".resx", ".resources.prebuilt"));
959 if (File.Exists (probe_prebuilt)) {
961 source = GetRelativePath (base_dir + "/", probe_prebuilt);
964 AppendResource (resources, source, dk.Value);
968 // Generate resources that were part of the explicit <resource> node
970 if (reskey != null && reskey != ""){
971 var pairs = reskey.Split (' ', '\n', '\t');
972 foreach (var pair in pairs){
973 var p = pair.IndexOf (",");
974 if (p == -1){
975 Console.Error.WriteLine ($"// Found a resource without a filename: {pairs} for {Csproj.csProjFilename}");
976 Environment.Exit (1);
978 AppendResource (resources, pair.Substring (p+1), pair.Substring (0, p) + ".resources");
981 if (resources.Length > 0){
982 resources.Insert (0, $" <ItemGroup {groupConditional}>{NewLine}");
983 resources.Append (" </ItemGroup>" + NewLine);
986 if (references.Count > 0 || reference_aliases.Count > 0) {
987 // -r:mscorlib.dll -r:System.dll
988 //<ProjectReference Include="..\corlib\corlib-basic.csproj">
989 // <Project>{155aef28-c81f-405d-9072-9d52780e3e70}</Project>
990 // <Name>corlib-basic</Name>
991 //</ProjectReference>
992 //<ProjectReference Include="..\System\System-basic.csproj">
993 // <Project>{2094e859-db2f-481f-9630-f89d31d9ed48}</Project>
994 // <Name>System-basic</Name>
995 //</ProjectReference>
996 var refdistinct = references.Distinct ();
997 foreach (string reference in refdistinct) {
999 var match = GetMatchingCsproj (library_output, reference, projects);
1000 if (match != null) {
1001 AddProjectReference (refs, Csproj, match, reference, null);
1002 } else {
1003 if (showWarnings){
1004 Console.Error.WriteLine ($"{library}: Could not find a matching project reference for {Path.GetFileName (reference)}");
1005 Console.Error.WriteLine (" --> Adding reference with hintpath instead");
1007 var externalDrawing = (reference == Environment.GetEnvironmentVariable ("EXTERNAL_FACADE_DRAWING_REFERENCE"));
1008 refs.Append (" <Reference Include=\"" + (externalDrawing ? "$(EXTERNAL_FACADE_DRAWING_REFERENCE)" : reference) + "\">" + NewLine);
1009 refs.Append (" <SpecificVersion>False</SpecificVersion>" + NewLine);
1010 refs.Append (" <HintPath>" + (externalDrawing ? "$(EXTERNAL_FACADE_DRAWING_REFERENCE)" : reference) + "</HintPath>" + NewLine);
1011 refs.Append (" <Private>False</Private>" + NewLine);
1012 refs.Append (" </Reference>" + NewLine);
1016 foreach (string r in reference_aliases) {
1017 int index = r.IndexOf ('=');
1018 string alias = r.Substring (0, index);
1019 string assembly = r.Substring (index + 1);
1020 var match = GetMatchingCsproj (library_output, assembly, projects, explicitPath: true);
1021 if (match != null) {
1022 AddProjectReference (refs, Csproj, match, r, alias);
1023 } else {
1024 throw new NotSupportedException (string.Format ("From {0}, could not find a matching project reference for {1}", library, r));
1025 refs.Append (" <Reference Include=\"" + assembly + "\">" + NewLine);
1026 refs.Append (" <SpecificVersion>False</SpecificVersion>" + NewLine);
1027 refs.Append (" <HintPath>" + r + "</HintPath>" + NewLine);
1028 refs.Append (" <Aliases>" + alias + "</Aliases>" + NewLine);
1029 refs.Append (" </Reference>" + NewLine);
1035 refs.Append (" </ItemGroup>");
1037 // Possible inputs:
1038 // ../class/lib/build/tmp/System.Xml.dll [No longer possible, we should be removing this from order.xml]
1039 // /class/lib/basic/System.Core.dll
1040 // <library_output>mcs.exe</library_output>
1041 string build_output_dir, intermediate_output_dir;
1042 if (LibraryOutput.Contains ("/")) {
1043 build_output_dir = Path.GetDirectoryName (LibraryOutput);
1044 intermediate_output_dir = build_output_dir.Substring (0, build_output_dir.IndexOf("/class/lib") + 7) + "obj";
1046 else {
1047 build_output_dir = "bin\\Debug\\" + library;
1048 intermediate_output_dir = "obj\\Debug\\" + library;
1051 if (build_output_dir.Contains ("-linux") || build_output_dir.Contains ("-darwin") || build_output_dir.Contains ("-win32"))
1052 build_output_dir = build_output_dir
1053 .Replace ("-linux", "-$(HostPlatform)")
1054 .Replace ("-darwin", "-$(HostPlatform)")
1055 .Replace ("-win32", "-$(HostPlatform)");
1057 bool basic_or_build = (library.Contains ("-basic") || library.Contains ("-build"));
1059 // If an EXE is built with nostdlib, it won't work unless run with mono.exe. This stops our build steps
1060 // from working in visual studio (because we already replace @MONO@ with '' on Windows.)
1062 if (Target != Target.Library)
1063 StdLib = true;
1065 // We have our target framework set to 4.5 in many places because broken scripts check for files with 4.5
1066 // in the path, even though we compile code that uses 4.6 features. So we need to manually fix that here.
1068 if (fx_version == "4.5")
1069 fx_version = "4.6.2";
1071 // The VS2017 signing system fails to sign using this key for some reason, so for now,
1072 // just disable code signing for the nunit assemblies. It's not important.
1073 // I'd rather fix this by updating the makefiles but it seems to be impossible to disable
1074 // code signing in our make system...
1076 if (StrongNameKeyFile?.Contains("nunit.snk") ?? false)
1077 StrongNameKeyFile = null;
1080 // Replace the template values
1083 string strongNameSection = "";
1084 if (StrongNameKeyFile != null){
1085 strongNameSection = String.Format (
1086 " <SignAssembly>true</SignAssembly>" + NewLine +
1087 "{1}" +
1088 " <AssemblyOriginatorKeyFile>{0}</AssemblyOriginatorKeyFile>",
1089 StrongNameKeyFile, StrongNameDelaySign ? " <DelaySign>true</DelaySign>" + NewLine : "");
1092 string assemblyName = Path.GetFileNameWithoutExtension (output_name);
1093 var outputSuffix = Path.GetFileName (build_output_dir);
1095 string textToUpdate = updatingExistingProject
1096 ? File.ReadAllText(generatedProjFile)
1097 : template;
1099 var properties = new StringBuilder ();
1100 properties.Append ($" <PropertyGroup {groupConditional}>{NewLine}");
1101 properties.Append ($" <OutputPath>{build_output_dir}</OutputPath>{NewLine}");
1102 properties.Append ($" <IntermediateOutputPath>{intermediate_output_dir}/$(AssemblyName)-{outputSuffix}</IntermediateOutputPath>{NewLine}");
1103 properties.Append ($" <DefineConstants>{defines.ToString ()}</DefineConstants>{NewLine}");
1104 properties.Append ($" </PropertyGroup>{NewLine}");
1106 var prebuild_postbuild = new StringBuilder ();
1107 if (!String.IsNullOrWhiteSpace(prebuild) || !String.IsNullOrWhiteSpace(postbuild)) {
1108 prebuild_postbuild.Append ($" <PropertyGroup>{NewLine}");
1109 prebuild_postbuild.Append (prebuild);
1110 prebuild_postbuild.Append (postbuild);
1111 prebuild_postbuild.Append ($" </PropertyGroup>{NewLine}");
1114 Csproj.output = textToUpdate.
1115 Replace ("@OUTPUTTYPE@", Target == Target.Library ? "Library" : "Exe").
1116 Replace ("@SIGNATURE@", strongNameSection).
1117 Replace ("@PROJECTGUID@", Csproj.projectGuid).
1118 Replace ("@DEFINES@", defines.ToString ()).
1119 Replace ("@DISABLEDWARNINGS@", string.Join (",", (from i in ignore_warning select i.ToString ()).ToArray ())).
1120 Replace ("@LANGVERSION@", LangVersion).
1121 //Replace("@NOSTDLIB@", (basic_or_build || (!StdLib)) ? "<NoStdLib>true</NoStdLib>" : string.Empty).
1122 Replace ("@NOSTDLIB@", "<NoStdLib>" + (!StdLib).ToString () + "</NoStdLib>").
1123 Replace ("@NOCONFIG@", "<NoConfig>" + (!load_default_config).ToString () + "</NoConfig>").
1124 Replace ("@ALLOWUNSAFE@", Unsafe ? "<AllowUnsafeBlocks>true</AllowUnsafeBlocks>" : "").
1125 Replace ("@FX_VERSION@", fx_version).
1126 Replace ("@ASSEMBLYNAME@", assemblyName).
1127 Replace ("@DEBUG@", want_debugging_support ? "true" : "false").
1128 Replace ("@DEBUGTYPE@", want_debugging_support ? "full" : "pdbonly").
1129 Replace ("@PREBUILD_POSTBUILD@", prebuild_postbuild.ToString ()).
1130 Replace ("@STARTUPOBJECT@", main == null ? "" : $"<StartupObject>{main}</StartupObject>").
1131 //Replace ("@ADDITIONALLIBPATHS@", String.Format ("<AdditionalLibPaths>{0}</AdditionalLibPaths>", string.Join (",", libs.ToArray ()))).
1132 Replace ("@ADDITIONALLIBPATHS@", String.Empty).
1133 Replace ("@OPTIMIZE@", Optimize ? "true" : "false").
1134 Replace ("@METADATAVERSION@", assemblyName == "mscorlib" ? "<RuntimeMetadataVersion>Mono</RuntimeMetadataVersion>" : "");
1136 var propertiesPlaceholder = "<!-- @ALL_PROFILE_PROPERTIES@ -->";
1137 var refsPlaceholder = "<!-- @ALL_REFERENCES@ -->";
1138 var resourcesPlaceholder = "<!-- @ALL_RESOURCES@ -->";
1139 var sourcesPlaceholder = "<!-- @ALL_SOURCES@ -->";
1141 Csproj.output = Csproj.output.
1142 Replace (propertiesPlaceholder, properties.ToString () + NewLine + propertiesPlaceholder).
1143 Replace (refsPlaceholder, refs.ToString () + NewLine + refsPlaceholder).
1144 Replace (resourcesPlaceholder, resources.ToString () + NewLine + resourcesPlaceholder).
1145 Replace (sourcesPlaceholder, sources.ToString () + NewLine + sourcesPlaceholder);
1147 Csproj.preBuildEvent = prebuild;
1148 Csproj.postBuildEvent = postbuild;
1150 //Console.WriteLine ("Generated {0}", ofile.Replace ("\\", "/"));
1151 // Console.WriteLine("Writing {0}", generatedProjFile);
1152 using (var o = new StreamWriter (generatedProjFile)) {
1153 o.WriteLine (Csproj.output);
1156 return Csproj;
1159 string GenerateStep (string library, string suffix, string eventKey)
1161 string target = Load (library + suffix);
1162 string target_windows, target_unix;
1164 int q = library.IndexOf ("-");
1165 if (q != -1)
1166 target = target + Load (library.Substring (0, q) + suffix);
1168 target_unix = target.Replace ("@MONO@", "mono").Replace ("@CAT@", "cat");
1169 target_windows = target.Replace ("@MONO@", "").Replace ("@CAT@", "type");
1171 target_unix = target_unix.Replace ("\\jay\\jay.exe", "\\jay\\jay");
1173 target_unix = target_unix.Replace ("@COPY@", "cp");
1174 target_windows = target_windows.Replace ("@COPY@", "copy");
1176 target_unix = target_unix.Replace ("\r", "");
1177 const string condition_unix = "Condition=\" '$(OS)' != 'Windows_NT' \"";
1178 const string condition_windows = "Condition=\" '$(OS)' == 'Windows_NT' \"";
1180 var result = new StringBuilder ();
1181 if (!String.IsNullOrWhiteSpace (target_unix))
1182 result.Append ($" <{eventKey} {condition_unix}>{target_unix.Trim ()}</{eventKey}>{NewLine}");
1183 if (!String.IsNullOrWhiteSpace (target_windows))
1184 result.Append ($" <{eventKey} {condition_windows}>{target_windows.Trim ()}</{eventKey}>{NewLine}");
1185 return result.ToString ();
1188 void AddProjectReference (StringBuilder refs, VsCsproj result, MsbuildGenerator match, string r, string alias)
1190 refs.AppendFormat (" <ProjectReference Include=\"{0}\"", GetRelativePath (result.csProjFilename, match.CsprojFilename));
1191 if (alias != null) {
1192 refs.Append (">" + NewLine);
1193 refs.Append (" <Aliases>" + alias + "</Aliases>" + NewLine);
1194 refs.Append (" </ProjectReference>" + NewLine);
1196 else {
1197 refs.Append (" />" + NewLine);
1199 if (!result.projReferences.Contains (match.Csproj))
1200 result.projReferences.Add (match.Csproj);
1203 public static string GetRelativePath (string from, string to)
1205 from = from.Replace ("\\", "/");
1206 to = to.Replace ("\\", "/");
1207 var fromUri = new Uri (Path.GetFullPath (from));
1208 var toUri = new Uri (Path.GetFullPath (to));
1210 var ret = fromUri.MakeRelativeUri (toUri).ToString ().Replace ("%5C", "\x5c");
1211 return ret;
1214 MsbuildGenerator GetMatchingCsproj (string library_output, string dllReferenceName, Dictionary<string,MsbuildGenerator> projects, bool explicitPath = false)
1216 // libDir would be "./../../class/lib/net_4_x for example
1217 // project
1218 if (!dllReferenceName.EndsWith (".dll") && !dllReferenceName.EndsWith (".exe"))
1219 dllReferenceName += ".dll";
1221 var probe = Path.GetFullPath (Path.Combine (base_dir, dllReferenceName));
1222 foreach (var project in projects){
1223 if (probe == project.Value.AbsoluteLibraryOutput)
1224 return project.Value;
1227 // not explicit, search for the library in the lib path order specified
1229 foreach (var libDir in libs) {
1230 var abs = Path.GetFullPath (Path.Combine (base_dir, libDir));
1231 foreach (var project in projects){
1232 probe = Path.Combine (abs, dllReferenceName);
1234 if (probe == project.Value.AbsoluteLibraryOutput)
1235 return project.Value;
1239 // Last attempt, try to find the library in all the projects
1240 foreach (var project in projects) {
1241 if (project.Value.AbsoluteLibraryOutput.EndsWith (dllReferenceName))
1242 return project.Value;
1245 var ljoined = String.Join (", ", libs);
1246 Console.Error.WriteLine ($"{library_output}: did not find referenced {dllReferenceName} with libs={ljoined}");
1248 // FIXME: This is incredibly noisy and generates a billion lines of output
1249 if (false)
1250 foreach (var p in projects) {
1251 Console.Error.WriteLine ("{0}", p.Value.AbsoluteLibraryOutput);
1254 return null;
1259 public class Driver {
1261 static IEnumerable<XElement> GetProjects (bool withTests = false)
1263 XDocument doc = XDocument.Load ("order.xml");
1264 foreach (XElement project in doc.Root.Elements ()) {
1265 string dir = project.Attribute ("dir").Value;
1266 string library = project.Attribute ("library").Value;
1267 var profile = project.Element ("profile").Value;
1270 // Do not do 2.1, it is not working yet
1271 // Do not do basic, as there is no point (requires a system mcs to be installed).
1273 if (library.Contains ("moonlight") || library.Contains ("-basic") || library.EndsWith ("bootstrap") || library.Contains ("build"))
1274 continue;
1276 // The next ones are to make debugging easier for now
1277 if (profile == "basic")
1278 continue;
1280 // For now -- problem is, our resolver currently only considers the assembly name, and we ahve
1281 // conflicing 2.0 and 2.4 versions so for now, we just skip the nunit20 versions
1282 if (dir.Contains ("nunit20"))
1283 continue;
1285 if (library.Contains ("tests") && !withTests)
1286 continue;
1288 yield return project;
1292 static void Main (string [] args)
1294 if (!File.Exists ("genproj.cs")) {
1295 Console.Error.WriteLine ("This command must be executed from mono/msvc/scripts");
1296 Environment.Exit (1);
1299 if (args.Length == 1 && args [0].ToLower ().Contains ("-h")) {
1300 Console.Error.WriteLine ("Usage:");
1301 Console.Error.WriteLine ("genproj.exe [visual_studio_release] [output_full_solutions] [with_tests]");
1302 Console.Error.WriteLine ("If output_full_solutions is false, only the main System*.dll");
1303 Console.Error.WriteLine (" assemblies (and dependencies) is included in the solution.");
1304 Console.Error.WriteLine ("Example:");
1305 Console.Error.WriteLine ("genproj.exe 2012 false false");
1306 Console.Error.WriteLine ("genproj.exe with no arguments is equivalent to 'genproj.exe 2012 true false'\n\n");
1307 Console.Error.WriteLine ("genproj.exe deps");
1308 Console.Error.WriteLine ("Generates a Makefile dependency file from the projects input");
1309 Environment.Exit (0);
1312 var slnVersion = (args.Length > 0) ? args [0] : "2012";
1313 bool fullSolutions = (args.Length > 1) ? bool.Parse (args [1]) : true;
1314 bool withTests = (args.Length > 2) ? bool.Parse (args [2]) : false;
1316 // To generate makefile depenedencies
1317 var makefileDeps = (args.Length > 0 && args [0] == "deps");
1319 var sln_gen = new SlnGenerator (slnVersion);
1320 var four_five_sln_gen = new SlnGenerator (slnVersion);
1321 var projects = new Dictionary<string,MsbuildGenerator> ();
1323 var duplicates = new List<string> ();
1324 Console.Error.WriteLine("// Deleting existing project files");
1325 foreach (var project in GetProjects (withTests)) {
1326 var library_output = project.Element ("library_output").Value;
1328 var gen = new MsbuildGenerator (project);
1329 projects [library_output] = gen;
1330 gen.EraseExisting ();
1332 Console.Error.WriteLine("// Generating project files");
1333 foreach (var project in GetProjects (withTests)){
1334 var library_output = project.Element ("library_output").Value;
1335 // Console.WriteLine ("=== {0} ===", library_output);
1336 var gen = projects [library_output];
1337 try {
1338 string profileName;
1339 var csproj = gen.Generate (library_output, projects, out profileName);
1340 var csprojFilename = csproj.csProjFilename;
1341 if (!sln_gen.ContainsProjectIdentifier (csproj.library)) {
1342 sln_gen.Add (csproj);
1343 } else {
1344 duplicates.Add (csprojFilename);
1347 if (profileName == null) {
1348 Console.Error.WriteLine ($"{library_output} has no profile");
1349 } else {
1350 HashSet<string> profileNames;
1351 if (!SlnGenerator.profilesByGuid.TryGetValue (csproj.projectGuid, out profileNames))
1352 SlnGenerator.profilesByGuid[csproj.projectGuid] = profileNames = new HashSet<string>();
1354 profileNames.Add (profileName);
1355 SlnGenerator.observedProfiles.Add (profileName);
1357 } catch (Exception e) {
1358 Console.Error.WriteLine ("// Error in {0}\n{1}", project, e);
1362 foreach (var csprojFile in projects.Values.Select (x => x.GetProjectFilename ()).Distinct ())
1364 Console.WriteLine ("Deduplicating: " + csprojFile);
1365 DeduplicateSourcesAndProjectReferences (csprojFile);
1368 Func<MsbuildGenerator.VsCsproj, bool> additionalFilter;
1369 additionalFilter = fullSolutions ? (Func<MsbuildGenerator.VsCsproj, bool>)null : IsCommonLibrary;
1371 FillSolution (four_five_sln_gen, MsbuildGenerator.profile_4_x, projects.Values, additionalFilter);
1373 if (duplicates.Count () > 0) {
1374 var sb = new StringBuilder ();
1375 sb.AppendLine ("// WARNING: Skipped some project references, apparent duplicates in order.xml:");
1376 foreach (var item in duplicates) {
1377 sb.AppendLine ($"// {item}");
1379 Console.Error.WriteLine (sb.ToString ());
1382 WriteSolution (four_five_sln_gen, Path.Combine ("..", "..", "bcl.sln"));
1384 if (makefileDeps){
1385 const string classDirPrefix = "./../../";
1386 Console.WriteLine ("here {0}", sln_gen.libraries.Count);
1387 foreach (var p in sln_gen.libraries){
1388 string rebasedOutput = RebaseToClassDirectory (MsbuildGenerator.GetRelativePath ("../../mcs/class", p.library_output));
1390 Console.Write ("{0}: ", rebasedOutput);
1391 foreach (var r in p.projReferences){
1392 var lo = r.library_output;
1393 if (lo.StartsWith (classDirPrefix))
1394 lo = lo.Substring (classDirPrefix.Length);
1395 else
1396 lo = "<<ERROR-dependency is not a class library>>";
1397 Console.Write ("{0} ", lo);
1399 Console.Write ("\n\t(cd {0}; make {1})", p.MsbuildGenerator.dir, p.library_output);
1400 Console.WriteLine ("\n");
1404 // A few other optional solutions
1405 // Solutions with 'everything' and the most common libraries used in development may be of interest
1406 //WriteSolution (sln_gen, "./mcs_full.sln");
1407 //WriteSolution (small_full_sln_gen, "small_full.sln");
1408 // The following may be useful if lacking visual studio or MonoDevelop, to bootstrap mono compiler self-hosting
1409 //WriteSolution (basic_sln_gen, "mcs_basic.sln");
1410 //WriteSolution (build_sln_gen, "mcs_build.sln");
1413 static void DeduplicateSourcesAndProjectReferences (string csprojFilename)
1415 XmlDocument doc = new XmlDocument ();
1416 doc.Load (csprojFilename);
1417 XmlNamespaceManager mgr = new XmlNamespaceManager (doc.NameTable);
1418 mgr.AddNamespace ("x", "http://schemas.microsoft.com/developer/msbuild/2003");
1420 XmlNode root = doc.DocumentElement;
1421 var allSources = new Dictionary<string, List<string>> ();
1422 var allProjectReferences = new Dictionary<string, List<string>> ();
1424 ProcessCompileOrProjectReferenceItems (mgr, root,
1425 // grab all sources across all platforms
1426 (source, platform) =>
1428 if (!allSources.ContainsKey (platform))
1429 allSources[platform] = new List<string> ();
1430 allSources[platform].Add (source.Attributes["Include"].Value);
1432 // grab all project references across all platforms
1433 (projRef, platform) =>
1435 if (!allProjectReferences.ContainsKey (platform))
1436 allProjectReferences[platform] = new List<string> ();
1437 allProjectReferences[platform].Add (projRef.Attributes["Include"].Value);
1440 if (allSources.Count > 1)
1442 // find the sources which are common across all platforms
1443 var commonSources = allSources.Values.First ();
1444 foreach (var l in allSources.Values.Skip (1))
1445 commonSources = commonSources.Intersect (l).ToList ();
1447 if (commonSources.Count > 0)
1449 // remove common sources from the individual platforms
1450 ProcessCompileOrProjectReferenceItems (mgr, root, (source, platform) =>
1452 var parent = source.ParentNode;
1453 if (commonSources.Contains (source.Attributes["Include"].Value))
1454 parent.RemoveChild (source);
1456 if (!parent.HasChildNodes)
1457 parent.ParentNode.RemoveChild (parent);
1458 }, null);
1460 // add common sources as ItemGroup
1461 XmlNode commonSourcesComment = root.SelectSingleNode ("//comment()[. = ' @COMMON_SOURCES@ ']");
1462 XmlElement commonSourcesElement = doc.CreateElement ("ItemGroup", root.NamespaceURI);
1464 foreach (var s in commonSources)
1466 var c = doc.CreateElement ("Compile", root.NamespaceURI);
1467 var v = doc.CreateAttribute ("Include");
1468 v.Value = s;
1469 c.Attributes.Append (v);
1471 commonSourcesElement.AppendChild (c);
1473 root.ReplaceChild (commonSourcesElement, commonSourcesComment);
1477 if (allProjectReferences.Count > 1)
1479 // find the project references which are common across all platforms
1480 var commonProjectReferences = allProjectReferences.Values.First ();
1481 foreach (var l in allProjectReferences.Values.Skip (1))
1482 commonProjectReferences = commonProjectReferences.Intersect (l).ToList ();
1484 if (commonProjectReferences.Count > 0)
1486 // remove common project references from the individual platforms
1487 ProcessCompileOrProjectReferenceItems (mgr, root, null, (projRef, platform) =>
1489 var parent = projRef.ParentNode;
1490 if (commonProjectReferences.Contains (projRef.Attributes["Include"].Value))
1491 parent.RemoveChild (projRef);
1493 if (!parent.HasChildNodes)
1494 parent.ParentNode.RemoveChild (parent);
1497 // add common project references as ItemGroup
1498 XmlNode commonProjRefsComment = root.SelectSingleNode ("//comment()[. = ' @COMMON_PROJECT_REFERENCES@ ']");
1499 XmlElement commonProjRefsElement = doc.CreateElement ("ItemGroup", root.NamespaceURI);
1501 foreach (var s in commonProjectReferences)
1503 var c = doc.CreateElement ("ProjectReference", root.NamespaceURI);
1504 var v = doc.CreateAttribute ("Include");
1505 v.Value = s;
1506 c.Attributes.Append (v);
1508 commonProjRefsElement.AppendChild (c);
1510 root.ReplaceChild (commonProjRefsElement, commonProjRefsComment);
1514 using (var w = XmlWriter.Create (csprojFilename, new XmlWriterSettings { NewLineChars = SlnGenerator.NewLine, Indent = true }))
1515 doc.Save (w);
1518 static void ProcessCompileOrProjectReferenceItems (XmlNamespaceManager mgr, XmlNode x, Action<XmlNode, string> compileAction, Action<XmlNode, string> projRefAction)
1520 foreach (XmlNode n in x.SelectNodes("//x:ItemGroup[@Condition]", mgr))
1522 if (n.Attributes.Count == 0)
1523 continue;
1525 var platform = n.Attributes["Condition"].Value;
1527 if (!platform.Contains("$(Platform)"))
1528 continue;
1530 var compileItems = n.SelectNodes("./x:Compile[@Include]", mgr);
1532 if (compileAction != null && compileItems.Count != 0) {
1533 foreach (XmlNode source in compileItems)
1534 compileAction(source, platform);
1537 var projRefItems = n.SelectNodes("./x:ProjectReference[@Include]", mgr);
1539 if (projRefAction != null && projRefItems.Count != 0) {
1540 foreach (XmlNode proj in projRefItems) {
1541 // we don't bother to process ProjectReferences with Aliases
1542 if (!proj.HasChildNodes)
1543 projRefAction(proj, platform);
1549 // Rebases a path, assuming that execution is taking place in the "class" subdirectory,
1550 // so it strips ../class/ from a path, which is a no-op
1551 static string RebaseToClassDirectory (string path)
1553 const string prefix = "../class/";
1554 int p = path.IndexOf (prefix);
1555 if (p == -1)
1556 return path;
1557 return path.Substring (0, p) + path.Substring (p+prefix.Length);
1558 return path;
1561 static void FillSolution (SlnGenerator solution, string profileString, IEnumerable<MsbuildGenerator> projects, Func<MsbuildGenerator.VsCsproj, bool> additionalFilter = null)
1563 foreach (var generator in projects) {
1564 var vsCsproj = generator.Csproj;
1565 if (!vsCsproj.library.Contains (profileString))
1566 continue;
1567 if (additionalFilter != null && !additionalFilter (vsCsproj))
1568 continue;
1569 var csprojFilename = vsCsproj.csProjFilename;
1570 if (!solution.ContainsProjectIdentifier (vsCsproj.library)) {
1571 solution.Add (vsCsproj);
1572 RecursiveAddProj (solution, vsCsproj);
1577 static void RecursiveAddProj (SlnGenerator solution, MsbuildGenerator.VsCsproj vsCsproj, int recursiveDepth = 1)
1579 const int max_recursive = 16;
1580 if (recursiveDepth > max_recursive) throw new Exception (string.Format ("Reached {0} levels of project dependency", max_recursive));
1581 foreach (var projRef in vsCsproj.projReferences) {
1582 if (!solution.ContainsProjectIdentifier (projRef.library)) {
1583 solution.Add (projRef);
1584 RecursiveAddProj (solution, projRef, recursiveDepth + 1);
1589 static void WriteSolution (SlnGenerator sln_gen, string slnfilename)
1591 Console.WriteLine (String.Format ("// Writing solution {1}, with {0} projects", sln_gen.Count, slnfilename));
1592 sln_gen.Write (slnfilename);
1595 static bool IsCommonLibrary (MsbuildGenerator.VsCsproj proj)
1597 var library = proj.library;
1598 //if (library.Contains ("-basic"))
1599 // return true;
1600 //if (library.Contains ("-build"))
1601 // return true;
1602 //if (library.StartsWith ("corlib"))
1603 // return true;
1604 if (library.StartsWith ("System-"))
1605 return true;
1606 if (library.StartsWith ("System.Xml"))
1607 return true;
1608 if (library.StartsWith ("System.Secu"))
1609 return true;
1610 if (library.StartsWith ("System.Configuration"))
1611 return true;
1612 if (library.StartsWith ("System.Core"))
1613 return true;
1614 //if (library.StartsWith ("Mono."))
1615 // return true;
1617 return false;