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