Merge pull request #946 from akoeplinger/fix-mono-parallel
[mono.git] / msvc / scripts / genproj.cs
1 using System;
2 using System.IO;
3 using System.Collections.Generic;
4 using System.Text;
5 using System.Globalization;
6 using System.Xml.Linq;
7 using System.Xml.XPath;
8 using System.Linq;
9 using System.Xml;
10
11 public enum Target {
12         Library, Exe, Module, WinExe
13 }
14
15 public enum LanguageVersion {
16         ISO_1 = 1,
17         Default_MCS = 2,
18         ISO_2 = 3,
19         LINQ = 4,
20         Future = 5,
21         Default = LINQ
22 }
23
24 class SlnGenerator {
25         public static readonly string NewLine = "\r\n"; //Environment.NewLine; // "\n"; 
26         public SlnGenerator (string formatVersion = "2012")
27         {
28                 switch (formatVersion) {
29                 case "2008":
30                         this.header = MakeHeader ("10.00", "2008");
31                         break;
32                 default:
33                         this.header = MakeHeader ("12.00", "2012");
34                         break;
35                 }
36         }
37
38         const string project_start = "Project(\"{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}\") = \"{0}\", \"{1}\", \"{2}\""; // Note: No need to double up on {} around {2}
39         const string project_end = "EndProject";
40
41         List<MsbuildGenerator.VsCsproj> libraries = new List<MsbuildGenerator.VsCsproj> ();
42         string header;
43
44         string MakeHeader (string formatVersion, string yearTag)
45         {
46                 return string.Format ("Microsoft Visual Studio Solution File, Format Version {0}" + NewLine + "# Visual Studio {1}", formatVersion, yearTag);
47         }
48
49         public void Add (MsbuildGenerator.VsCsproj vsproj)
50         {
51                 try {
52                         libraries.Add (vsproj);
53                 } catch (Exception ex) {
54                         Console.WriteLine (ex);
55                 }
56         }
57
58         public void Write (string filename)
59         {
60                 using (var sln = new StreamWriter (filename)) {
61                         sln.WriteLine ();
62                         sln.WriteLine (header);
63                         foreach (var proj in libraries) {
64                                 sln.WriteLine (project_start, proj.library, proj.csProjFilename, proj.projectGuid);
65                                 sln.WriteLine (project_end);
66                         }
67                         sln.WriteLine ("Global");
68
69                         sln.WriteLine ("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution");
70                         sln.WriteLine ("\t\tDebug|Any CPU = Debug|Any CPU");
71                         sln.WriteLine ("\t\tRelease|Any CPU = Release|Any CPU");
72                         sln.WriteLine ("\tEndGlobalSection");
73
74                         sln.WriteLine ("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution");
75                         foreach (var proj in libraries) {
76                                 var guid = proj.projectGuid;
77                                 sln.WriteLine ("\t\t{0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU", guid);
78                                 sln.WriteLine ("\t\t{0}.Debug|Any CPU.Build.0 = Debug|Any CPU", guid);
79                                 sln.WriteLine ("\t\t{0}.Release|Any CPU.ActiveCfg = Release|Any CPU", guid);
80                                 sln.WriteLine ("\t\t{0}.Release|Any CPU.Build.0 = Release|Any CPU", guid);
81                         }
82                         sln.WriteLine ("\tEndGlobalSection");
83
84                         sln.WriteLine ("\tGlobalSection(SolutionProperties) = preSolution");
85                         sln.WriteLine ("\t\tHideSolutionNode = FALSE");
86                         sln.WriteLine ("\tEndGlobalSection");
87
88                         sln.WriteLine ("EndGlobal");
89                 }
90         }
91
92         internal bool ContainsProjectIdentifier (string projId)
93         {
94                 return libraries.FindIndex (x => (x.library == projId)) >= 0;
95         }
96
97         public int Count { get { return libraries.Count; } }
98 }
99
100 class MsbuildGenerator {
101         static readonly string NewLine = SlnGenerator.NewLine;
102         static XmlNamespaceManager xmlns;
103
104         public const string profile_2_0 = "_2_0";
105         public const string profile_3_5 = "_3_5";
106         public const string profile_4_0 = "_4_0";
107         public const string profile_4_5 = "_4_5";
108
109         static void Usage ()
110         {
111                 Console.WriteLine ("Invalid argument");
112         }
113
114         static string template;
115         static MsbuildGenerator ()
116         {
117                 using (var input = new StreamReader ("csproj.tmpl")) {
118                         template = input.ReadToEnd ();
119                 }
120
121                 xmlns = new XmlNamespaceManager (new NameTable ());
122                 xmlns.AddNamespace ("x", "http://schemas.microsoft.com/developer/msbuild/2003");
123         }
124
125         // The directory as specified in order.xml
126         string dir;
127         string library;
128         string projectGuid;
129         string fx_version;
130
131         XElement xproject;
132         public string CsprojFilename;
133
134         //
135         // Our base directory, this is relative to our exectution point mono/msvc/scripts
136         string base_dir;
137         string mcs_topdir;
138
139         public string LibraryOutput, AbsoluteLibraryOutput;
140
141         public MsbuildGenerator (XElement xproject)
142         {
143                 this.xproject = xproject;
144                 dir = xproject.Attribute ("dir").Value;
145                 library = xproject.Attribute ("library").Value;
146                 CsprojFilename = "..\\..\\mcs\\" + dir + "\\" + library + ".csproj";
147                 LibraryOutput = xproject.Element ("library_output").Value;
148
149                 projectGuid = LookupOrGenerateGuid ();
150                 fx_version = xproject.Element ("fx_version").Value;
151                 Csproj = new VsCsproj () {
152                         csProjFilename = this.CsprojFilename,
153                         projectGuid = this.projectGuid,
154                         library_output = this.LibraryOutput,
155                         fx_version = double.Parse (fx_version),
156                         library = this.library
157                 };
158
159                 if (dir == "mcs") {
160                         mcs_topdir = "../";
161                         class_dir = "../class/";
162                         base_dir = "../../mcs/mcs";
163                 } else {
164                         mcs_topdir = "../";
165
166                         foreach (char c in dir) {
167                                 if (c == '/')
168                                         mcs_topdir = "..//" + mcs_topdir;
169                         }
170                         class_dir = mcs_topdir.Substring (3);
171
172                         base_dir = Path.Combine ("..", "..", "mcs", dir);
173                 }
174                 AbsoluteLibraryOutput = Path.GetFullPath (Path.Combine (base_dir, LibraryOutput));
175         }
176
177         string LookupOrGenerateGuid ()
178         {
179                 var projectFile = NativeName (CsprojFilename);
180                 if (File.Exists (projectFile)){
181                         var doc = XDocument.Load (projectFile);
182                         return doc.XPathSelectElement ("x:Project/x:PropertyGroup/x:ProjectGuid", xmlns).Value;
183                 }
184                 return "{" + Guid.NewGuid ().ToString ().ToUpper () + "}";
185         }
186
187         // Currently used
188         bool Unsafe = false;
189         StringBuilder defines = new StringBuilder ();
190         bool Optimize = true;
191         bool want_debugging_support = false;
192         Dictionary<string, string> embedded_resources = new Dictionary<string, string> ();
193         List<string> warning_as_error = new List<string> ();
194         List<int> ignore_warning = new List<int> ();
195         bool load_default_config = true;
196         bool StdLib = true;
197         List<string> references = new List<string> ();
198         List<string> libs = new List<string> ();
199         List<string> reference_aliases = new List<string> ();
200         bool showWarnings = false;
201
202         // Currently unused
203 #pragma warning disable 0219, 0414
204         int WarningLevel = 4;
205
206         bool Checked = false;
207         bool WarningsAreErrors;
208         bool VerifyClsCompliance = true;
209         string win32IconFile;
210         string StrongNameKeyFile;
211         bool copyLocal = true;
212         Target Target = Target.Exe;
213         string TargetExt = ".exe";
214         string OutputFile;
215         string StrongNameKeyContainer;
216         bool StrongNameDelaySign = false;
217         LanguageVersion Version = LanguageVersion.Default;
218         string CodePage;
219
220         // Class directory, relative to 
221         string class_dir;
222 #pragma warning restore 0219,414
223
224         readonly char [] argument_value_separator = new char [] { ';', ',' };
225
226         //
227         // This parses the -arg and /arg options to the compiler, even if the strings
228         // in the following text use "/arg" on the strings.
229         //
230         bool CSCParseOption (string option, ref string [] args)
231         {
232                 int idx = option.IndexOf (':');
233                 string arg, value;
234
235                 if (idx == -1) {
236                         arg = option;
237                         value = "";
238                 } else {
239                         arg = option.Substring (0, idx);
240
241                         value = option.Substring (idx + 1);
242                 }
243
244                 switch (arg.ToLower (CultureInfo.InvariantCulture)) {
245                 case "/nologo":
246                         return true;
247
248                 case "/t":
249                 case "/target":
250                         switch (value) {
251                         case "exe":
252                                 Target = Target.Exe;
253                                 break;
254
255                         case "winexe":
256                                 Target = Target.WinExe;
257                                 break;
258
259                         case "library":
260                                 Target = Target.Library;
261                                 TargetExt = ".dll";
262                                 break;
263
264                         case "module":
265                                 Target = Target.Module;
266                                 TargetExt = ".netmodule";
267                                 break;
268
269                         default:
270                                 return false;
271                         }
272                         return true;
273
274                 case "/out":
275                         if (value.Length == 0) {
276                                 Usage ();
277                                 Environment.Exit (1);
278                         }
279                         OutputFile = value;
280                         return true;
281
282                 case "/o":
283                 case "/o+":
284                 case "/optimize":
285                 case "/optimize+":
286                         Optimize = true;
287                         return true;
288
289                 case "/o-":
290                 case "/optimize-":
291                         Optimize = false;
292                         return true;
293
294                 case "/incremental":
295                 case "/incremental+":
296                 case "/incremental-":
297                         // nothing.
298                         return true;
299
300                 case "/d":
301                 case "/define": {
302                                 if (value.Length == 0) {
303                                         Usage ();
304                                         Environment.Exit (1);
305                                 }
306
307                                 foreach (string d in value.Split (argument_value_separator)) {
308                                         if (defines.Length != 0)
309                                                 defines.Append (";");
310                                         defines.Append (d);
311                                 }
312
313                                 return true;
314                         }
315
316                 case "/bugreport":
317                         //
318                         // We should collect data, runtime, etc and store in the file specified
319                         //
320                         return true;
321                 case "/linkres":
322                 case "/linkresource":
323                 case "/res":
324                 case "/resource":
325                         bool embeded = arg [1] == 'r' || arg [1] == 'R';
326                         string [] s = value.Split (argument_value_separator);
327                         switch (s.Length) {
328                         case 1:
329                                 if (s [0].Length == 0)
330                                         goto default;
331                                 embedded_resources [s [0]] = Path.GetFileName (s [0]);
332                                 break;
333                         case 2:
334                                 embedded_resources [s [0]] = s [1];
335                                 break;
336                         case 3:
337                                 Console.WriteLine ("Does not support this method yet: {0}", arg);
338                                 Environment.Exit (1);
339                                 break;
340                         default:
341                                 Console.WriteLine ("Wrong number of arguments for option `{0}'", option);
342                                 Environment.Exit (1);
343                                 break;
344                         }
345
346                         return true;
347
348                 case "/recurse":
349                         Console.WriteLine ("/recurse not supported");
350                         Environment.Exit (1);
351                         return true;
352
353                 case "/r":
354                 case "/reference": {
355                                 if (value.Length == 0) {
356                                         Console.WriteLine ("-reference requires an argument");
357                                         Environment.Exit (1);
358                                 }
359
360                                 string [] refs = value.Split (argument_value_separator);
361                                 foreach (string r in refs) {
362                                         string val = r;
363                                         int index = val.IndexOf ('=');
364                                         if (index > -1) {
365                                                 reference_aliases.Add (r);
366                                                 continue;
367                                         }
368
369                                         if (val.Length != 0)
370                                                 references.Add (val);
371                                 }
372                                 return true;
373                         }
374                 case "/main":
375                 case "/m":
376                 case "/addmodule":
377                 case "/win32res":
378                 case "/doc": 
379                         if (showWarnings)
380                                 Console.WriteLine ("{0} = not supported", arg);
381                         return true;
382                         
383                 case "/lib": {
384                                 libs.Add (value);
385                                 return true;
386                         }
387                 case "/win32icon": {
388                                 win32IconFile = value;
389                                 return true;
390                         }
391                 case "/debug-":
392                         want_debugging_support = false;
393                         return true;
394
395                 case "/debug":
396                 case "/debug+":
397                         want_debugging_support = true;
398                         return true;
399
400                 case "/checked":
401                 case "/checked+":
402                         Checked = true;
403                         return true;
404
405                 case "/checked-":
406                         Checked = false;
407                         return true;
408
409                 case "/clscheck":
410                 case "/clscheck+":
411                         return true;
412
413                 case "/clscheck-":
414                         VerifyClsCompliance = false;
415                         return true;
416
417                 case "/unsafe":
418                 case "/unsafe+":
419                         Unsafe = true;
420                         return true;
421
422                 case "/unsafe-":
423                         Unsafe = false;
424                         return true;
425
426                 case "/warnaserror":
427                 case "/warnaserror+":
428                         if (value.Length == 0) {
429                                 WarningsAreErrors = true;
430                         } else {
431                                 foreach (string wid in value.Split (argument_value_separator))
432                                         warning_as_error.Add (wid);
433                         }
434                         return true;
435
436                 case "/-runtime":
437                         // Console.WriteLine ("Warning ignoring /runtime:v4");
438                         return true;
439
440                 case "/warnaserror-":
441                         if (value.Length == 0) {
442                                 WarningsAreErrors = false;
443                         } else {
444                                 foreach (string wid in value.Split (argument_value_separator))
445                                         warning_as_error.Remove (wid);
446                         }
447                         return true;
448
449                 case "/warn":
450                         WarningLevel = Int32.Parse (value);
451                         return true;
452
453                 case "/nowarn": {
454                                 string [] warns;
455
456                                 if (value.Length == 0) {
457                                         Console.WriteLine ("/nowarn requires an argument");
458                                         Environment.Exit (1);
459                                 }
460
461                                 warns = value.Split (argument_value_separator);
462                                 foreach (string wc in warns) {
463                                         try {
464                                                 if (wc.Trim ().Length == 0)
465                                                         continue;
466
467                                                 int warn = Int32.Parse (wc);
468                                                 if (warn < 1) {
469                                                         throw new ArgumentOutOfRangeException ("warn");
470                                                 }
471                                                 ignore_warning.Add (warn);
472                                         } catch {
473                                                 Console.WriteLine (String.Format ("`{0}' is not a valid warning number", wc));
474                                                 Environment.Exit (1);
475                                         }
476                                 }
477                                 return true;
478                         }
479
480                 case "/noconfig":
481                         load_default_config = false;
482                         return true;
483
484                 case "/nostdlib":
485                 case "/nostdlib+":
486                         StdLib = false;
487                         return true;
488
489                 case "/nostdlib-":
490                         StdLib = true;
491                         return true;
492
493                 case "/fullpaths":
494                         return true;
495
496                 case "/keyfile":
497                         if (value == String.Empty) {
498                                 Console.WriteLine ("{0} requires an argument", arg);
499                                 Environment.Exit (1);
500                         }
501                         StrongNameKeyFile = value;
502                         return true;
503                 case "/keycontainer":
504                         if (value == String.Empty) {
505                                 Console.WriteLine ("{0} requires an argument", arg);
506                                 Environment.Exit (1);
507                         }
508                         StrongNameKeyContainer = value;
509                         return true;
510                 case "/delaysign+":
511                 case "/delaysign":
512                         StrongNameDelaySign = true;
513                         return true;
514                 case "/delaysign-":
515                         StrongNameDelaySign = false;
516                         return true;
517
518                 case "/langversion":
519                         switch (value.ToLower (CultureInfo.InvariantCulture)) {
520                         case "iso-1":
521                                 Version = LanguageVersion.ISO_1;
522                                 return true;
523
524                         case "default":
525                                 Version = LanguageVersion.Default;
526                                 return true;
527                         case "iso-2":
528                                 Version = LanguageVersion.ISO_2;
529                                 return true;
530                         case "future":
531                                 Version = LanguageVersion.Future;
532                                 return true;
533                         }
534                         Console.WriteLine ("Invalid option `{0}' for /langversion. It must be either `ISO-1', `ISO-2' or `Default'", value);
535                         Environment.Exit (1);
536                         return true;
537
538                 case "/codepage":
539                         CodePage = value;
540                         return true;
541                 }
542
543                 Console.WriteLine ("Failing with : {0}", arg);
544                 return false;
545         }
546
547         static string [] LoadArgs (string file)
548         {
549                 StreamReader f;
550                 var args = new List<string> ();
551                 string line;
552                 try {
553                         f = new StreamReader (file);
554                 } catch {
555                         return null;
556                 }
557
558                 StringBuilder sb = new StringBuilder ();
559
560                 while ((line = f.ReadLine ()) != null) {
561                         int t = line.Length;
562
563                         for (int i = 0; i < t; i++) {
564                                 char c = line [i];
565
566                                 if (c == '"' || c == '\'') {
567                                         char end = c;
568
569                                         for (i++; i < t; i++) {
570                                                 c = line [i];
571
572                                                 if (c == end)
573                                                         break;
574                                                 sb.Append (c);
575                                         }
576                                 } else if (c == ' ') {
577                                         if (sb.Length > 0) {
578                                                 args.Add (sb.ToString ());
579                                                 sb.Length = 0;
580                                         }
581                                 } else
582                                         sb.Append (c);
583                         }
584                         if (sb.Length > 0) {
585                                 args.Add (sb.ToString ());
586                                 sb.Length = 0;
587                         }
588                 }
589
590                 string [] ret_value = new string [args.Count];
591                 args.CopyTo (ret_value, 0);
592
593                 return ret_value;
594         }
595
596         static string Load (string f)
597         {
598                 var native = NativeName (f);
599
600                 if (File.Exists (native)) {
601                         using (var sr = new StreamReader (native)) {
602                                 return sr.ReadToEnd ();
603                         }
604                 } else
605                         return "";
606         }
607
608         public static string NativeName (string path)
609         {
610                 if (System.IO.Path.DirectorySeparatorChar == '/')
611                         return path.Replace ("\\", "/");
612                 else
613                         return path.Replace ("/", "\\");
614         }
615
616         public class VsCsproj {
617                 public string projectGuid;
618                 public string output;
619                 public string library_output;
620                 public string csProjFilename;
621                 public double fx_version;
622                 public List<VsCsproj> projReferences = new List<VsCsproj> ();
623                 public string library;
624         }
625
626         public VsCsproj Csproj;
627
628         public VsCsproj Generate (Dictionary<string,MsbuildGenerator> projects, bool showWarnings = false)
629         {
630                 var generatedProjFile = NativeName (Csproj.csProjFilename);
631                 //Console.WriteLine ("Generating: {0}", generatedProjFile);
632
633                 string boot, flags, output_name, built_sources, response, profile;
634
635                 boot = xproject.Element ("boot").Value;
636                 flags = xproject.Element ("flags").Value;
637                 output_name = xproject.Element ("output").Value;
638                 built_sources = xproject.Element ("built_sources").Value;
639                 response = xproject.Element ("response").Value;
640                 //if (library.EndsWith("-build")) fx_version = "2.0"; // otherwise problem if .NET4.5 is installed, seems. (https://github.com/nikhilk/scriptsharp/issues/156)
641                 profile = xproject.Element ("profile").Value;
642                 if (string.IsNullOrEmpty (response)) {
643                         // Address the issue where entries are missing the fx_version
644                         // Should be fixed in the Makefile or elsewhere; this is a workaround
645                         //<fx_version>basic</fx_version>
646                         //<profile>./../build/deps/mcs.exe.sources.response</profile>
647                         //<response></response>
648                         response = profile;
649                         profile = fx_version;
650                         if (response.Contains ("build") || response.Contains ("basic") || response.Contains (profile_2_0)) {
651                                 fx_version = "2.0";
652                                 if (response.Contains (profile_2_0)) profile = "net_2_0";
653                         } if (response.Contains ("build") || response.Contains ("basic") || response.Contains (profile_2_0)) {
654                                 fx_version = "2.0";
655                         } else if (response.Contains (profile_3_5)) {
656                                 fx_version = "3.5";
657                                 profile = "net_3_5";
658                         } else if (response.Contains (profile_4_0)) {
659                                 fx_version = "4.0";
660                                 profile = "net_4_0";
661                         } else if (response.Contains (profile_4_5)) {
662                                 fx_version = "4.5";
663                                 profile = "net_4_5";
664                         }
665                 }
666                 //
667                 // Prebuild code, might be in inputs, check:
668                 //  inputs/LIBRARY-PROFILE.pre
669                 //  inputs/LIBRARY.pre
670                 //
671                 string prebuild = Load (library + ".pre");
672                 string prebuild_windows, prebuild_unix;
673                 
674                 int q = library.IndexOf ("-");
675                 if (q != -1)
676                         prebuild = prebuild + Load (library.Substring (0, q) + ".pre");
677
678                 if (prebuild.IndexOf ("@MONO@") != -1){
679                         prebuild_unix = prebuild.Replace ("@MONO@", "mono").Replace ("@CAT@", "cat");
680                         prebuild_windows = prebuild.Replace ("@MONO@", "").Replace ("@CAT@", "type");
681                 } else {
682                         prebuild_unix = prebuild.Replace ("jay.exe", "jay");
683                         prebuild_windows = prebuild;
684                 }
685                 
686                 const string condition_unix    = "Condition=\" '$(OS)' != 'Windows_NT' \"";
687                 const string condition_windows = "Condition=\" '$(OS)' == 'Windows_NT' \"";
688                 prebuild =
689                         "    <PreBuildEvent " + condition_unix + ">\n" + prebuild_unix + "\n    </PreBuildEvent>\n" +
690                         "    <PreBuildEvent " + condition_windows + ">\n" + prebuild_windows + "\n    </PreBuildEvent>\n";
691
692                 var all_args = new Queue<string []> ();
693                 all_args.Enqueue (flags.Split ());
694                 while (all_args.Count > 0) {
695                         string [] f = all_args.Dequeue ();
696
697                         for (int i = 0; i < f.Length; i++) {
698                                 if (f [i].Length > 0 && f [i][0] == '-')
699                                         f [i] = "/" + f [i].Substring (1);
700                                 
701                                 if (f [i] [0] == '@') {
702                                         string [] extra_args;
703                                         string response_file = f [i].Substring (1);
704
705                                         var resp_file_full = Path.Combine (base_dir, response_file);
706                                         extra_args = LoadArgs (resp_file_full);
707                                         if (extra_args == null) {
708                                                 Console.WriteLine ("Unable to open response file: " + resp_file_full);
709                                                 Environment.Exit (1);
710                                         }
711
712                                         all_args.Enqueue (extra_args);
713                                         continue;
714                                 }
715
716                                 if (CSCParseOption (f [i], ref f))
717                                         continue;
718                                 Console.WriteLine ("Failure with {0}", f [i]);
719                                 Environment.Exit (1);
720                         }
721                 }
722
723                 string [] source_files;
724                 //Console.WriteLine ("Base: {0} res: {1}", base_dir, response);
725                 using (var reader = new StreamReader (NativeName (base_dir + "\\" + response))) {
726                         source_files = reader.ReadToEnd ().Split ();
727                 }
728
729                 Array.Sort (source_files);
730
731                 StringBuilder sources = new StringBuilder ();
732                 foreach (string s in source_files) {
733                         if (s.Length == 0)
734                                 continue;
735
736                         string src = s.Replace ("/", "\\");
737                         if (src.StartsWith (@"Test\..\"))
738                                 src = src.Substring (8, src.Length - 8);
739
740                         sources.AppendFormat ("    <Compile Include=\"{0}\" />" + NewLine, src);
741                 }
742
743                 source_files = built_sources.Split ();
744                 Array.Sort (source_files);
745
746                 foreach (string s in source_files) {
747                         if (s.Length == 0)
748                                 continue;
749
750                         string src = s.Replace ("/", "\\");
751                         if (src.StartsWith (@"Test\..\"))
752                                 src = src.Substring (8, src.Length - 8);
753
754                         sources.AppendFormat ("    <Compile Include=\"{0}\" />" + NewLine, src);
755                 }
756                 sources.Remove (sources.Length - 1, 1);
757
758                 //if (library == "corlib-build") // otherwise, does not compile on fx_version == 4.0
759                 //{
760                 //    references.Add("System.dll");
761                 //    references.Add("System.Xml.dll");
762                 //}
763
764                 //if (library == "System.Core-build") // otherwise, slow compile. May be a transient need.
765                 //{
766                 //    this.ignore_warning.Add(1685);
767                 //    this.ignore_warning.Add(0436);
768                 //}
769
770                 var refs = new StringBuilder ();
771
772                 bool is_test = response.Contains ("_test_");
773                 if (is_test) {
774                         // F:\src\mono\mcs\class\lib\net_2_0\nunit.framework.dll
775                         // F:\src\mono\mcs\class\SomeProject\SomeProject_test_-net_2_0.csproj
776                         var nunitLibPath = string.Format (@"..\lib\{0}\nunit.framework.dll", profile);
777                         refs.Append (string.Format ("    <Reference Include=\"{0}\" />" + NewLine, nunitLibPath));
778                 }
779
780                 var resources = new StringBuilder ();
781                 if (embedded_resources.Count > 0) {
782                         resources.AppendFormat ("  <ItemGroup>" + NewLine);
783                         foreach (var dk in embedded_resources) {
784                                 resources.AppendFormat ("    <EmbeddedResource Include=\"{0}\">" + NewLine, dk.Key);
785                                 resources.AppendFormat ("      <LogicalName>{0}</LogicalName>" + NewLine, dk.Value);
786                                 resources.AppendFormat ("    </EmbeddedResource>" + NewLine);
787                         }
788                         resources.AppendFormat ("  </ItemGroup>" + NewLine);
789                 }
790         
791
792                 if (references.Count > 0 || reference_aliases.Count > 0) {
793                         // -r:mscorlib.dll -r:System.dll
794                         //<ProjectReference Include="..\corlib\corlib-basic.csproj">
795                         //  <Project>{155aef28-c81f-405d-9072-9d52780e3e70}</Project>
796                         //  <Name>corlib-basic</Name>
797                         //</ProjectReference>
798                         //<ProjectReference Include="..\System\System-basic.csproj">
799                         //  <Project>{2094e859-db2f-481f-9630-f89d31d9ed48}</Project>
800                         //  <Name>System-basic</Name>
801                         //</ProjectReference>
802                         var refdistinct = references.Distinct ();
803                         foreach (string r in refdistinct) {
804                                 var match = GetMatchingCsproj (Path.GetFileName (r), projects);
805                                 if (match != null) {
806                                         AddProjectReference (refs, Csproj, match, r, null);
807                                 } else {
808                                         if (showWarnings){
809                                                 Console.WriteLine ("{0}: Could not find a matching project reference for {1}", library, Path.GetFileName (r));
810                                                 Console.WriteLine ("  --> Adding reference with hintpath instead");
811                                         }
812                                         refs.Append ("    <Reference Include=\"" + r + "\">" + NewLine);
813                                         refs.Append ("      <SpecificVersion>False</SpecificVersion>" + NewLine);
814                                         refs.Append ("      <HintPath>" + r + "</HintPath>" + NewLine);
815                                         refs.Append ("      <Private>False</Private>" + NewLine);
816                                         refs.Append ("    </Reference>" + NewLine);
817                                 }
818                         }
819
820                         foreach (string r in reference_aliases) {
821                                 int index = r.IndexOf ('=');
822                                 string alias = r.Substring (0, index);
823                                 string assembly = r.Substring (index + 1);
824                                 var match = GetMatchingCsproj (assembly, projects, explicitPath: true);
825                                 if (match != null) {
826                                         AddProjectReference (refs, Csproj, match, r, alias);
827                                 } else {
828                                         throw new NotSupportedException (string.Format ("From {0}, could not find a matching project reference for {1}", library, r));
829                                         refs.Append ("    <Reference Include=\"" + assembly + "\">" + NewLine);
830                                         refs.Append ("      <SpecificVersion>False</SpecificVersion>" + NewLine);
831                                         refs.Append ("      <HintPath>" + r + "</HintPath>" + NewLine);
832                                         refs.Append ("      <Aliases>" + alias + "</Aliases>" + NewLine);
833                                         refs.Append ("    </Reference>" + NewLine);
834
835                                 }
836                         }
837                 }
838
839                 // Possible inputs:
840                 // ../class/lib/build/tmp/System.Xml.dll  [No longer possible, we should be removing this from order.xml]
841                 //   /class/lib/basic/System.Core.dll
842                 // <library_output>mcs.exe</library_output>
843                 string build_output_dir;
844                 if (LibraryOutput.Contains ("/"))
845                         build_output_dir = Path.GetDirectoryName (LibraryOutput);
846                 else
847                         build_output_dir = "bin\\Debug\\" + library;
848                 
849
850                 string postbuild_unix = string.Empty;
851                 string postbuild_windows = string.Empty;
852
853                 var postbuild =  
854                         "    <PostBuildEvent " + condition_unix + ">\n" + postbuild_unix + "\n    </PostBuildEvent>\n" +
855                         "    <PostBuildEvent " + condition_windows + ">\n" + postbuild_windows + "\n    </PostBuildEvent>";
856                         
857
858                 bool basic_or_build = (library.Contains ("-basic") || library.Contains ("-build"));
859
860                 //
861                 // Replace the template values
862                 //
863
864                 string strongNameSection = "";
865                 if (StrongNameKeyFile != null){
866                         strongNameSection = String.Format (
867                                 "  <PropertyGroup>\n" +
868                                 "    <SignAssembly>true</SignAssembly>\n" +
869                                 "{1}" +
870                                 "  </PropertyGroup>\n" + 
871                                 "  <PropertyGroup>\n" + 
872                                 "    <AssemblyOriginatorKeyFile>{0}</AssemblyOriginatorKeyFile>\n" +
873                                 "  </PropertyGroup>", StrongNameKeyFile, StrongNameDelaySign ? "    <DelaySign>true</DelaySign>\n" : "");
874                 }
875                 Csproj.output = template.
876                         Replace ("@SIGNATURE@", strongNameSection).
877                         Replace ("@PROJECTGUID@", Csproj.projectGuid).
878                         Replace ("@DEFINES@", defines.ToString ()).
879                         Replace ("@DISABLEDWARNINGS@", string.Join (",", (from i in ignore_warning select i.ToString ()).ToArray ())).
880                         //Replace("@NOSTDLIB@", (basic_or_build || (!StdLib)) ? "<NoStdLib>true</NoStdLib>" : string.Empty).
881                         Replace ("@NOSTDLIB@", "<NoStdLib>" + (!StdLib).ToString () + "</NoStdLib>").
882                         Replace ("@NOCONFIG@", "<NoConfig>" + (!load_default_config).ToString () + "</NoConfig>").
883                         Replace ("@ALLOWUNSAFE@", Unsafe ? "<AllowUnsafeBlocks>true</AllowUnsafeBlocks>" : "").
884                         Replace ("@FX_VERSION", fx_version).
885                         Replace ("@ASSEMBLYNAME@", Path.GetFileNameWithoutExtension (output_name)).
886                         Replace ("@OUTPUTDIR@", build_output_dir).
887                         Replace ("@DEFINECONSTANTS@", defines.ToString ()).
888                         Replace ("@DEBUG@", want_debugging_support ? "true" : "false").
889                         Replace ("@DEBUGTYPE@", want_debugging_support ? "full" : "pdbonly").
890                         Replace ("@REFERENCES@", refs.ToString ()).
891                         Replace ("@PREBUILD@", prebuild).
892                         Replace ("@POSTBUILD@", postbuild).
893                         //Replace ("@ADDITIONALLIBPATHS@", String.Format ("<AdditionalLibPaths>{0}</AdditionalLibPaths>", string.Join (",", libs.ToArray ()))).
894                         Replace ("@ADDITIONALLIBPATHS@", String.Empty).
895                         Replace ("@RESOURCES@", resources.ToString ()).
896                         Replace ("@OPTIMIZE@", Optimize ? "true" : "false").
897                         Replace ("@SOURCES@", sources.ToString ());
898
899                 //Console.WriteLine ("Generated {0}", ofile.Replace ("\\", "/"));
900                 using (var o = new StreamWriter (generatedProjFile)) {
901                         o.WriteLine (Csproj.output);
902                 }
903
904                 return Csproj;
905         }
906
907         void AddProjectReference (StringBuilder refs, VsCsproj result, MsbuildGenerator match, string r, string alias)
908         {
909                 refs.AppendFormat ("    <ProjectReference Include=\"{0}\">{1}", GetRelativePath (result.csProjFilename, match.CsprojFilename), NewLine);
910                 refs.Append ("      <Project>" + match.projectGuid + "</Project>" + NewLine);
911                 refs.Append ("      <Name>" + Path.GetFileNameWithoutExtension (match.CsprojFilename) + "</Name>" + NewLine);
912                 if (alias != null)
913                         refs.Append ("      <Aliases>" + alias + "</Aliases>");
914                 refs.Append ("    </ProjectReference>" + NewLine);
915                 if (!result.projReferences.Contains (match.Csproj))
916                         result.projReferences.Add (match.Csproj);
917         }
918
919         static string GetRelativePath (string from, string to)
920         {
921                 from = from.Replace ("\\", "/");
922                 to = to.Replace ("\\", "/");
923                 var fromUri = new Uri (Path.GetFullPath (from));
924                 var toUri = new Uri (Path.GetFullPath (to));
925
926                 var ret =  fromUri.MakeRelativeUri (toUri).ToString ().Replace ("%5C", "\x5c");
927                 return ret;
928         }
929
930         MsbuildGenerator GetMatchingCsproj (string dllReferenceName, Dictionary<string,MsbuildGenerator> projects, bool explicitPath = false)
931         {
932                 // libDir would be "./../../class/lib/net_4_5 for example
933                 // project 
934                 if (!dllReferenceName.EndsWith (".dll"))
935                         dllReferenceName += ".dll";
936
937                 if (explicitPath){
938                         var probe = Path.GetFullPath (Path.Combine (base_dir, dllReferenceName));
939                         foreach (var project in projects){
940                                 if (probe == project.Value.AbsoluteLibraryOutput)
941                                         return project.Value;
942                         }
943                 } 
944
945                 // not explicit, search for the library in the lib path order specified
946
947                 foreach (var libDir in libs) {
948                         var abs = Path.GetFullPath (Path.Combine (base_dir, libDir));
949                         foreach (var project in projects){
950                                 var probe = Path.Combine (abs, dllReferenceName);
951
952                                 if (probe == project.Value.AbsoluteLibraryOutput)
953                                         return project.Value;
954                         }
955                 }
956                 Console.WriteLine ("Did not find referenced {0} with libs={1}", dllReferenceName, String.Join (", ", libs));
957                 foreach (var p in projects) {
958                         Console.WriteLine ("    => {0}", p.Value.AbsoluteLibraryOutput);
959                 }
960                 return null;
961         }
962
963 }
964
965 public class Driver {
966
967         static IEnumerable<XElement> GetProjects ()
968         {
969                 XDocument doc = XDocument.Load ("order.xml");
970                 foreach (XElement project in doc.Root.Elements ()) {
971                         string dir = project.Attribute ("dir").Value;
972                         string library = project.Attribute ("library").Value;
973                         var profile = project.Element ("profile").Value;
974
975                         //
976                         // Do only class libraries for now
977                         //
978                         if (!(dir.StartsWith ("class") || dir.StartsWith ("mcs") || dir.StartsWith ("basic")))
979                                 continue;
980
981                         //
982                         // Do not do 2.1, it is not working yet
983                         // Do not do basic, as there is no point (requires a system mcs to be installed).
984                         //
985                         if (library.Contains ("moonlight") || library.Contains ("-basic") || library.EndsWith ("bootstrap")  || library.Contains ("build"))
986                                 continue;
987
988                         // The next ones are to make debugging easier for now
989                         if (profile == "basic")
990                                 continue;
991                         if (profile != "net_4_5" || library.Contains ("tests"))
992                                 continue;
993
994                         yield return project;
995                 }
996         }
997
998         static void Main (string [] args)
999         {
1000                 if (!File.Exists ("genproj.cs")) {
1001                         Console.WriteLine ("This command must be executed from mono/msvc/scripts");
1002                         Environment.Exit (1);
1003                 }
1004
1005                 if (args.Length == 1 && args [0].ToLower ().Contains ("-h")) {
1006                         Console.WriteLine ("Usage:");
1007                         Console.WriteLine ("genproj.exe [visual_studio_release] [output_full_solutions]");
1008                         Console.WriteLine ("If output_full_solutions is false, only the main System*.dll");
1009                         Console.WriteLine (" assemblies (and dependencies) is included in the solution.");
1010                         Console.WriteLine ("Example:");
1011                         Console.WriteLine ("genproj.exe 2012 false");
1012                         Console.WriteLine ("genproj.exe with no arguments is equivalent to 'genproj.exe 2012 true'");
1013                         Environment.Exit (0);
1014                 }
1015                 var slnVersion = (args.Length > 0) ? args [0] : "2012";
1016                 bool fullSolutions = (args.Length > 1) ? bool.Parse (args [1]) : true;
1017
1018                 var sln_gen = new SlnGenerator (slnVersion);
1019                 var two_sln_gen = new SlnGenerator (slnVersion);
1020                 var four_sln_gen = new SlnGenerator (slnVersion);
1021                 var three_five_sln_gen = new SlnGenerator (slnVersion);
1022                 var four_five_sln_gen = new SlnGenerator (slnVersion);
1023                 var projects = new Dictionary<string,MsbuildGenerator> ();
1024
1025                 var duplicates = new List<string> ();
1026                 foreach (var project in GetProjects ()) {
1027                         var library_output = project.Element ("library_output").Value;
1028                         projects [library_output] = new MsbuildGenerator (project);
1029                 }
1030                 foreach (var project in GetProjects ()){
1031                         var library_output = project.Element ("library_output").Value;
1032                         var gen = projects [library_output];
1033                         try {
1034                                 var csproj = gen.Generate (projects);
1035                                 var csprojFilename = csproj.csProjFilename;
1036                                 if (!sln_gen.ContainsProjectIdentifier (csproj.library)) {
1037                                         sln_gen.Add (csproj);
1038                                 } else {
1039                                         duplicates.Add (csprojFilename);
1040                                 }
1041
1042                         } catch (Exception e) {
1043                                 Console.WriteLine ("Error in {0}\n{1}", project, e);
1044                         }
1045                 }
1046
1047                 Func<MsbuildGenerator.VsCsproj, bool> additionalFilter;
1048                 additionalFilter = fullSolutions ? (Func<MsbuildGenerator.VsCsproj, bool>)null : IsCommonLibrary;
1049
1050                 FillSolution (two_sln_gen, MsbuildGenerator.profile_2_0, projects.Values, additionalFilter);
1051                 FillSolution (four_five_sln_gen, MsbuildGenerator.profile_4_5, projects.Values, additionalFilter);
1052                 FillSolution (four_sln_gen, MsbuildGenerator.profile_4_0, projects.Values, additionalFilter);
1053                 FillSolution (three_five_sln_gen, MsbuildGenerator.profile_3_5, projects.Values, additionalFilter);
1054
1055                 var sb = new StringBuilder ();
1056                 sb.AppendLine ("WARNING: Skipped some project references, apparent duplicates in order.xml:");
1057                 foreach (var item in duplicates) {
1058                         sb.AppendLine (item);
1059                 }
1060                 Console.WriteLine (sb.ToString ());
1061
1062                 WriteSolution (two_sln_gen, MakeSolutionName (MsbuildGenerator.profile_2_0));
1063                 WriteSolution (three_five_sln_gen, MakeSolutionName (MsbuildGenerator.profile_3_5));
1064                 WriteSolution (four_sln_gen, MakeSolutionName (MsbuildGenerator.profile_4_0));
1065                 WriteSolution (four_five_sln_gen, MakeSolutionName (MsbuildGenerator.profile_4_5));
1066                 
1067                 // A few other optional solutions
1068                 // Solutions with 'everything' and the most common libraries used in development may be of interest
1069                 //WriteSolution (sln_gen, "mcs_full.sln");
1070                 //WriteSolution (small_full_sln_gen, "small_full.sln");
1071                 // The following may be useful if lacking visual studio or MonoDevelop, to bootstrap mono compiler self-hosting
1072                 //WriteSolution (basic_sln_gen, "mcs_basic.sln");
1073                 //WriteSolution (build_sln_gen, "mcs_build.sln");
1074         }
1075
1076         static string MakeSolutionName (string profileTag)
1077         {
1078                 return "net" + profileTag + ".sln";
1079         }
1080
1081         static void FillSolution (SlnGenerator solution, string profileString, IEnumerable<MsbuildGenerator> projects, Func<MsbuildGenerator.VsCsproj, bool> additionalFilter = null)
1082         {
1083                 foreach (var generator in projects) {
1084                         var vsCsproj = generator.Csproj;
1085                         if (!vsCsproj.library.Contains (profileString))
1086                                 continue;
1087                         if (additionalFilter != null && !additionalFilter (vsCsproj))
1088                                 continue;
1089                         var csprojFilename = vsCsproj.csProjFilename;
1090                         if (!solution.ContainsProjectIdentifier (vsCsproj.library)) {
1091                                 solution.Add (vsCsproj);
1092                                 RecursiveAddProj (solution, vsCsproj);
1093                         }
1094                 }
1095         }
1096
1097         static void RecursiveAddProj (SlnGenerator solution, MsbuildGenerator.VsCsproj vsCsproj, int recursiveDepth = 1)
1098         {
1099                 const int max_recursive = 16;
1100                 if (recursiveDepth > max_recursive) throw new Exception (string.Format ("Reached {0} levels of project dependency", max_recursive));
1101                 foreach (var projRef in vsCsproj.projReferences) {
1102                         if (!solution.ContainsProjectIdentifier (projRef.library)) {
1103                                 solution.Add (projRef);
1104                                 RecursiveAddProj (solution, projRef, recursiveDepth + 1);
1105                         }
1106                 }
1107         }
1108
1109         static void WriteSolution (SlnGenerator sln_gen, string slnfilename)
1110         {
1111                 Console.WriteLine (String.Format ("Writing solution {1}, with {0} projects", sln_gen.Count, slnfilename));
1112                 sln_gen.Write (slnfilename);
1113         }
1114
1115         static bool IsCommonLibrary (MsbuildGenerator.VsCsproj proj)
1116         {
1117                 var library = proj.library;
1118                 //if (library.Contains ("-basic"))
1119                 //      return true;
1120                 //if (library.Contains ("-build"))
1121                 //      return true;
1122                 //if (library.StartsWith ("corlib"))
1123                 //      return true;
1124                 if (library.StartsWith ("System-"))
1125                         return true;
1126                 if (library.StartsWith ("System.Xml"))
1127                         return true;
1128                 if (library.StartsWith ("System.Secu"))
1129                         return true;
1130                 if (library.StartsWith ("System.Configuration"))
1131                         return true;
1132                 if (library.StartsWith ("System.Core"))
1133                         return true;
1134                 //if (library.StartsWith ("Mono."))
1135                 //      return true;
1136
1137                 return false;
1138         }
1139 }