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