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