[jit] Fix the coop amd64 backend.
[mono-project.git] / msvc / scripts / genproj.cs
blob32d6d8574b31add493ef6ccdba2d132f41a86920
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;
11 public enum Target {
12 Library, Exe, Module, WinExe
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
24 class SlnGenerator {
25 public static readonly string NewLine = "\r\n"; //Environment.NewLine; // "\n";
26 public SlnGenerator (string formatVersion = "2012")
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;
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";
41 public List<MsbuildGenerator.VsCsproj> libraries = new List<MsbuildGenerator.VsCsproj> ();
42 string header;
44 string MakeHeader (string formatVersion, string yearTag)
46 return string.Format ("Microsoft Visual Studio Solution File, Format Version {0}" + NewLine + "# Visual Studio {1}", formatVersion, yearTag);
49 public void Add (MsbuildGenerator.VsCsproj vsproj)
51 try {
52 libraries.Add (vsproj);
53 } catch (Exception ex) {
54 Console.WriteLine (ex);
58 public void Write (string filename)
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);
67 sln.WriteLine ("Global");
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");
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);
82 sln.WriteLine ("\tEndGlobalSection");
84 sln.WriteLine ("\tGlobalSection(SolutionProperties) = preSolution");
85 sln.WriteLine ("\t\tHideSolutionNode = FALSE");
86 sln.WriteLine ("\tEndGlobalSection");
88 sln.WriteLine ("EndGlobal");
92 internal bool ContainsProjectIdentifier (string projId)
94 return libraries.FindIndex (x => (x.library == projId)) >= 0;
97 public int Count { get { return libraries.Count; } }
100 class MsbuildGenerator {
101 static readonly string NewLine = SlnGenerator.NewLine;
102 static XmlNamespaceManager xmlns;
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";
109 static void Usage ()
111 Console.WriteLine ("Invalid argument");
114 static string template;
115 static MsbuildGenerator ()
117 using (var input = new StreamReader ("csproj.tmpl")) {
118 template = input.ReadToEnd ();
121 xmlns = new XmlNamespaceManager (new NameTable ());
122 xmlns.AddNamespace ("x", "http://schemas.microsoft.com/developer/msbuild/2003");
125 // The directory as specified in order.xml
126 public string dir;
127 string library;
128 string projectGuid;
129 string fx_version;
131 XElement xproject;
132 public string CsprojFilename;
135 // Our base directory, this is relative to our exectution point mono/msvc/scripts
136 string base_dir;
137 string mcs_topdir;
139 public string LibraryOutput, AbsoluteLibraryOutput;
141 public MsbuildGenerator (XElement xproject)
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;
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
160 if (dir == "mcs") {
161 mcs_topdir = "../";
162 class_dir = "../class/";
163 base_dir = "../../mcs/mcs";
164 } else {
165 mcs_topdir = "../";
167 foreach (char c in dir) {
168 if (c == '/')
169 mcs_topdir = "..//" + mcs_topdir;
171 class_dir = mcs_topdir.Substring (3);
173 base_dir = Path.Combine ("..", "..", "mcs", dir);
175 AbsoluteLibraryOutput = Path.GetFullPath (Path.Combine (base_dir, LibraryOutput));
178 string LookupOrGenerateGuid ()
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;
185 return "{" + Guid.NewGuid ().ToString ().ToUpper () + "}";
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;
203 // Currently unused
204 #pragma warning disable 0219, 0414
205 int WarningLevel = 4;
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;
221 // Class directory, relative to
222 string class_dir;
223 #pragma warning restore 0219,414
225 readonly char [] argument_value_separator = new char [] { ';', ',' };
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.
231 bool CSCParseOption (string option, ref string [] args)
233 int idx = option.IndexOf (':');
234 string arg, value;
236 if (idx == -1) {
237 arg = option;
238 value = "";
239 } else {
240 arg = option.Substring (0, idx);
242 value = option.Substring (idx + 1);
245 switch (arg.ToLower (CultureInfo.InvariantCulture)) {
246 case "/nologo":
247 return true;
249 case "/t":
250 case "/target":
251 switch (value) {
252 case "exe":
253 Target = Target.Exe;
254 break;
256 case "winexe":
257 Target = Target.WinExe;
258 break;
260 case "library":
261 Target = Target.Library;
262 TargetExt = ".dll";
263 break;
265 case "module":
266 Target = Target.Module;
267 TargetExt = ".netmodule";
268 break;
270 default:
271 return false;
273 return true;
275 case "/out":
276 if (value.Length == 0) {
277 Usage ();
278 Environment.Exit (1);
280 OutputFile = value;
281 return true;
283 case "/o":
284 case "/o+":
285 case "/optimize":
286 case "/optimize+":
287 Optimize = true;
288 return true;
290 case "/o-":
291 case "/optimize-":
292 Optimize = false;
293 return true;
295 case "/incremental":
296 case "/incremental+":
297 case "/incremental-":
298 // nothing.
299 return true;
301 case "/d":
302 case "/define": {
303 if (value.Length == 0) {
304 Usage ();
305 Environment.Exit (1);
308 foreach (string d in value.Split (argument_value_separator)) {
309 if (defines.Length != 0)
310 defines.Append (";");
311 defines.Append (d);
314 return true;
317 case "/bugreport":
319 // We should collect data, runtime, etc and store in the file specified
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;
347 return true;
349 case "/recurse":
350 Console.WriteLine ("/recurse not supported");
351 Environment.Exit (1);
352 return true;
354 case "/r":
355 case "/reference": {
356 if (value.Length == 0) {
357 Console.WriteLine ("-reference requires an argument");
358 Environment.Exit (1);
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;
370 if (val.Length != 0)
371 references.Add (val);
373 return true;
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;
384 case "/lib": {
385 libs.Add (value);
386 return true;
388 case "/win32icon": {
389 win32IconFile = value;
390 return true;
392 case "/debug-":
393 want_debugging_support = false;
394 return true;
396 case "/debug":
397 case "/debug+":
398 want_debugging_support = true;
399 return true;
401 case "/checked":
402 case "/checked+":
403 Checked = true;
404 return true;
406 case "/checked-":
407 Checked = false;
408 return true;
410 case "/clscheck":
411 case "/clscheck+":
412 return true;
414 case "/clscheck-":
415 VerifyClsCompliance = false;
416 return true;
418 case "/unsafe":
419 case "/unsafe+":
420 Unsafe = true;
421 return true;
423 case "/unsafe-":
424 Unsafe = false;
425 return true;
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);
435 return true;
437 case "/-runtime":
438 // Console.WriteLine ("Warning ignoring /runtime:v4");
439 return true;
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);
448 return true;
450 case "/warn":
451 WarningLevel = Int32.Parse (value);
452 return true;
454 case "/nowarn": {
455 string [] warns;
457 if (value.Length == 0) {
458 Console.WriteLine ("/nowarn requires an argument");
459 Environment.Exit (1);
462 warns = value.Split (argument_value_separator);
463 foreach (string wc in warns) {
464 try {
465 if (wc.Trim ().Length == 0)
466 continue;
468 int warn = Int32.Parse (wc);
469 if (warn < 1) {
470 throw new ArgumentOutOfRangeException ("warn");
472 ignore_warning.Add (warn);
473 } catch {
474 Console.WriteLine (String.Format ("`{0}' is not a valid warning number", wc));
475 Environment.Exit (1);
478 return true;
481 case "/noconfig":
482 load_default_config = false;
483 return true;
485 case "/nostdlib":
486 case "/nostdlib+":
487 StdLib = false;
488 return true;
490 case "/nostdlib-":
491 StdLib = true;
492 return true;
494 case "/fullpaths":
495 return true;
497 case "/keyfile":
498 if (value == String.Empty) {
499 Console.WriteLine ("{0} requires an argument", arg);
500 Environment.Exit (1);
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);
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;
519 case "/langversion":
520 switch (value.ToLower (CultureInfo.InvariantCulture)) {
521 case "iso-1":
522 Version = LanguageVersion.ISO_1;
523 return true;
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;
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;
539 case "/codepage":
540 CodePage = value;
541 return true;
543 case "/-getresourcestrings":
544 return true;
547 Console.WriteLine ("Failing with : {0}", arg);
548 return false;
551 static string [] LoadArgs (string file)
553 StreamReader f;
554 var args = new List<string> ();
555 string line;
556 try {
557 f = new StreamReader (file);
558 } catch {
559 return null;
562 StringBuilder sb = new StringBuilder ();
564 while ((line = f.ReadLine ()) != null) {
565 int t = line.Length;
567 for (int i = 0; i < t; i++) {
568 char c = line [i];
570 if (c == '"' || c == '\'') {
571 char end = c;
573 for (i++; i < t; i++) {
574 c = line [i];
576 if (c == end)
577 break;
578 sb.Append (c);
580 } else if (c == ' ') {
581 if (sb.Length > 0) {
582 args.Add (sb.ToString ());
583 sb.Length = 0;
585 } else
586 sb.Append (c);
588 if (sb.Length > 0) {
589 args.Add (sb.ToString ());
590 sb.Length = 0;
594 string [] ret_value = new string [args.Count];
595 args.CopyTo (ret_value, 0);
597 return ret_value;
600 static string Load (string f)
602 var native = NativeName (f);
604 if (File.Exists (native)) {
605 using (var sr = new StreamReader (native)) {
606 return sr.ReadToEnd ();
608 } else
609 return "";
612 public static string NativeName (string path)
614 if (System.IO.Path.DirectorySeparatorChar == '/')
615 return path.Replace ("\\", "/");
616 else
617 return path.Replace ("/", "\\");
620 public class VsCsproj {
621 public string projectGuid;
622 public string output;
623 public string library_output;
624 public string csProjFilename;
625 public double fx_version;
626 public List<VsCsproj> projReferences = new List<VsCsproj> ();
627 public string library;
628 public MsbuildGenerator MsbuildGenerator;
631 public VsCsproj Csproj;
633 public VsCsproj Generate (Dictionary<string,MsbuildGenerator> projects, bool showWarnings = false)
635 var generatedProjFile = NativeName (Csproj.csProjFilename);
636 //Console.WriteLine ("Generating: {0}", generatedProjFile);
638 string boot, flags, output_name, built_sources, response, profile;
640 boot = xproject.Element ("boot").Value;
641 flags = xproject.Element ("flags").Value;
642 output_name = xproject.Element ("output").Value;
643 built_sources = xproject.Element ("built_sources").Value;
644 response = xproject.Element ("response").Value;
645 //if (library.EndsWith("-build")) fx_version = "2.0"; // otherwise problem if .NET4.5 is installed, seems. (https://github.com/nikhilk/scriptsharp/issues/156)
646 profile = xproject.Element ("profile").Value;
647 if (string.IsNullOrEmpty (response)) {
648 // Address the issue where entries are missing the fx_version
649 // Should be fixed in the Makefile or elsewhere; this is a workaround
650 //<fx_version>basic</fx_version>
651 //<profile>./../build/deps/mcs.exe.sources.response</profile>
652 //<response></response>
653 response = profile;
654 profile = fx_version;
655 if (response.Contains ("build") || response.Contains ("basic") || response.Contains (profile_2_0)) {
656 fx_version = "2.0";
657 if (response.Contains (profile_2_0)) profile = "net_2_0";
658 } if (response.Contains ("build") || response.Contains ("basic") || response.Contains (profile_2_0)) {
659 fx_version = "2.0";
660 } else if (response.Contains (profile_3_5)) {
661 fx_version = "3.5";
662 profile = "net_3_5";
663 } else if (response.Contains (profile_4_0)) {
664 fx_version = "4.0";
665 profile = "net_4_0";
666 } else if (response.Contains (profile_4_5)) {
667 fx_version = "4.5";
668 profile = "net_4_5";
672 // Prebuild code, might be in inputs, check:
673 // inputs/LIBRARY-PROFILE.pre
674 // inputs/LIBRARY.pre
676 string prebuild = Load (library + ".pre");
677 string prebuild_windows, prebuild_unix;
679 int q = library.IndexOf ("-");
680 if (q != -1)
681 prebuild = prebuild + Load (library.Substring (0, q) + ".pre");
683 if (prebuild.IndexOf ("@MONO@") != -1){
684 prebuild_unix = prebuild.Replace ("@MONO@", "mono").Replace ("@CAT@", "cat");
685 prebuild_windows = prebuild.Replace ("@MONO@", "").Replace ("@CAT@", "type");
686 } else {
687 prebuild_unix = prebuild.Replace ("jay.exe", "jay");
688 prebuild_windows = prebuild;
691 const string condition_unix = "Condition=\" '$(OS)' != 'Windows_NT' \"";
692 const string condition_windows = "Condition=\" '$(OS)' == 'Windows_NT' \"";
693 prebuild =
694 " <PreBuildEvent " + condition_unix + ">" + NewLine + prebuild_unix + NewLine + " </PreBuildEvent>" + NewLine +
695 " <PreBuildEvent " + condition_windows + ">" + NewLine + prebuild_windows + NewLine + " </PreBuildEvent>" + NewLine;
697 var all_args = new Queue<string []> ();
698 all_args.Enqueue (flags.Split ());
699 while (all_args.Count > 0) {
700 string [] f = all_args.Dequeue ();
702 for (int i = 0; i < f.Length; i++) {
703 if (f [i].Length > 0 && f [i][0] == '-')
704 f [i] = "/" + f [i].Substring (1);
706 if (f [i] [0] == '@') {
707 string [] extra_args;
708 string response_file = f [i].Substring (1);
710 var resp_file_full = Path.Combine (base_dir, response_file);
711 extra_args = LoadArgs (resp_file_full);
712 if (extra_args == null) {
713 Console.WriteLine ("Unable to open response file: " + resp_file_full);
714 Environment.Exit (1);
717 all_args.Enqueue (extra_args);
718 continue;
721 if (CSCParseOption (f [i], ref f))
722 continue;
723 Console.WriteLine ("Failure with {0}", f [i]);
724 Environment.Exit (1);
728 string [] source_files;
729 //Console.WriteLine ("Base: {0} res: {1}", base_dir, response);
730 using (var reader = new StreamReader (NativeName (base_dir + "\\" + response))) {
731 source_files = reader.ReadToEnd ().Split ();
734 Array.Sort (source_files);
736 StringBuilder sources = new StringBuilder ();
737 foreach (string s in source_files) {
738 if (s.Length == 0)
739 continue;
741 string src = s.Replace ("/", "\\");
742 if (src.StartsWith (@"Test\..\"))
743 src = src.Substring (8, src.Length - 8);
745 sources.AppendFormat (" <Compile Include=\"{0}\" />" + NewLine, src);
748 source_files = built_sources.Split ();
749 Array.Sort (source_files);
751 foreach (string s in source_files) {
752 if (s.Length == 0)
753 continue;
755 string src = s.Replace ("/", "\\");
756 if (src.StartsWith (@"Test\..\"))
757 src = src.Substring (8, src.Length - 8);
759 sources.AppendFormat (" <Compile Include=\"{0}\" />" + NewLine, src);
761 sources.Remove (sources.Length - 1, 1);
763 //if (library == "corlib-build") // otherwise, does not compile on fx_version == 4.0
765 // references.Add("System.dll");
766 // references.Add("System.Xml.dll");
769 //if (library == "System.Core-build") // otherwise, slow compile. May be a transient need.
771 // this.ignore_warning.Add(1685);
772 // this.ignore_warning.Add(0436);
775 var refs = new StringBuilder ();
777 bool is_test = response.Contains ("_test_");
778 if (is_test) {
779 // F:\src\mono\mcs\class\lib\net_2_0\nunit.framework.dll
780 // F:\src\mono\mcs\class\SomeProject\SomeProject_test_-net_2_0.csproj
781 var nunitLibPath = string.Format (@"..\lib\{0}\nunit.framework.dll", profile);
782 refs.Append (string.Format (" <Reference Include=\"{0}\" />" + NewLine, nunitLibPath));
785 var resources = new StringBuilder ();
786 if (embedded_resources.Count > 0) {
787 resources.AppendFormat (" <ItemGroup>" + NewLine);
788 foreach (var dk in embedded_resources) {
789 resources.AppendFormat (" <EmbeddedResource Include=\"{0}\">" + NewLine, dk.Key);
790 resources.AppendFormat (" <LogicalName>{0}</LogicalName>" + NewLine, dk.Value);
791 resources.AppendFormat (" </EmbeddedResource>" + NewLine);
793 resources.AppendFormat (" </ItemGroup>" + NewLine);
797 if (references.Count > 0 || reference_aliases.Count > 0) {
798 // -r:mscorlib.dll -r:System.dll
799 //<ProjectReference Include="..\corlib\corlib-basic.csproj">
800 // <Project>{155aef28-c81f-405d-9072-9d52780e3e70}</Project>
801 // <Name>corlib-basic</Name>
802 //</ProjectReference>
803 //<ProjectReference Include="..\System\System-basic.csproj">
804 // <Project>{2094e859-db2f-481f-9630-f89d31d9ed48}</Project>
805 // <Name>System-basic</Name>
806 //</ProjectReference>
807 var refdistinct = references.Distinct ();
808 foreach (string r in refdistinct) {
809 var match = GetMatchingCsproj (Path.GetFileName (r), projects);
810 if (match != null) {
811 AddProjectReference (refs, Csproj, match, r, null);
812 } else {
813 if (showWarnings){
814 Console.WriteLine ("{0}: Could not find a matching project reference for {1}", library, Path.GetFileName (r));
815 Console.WriteLine (" --> Adding reference with hintpath instead");
817 refs.Append (" <Reference Include=\"" + r + "\">" + NewLine);
818 refs.Append (" <SpecificVersion>False</SpecificVersion>" + NewLine);
819 refs.Append (" <HintPath>" + r + "</HintPath>" + NewLine);
820 refs.Append (" <Private>False</Private>" + NewLine);
821 refs.Append (" </Reference>" + NewLine);
825 foreach (string r in reference_aliases) {
826 int index = r.IndexOf ('=');
827 string alias = r.Substring (0, index);
828 string assembly = r.Substring (index + 1);
829 var match = GetMatchingCsproj (assembly, projects, explicitPath: true);
830 if (match != null) {
831 AddProjectReference (refs, Csproj, match, r, alias);
832 } else {
833 throw new NotSupportedException (string.Format ("From {0}, could not find a matching project reference for {1}", library, r));
834 refs.Append (" <Reference Include=\"" + assembly + "\">" + NewLine);
835 refs.Append (" <SpecificVersion>False</SpecificVersion>" + NewLine);
836 refs.Append (" <HintPath>" + r + "</HintPath>" + NewLine);
837 refs.Append (" <Aliases>" + alias + "</Aliases>" + NewLine);
838 refs.Append (" </Reference>" + NewLine);
844 // Possible inputs:
845 // ../class/lib/build/tmp/System.Xml.dll [No longer possible, we should be removing this from order.xml]
846 // /class/lib/basic/System.Core.dll
847 // <library_output>mcs.exe</library_output>
848 string build_output_dir;
849 if (LibraryOutput.Contains ("/"))
850 build_output_dir = Path.GetDirectoryName (LibraryOutput);
851 else
852 build_output_dir = "bin\\Debug\\" + library;
855 string postbuild_unix = string.Empty;
856 string postbuild_windows = string.Empty;
858 var postbuild =
859 " <PostBuildEvent " + condition_unix + ">" + NewLine + postbuild_unix + NewLine + " </PostBuildEvent>" + NewLine +
860 " <PostBuildEvent " + condition_windows + ">" + NewLine + postbuild_windows + NewLine + " </PostBuildEvent>";
863 bool basic_or_build = (library.Contains ("-basic") || library.Contains ("-build"));
866 // Replace the template values
869 string strongNameSection = "";
870 if (StrongNameKeyFile != null){
871 strongNameSection = String.Format (
872 " <PropertyGroup>" + NewLine +
873 " <SignAssembly>true</SignAssembly>" + NewLine +
874 "{1}" +
875 " </PropertyGroup>" + NewLine +
876 " <PropertyGroup>" + NewLine +
877 " <AssemblyOriginatorKeyFile>{0}</AssemblyOriginatorKeyFile>" + NewLine +
878 " </PropertyGroup>", StrongNameKeyFile, StrongNameDelaySign ? " <DelaySign>true</DelaySign>" + NewLine : "");
880 Csproj.output = template.
881 Replace ("@SIGNATURE@", strongNameSection).
882 Replace ("@PROJECTGUID@", Csproj.projectGuid).
883 Replace ("@DEFINES@", defines.ToString ()).
884 Replace ("@DISABLEDWARNINGS@", string.Join (",", (from i in ignore_warning select i.ToString ()).ToArray ())).
885 //Replace("@NOSTDLIB@", (basic_or_build || (!StdLib)) ? "<NoStdLib>true</NoStdLib>" : string.Empty).
886 Replace ("@NOSTDLIB@", "<NoStdLib>" + (!StdLib).ToString () + "</NoStdLib>").
887 Replace ("@NOCONFIG@", "<NoConfig>" + (!load_default_config).ToString () + "</NoConfig>").
888 Replace ("@ALLOWUNSAFE@", Unsafe ? "<AllowUnsafeBlocks>true</AllowUnsafeBlocks>" : "").
889 Replace ("@FX_VERSION", fx_version).
890 Replace ("@ASSEMBLYNAME@", Path.GetFileNameWithoutExtension (output_name)).
891 Replace ("@OUTPUTDIR@", build_output_dir).
892 Replace ("@DEFINECONSTANTS@", defines.ToString ()).
893 Replace ("@DEBUG@", want_debugging_support ? "true" : "false").
894 Replace ("@DEBUGTYPE@", want_debugging_support ? "full" : "pdbonly").
895 Replace ("@REFERENCES@", refs.ToString ()).
896 Replace ("@PREBUILD@", prebuild).
897 Replace ("@POSTBUILD@", postbuild).
898 //Replace ("@ADDITIONALLIBPATHS@", String.Format ("<AdditionalLibPaths>{0}</AdditionalLibPaths>", string.Join (",", libs.ToArray ()))).
899 Replace ("@ADDITIONALLIBPATHS@", String.Empty).
900 Replace ("@RESOURCES@", resources.ToString ()).
901 Replace ("@OPTIMIZE@", Optimize ? "true" : "false").
902 Replace ("@SOURCES@", sources.ToString ());
904 //Console.WriteLine ("Generated {0}", ofile.Replace ("\\", "/"));
905 using (var o = new StreamWriter (generatedProjFile)) {
906 o.WriteLine (Csproj.output);
909 return Csproj;
912 void AddProjectReference (StringBuilder refs, VsCsproj result, MsbuildGenerator match, string r, string alias)
914 refs.AppendFormat (" <ProjectReference Include=\"{0}\">{1}", GetRelativePath (result.csProjFilename, match.CsprojFilename), NewLine);
915 refs.Append (" <Project>" + match.projectGuid + "</Project>" + NewLine);
916 refs.Append (" <Name>" + Path.GetFileNameWithoutExtension (match.CsprojFilename.Replace ('\\', Path.DirectorySeparatorChar)) + "</Name>" + NewLine);
917 if (alias != null)
918 refs.Append (" <Aliases>" + alias + "</Aliases>");
919 refs.Append (" </ProjectReference>" + NewLine);
920 if (!result.projReferences.Contains (match.Csproj))
921 result.projReferences.Add (match.Csproj);
924 public static string GetRelativePath (string from, string to)
926 from = from.Replace ("\\", "/");
927 to = to.Replace ("\\", "/");
928 var fromUri = new Uri (Path.GetFullPath (from));
929 var toUri = new Uri (Path.GetFullPath (to));
931 var ret = fromUri.MakeRelativeUri (toUri).ToString ().Replace ("%5C", "\x5c");
932 return ret;
935 MsbuildGenerator GetMatchingCsproj (string dllReferenceName, Dictionary<string,MsbuildGenerator> projects, bool explicitPath = false)
937 // libDir would be "./../../class/lib/net_4_5 for example
938 // project
939 if (!dllReferenceName.EndsWith (".dll"))
940 dllReferenceName += ".dll";
942 if (explicitPath){
943 var probe = Path.GetFullPath (Path.Combine (base_dir, dllReferenceName));
944 foreach (var project in projects){
945 if (probe == project.Value.AbsoluteLibraryOutput)
946 return project.Value;
950 // not explicit, search for the library in the lib path order specified
952 foreach (var libDir in libs) {
953 var abs = Path.GetFullPath (Path.Combine (base_dir, libDir));
954 foreach (var project in projects){
955 var probe = Path.Combine (abs, dllReferenceName);
957 if (probe == project.Value.AbsoluteLibraryOutput)
958 return project.Value;
961 Console.WriteLine ("Did not find referenced {0} with libs={1}", dllReferenceName, String.Join (", ", libs));
962 foreach (var p in projects) {
963 Console.WriteLine (" => {0}", p.Value.AbsoluteLibraryOutput);
965 return null;
970 public class Driver {
972 static IEnumerable<XElement> GetProjects (bool full = false)
974 XDocument doc = XDocument.Load ("order.xml");
975 foreach (XElement project in doc.Root.Elements ()) {
976 string dir = project.Attribute ("dir").Value;
977 string library = project.Attribute ("library").Value;
978 var profile = project.Element ("profile").Value;
981 // Do only class libraries for now
983 if (!(dir.StartsWith ("class") || dir.StartsWith ("mcs") || dir.StartsWith ("basic")))
984 continue;
986 if (full){
987 if (!library.Contains ("tests"))
988 yield return project;
989 continue;
993 // Do not do 2.1, it is not working yet
994 // Do not do basic, as there is no point (requires a system mcs to be installed).
996 if (library.Contains ("moonlight") || library.Contains ("-basic") || library.EndsWith ("bootstrap") || library.Contains ("build"))
997 continue;
999 // The next ones are to make debugging easier for now
1000 if (profile == "basic")
1001 continue;
1002 if (profile != "net_4_5" || library.Contains ("tests"))
1003 continue;
1005 yield return project;
1009 static void Main (string [] args)
1011 if (!File.Exists ("genproj.cs")) {
1012 Console.WriteLine ("This command must be executed from mono/msvc/scripts");
1013 Environment.Exit (1);
1016 if (args.Length == 1 && args [0].ToLower ().Contains ("-h")) {
1017 Console.WriteLine ("Usage:");
1018 Console.WriteLine ("genproj.exe [visual_studio_release] [output_full_solutions]");
1019 Console.WriteLine ("If output_full_solutions is false, only the main System*.dll");
1020 Console.WriteLine (" assemblies (and dependencies) is included in the solution.");
1021 Console.WriteLine ("Example:");
1022 Console.WriteLine ("genproj.exe 2012 false");
1023 Console.WriteLine ("genproj.exe with no arguments is equivalent to 'genproj.exe 2012 true'\n\n");
1024 Console.WriteLine ("genproj.exe deps");
1025 Console.WriteLine ("Generates a Makefile dependency file from the projects input");
1026 Environment.Exit (0);
1029 var slnVersion = (args.Length > 0) ? args [0] : "2012";
1030 bool fullSolutions = (args.Length > 1) ? bool.Parse (args [1]) : true;
1032 // To generate makefile depenedencies
1033 var makefileDeps = (args.Length > 0 && args [0] == "deps");
1035 var sln_gen = new SlnGenerator (slnVersion);
1036 var four_five_sln_gen = new SlnGenerator (slnVersion);
1037 var projects = new Dictionary<string,MsbuildGenerator> ();
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);
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);
1056 } catch (Exception e) {
1057 Console.WriteLine ("Error in {0}\n{1}", project, e);
1061 Func<MsbuildGenerator.VsCsproj, bool> additionalFilter;
1062 additionalFilter = fullSolutions ? (Func<MsbuildGenerator.VsCsproj, bool>)null : IsCommonLibrary;
1064 FillSolution (four_five_sln_gen, MsbuildGenerator.profile_4_5, projects.Values, additionalFilter);
1066 var sb = new StringBuilder ();
1067 sb.AppendLine ("WARNING: Skipped some project references, apparent duplicates in order.xml:");
1068 foreach (var item in duplicates) {
1069 sb.AppendLine (item);
1071 Console.WriteLine (sb.ToString ());
1073 WriteSolution (four_five_sln_gen, MakeSolutionName (MsbuildGenerator.profile_4_5));
1075 if (makefileDeps){
1076 const string classDirPrefix = "./../../";
1077 Console.WriteLine ("here {0}", sln_gen.libraries.Count);
1078 foreach (var p in sln_gen.libraries){
1079 string rebasedOutput = RebaseToClassDirectory (MsbuildGenerator.GetRelativePath ("../../mcs/class", p.library_output));
1081 Console.Write ("{0}: ", rebasedOutput);
1082 foreach (var r in p.projReferences){
1083 var lo = r.library_output;
1084 if (lo.StartsWith (classDirPrefix))
1085 lo = lo.Substring (classDirPrefix.Length);
1086 else
1087 lo = "<<ERROR-dependency is not a class library>>";
1088 Console.Write ("{0} ", lo);
1090 Console.Write ("\n\t(cd {0}; make {1})", p.MsbuildGenerator.dir, p.library_output);
1091 Console.WriteLine ("\n");
1095 // A few other optional solutions
1096 // Solutions with 'everything' and the most common libraries used in development may be of interest
1097 //WriteSolution (sln_gen, "mcs_full.sln");
1098 //WriteSolution (small_full_sln_gen, "small_full.sln");
1099 // The following may be useful if lacking visual studio or MonoDevelop, to bootstrap mono compiler self-hosting
1100 //WriteSolution (basic_sln_gen, "mcs_basic.sln");
1101 //WriteSolution (build_sln_gen, "mcs_build.sln");
1104 // Rebases a path, assuming that execution is taking place in the "class" subdirectory,
1105 // so it strips ../class/ from a path, which is a no-op
1106 static string RebaseToClassDirectory (string path)
1108 const string prefix = "../class/";
1109 int p = path.IndexOf (prefix);
1110 if (p == -1)
1111 return path;
1112 return path.Substring (0, p) + path.Substring (p+prefix.Length);
1113 return path;
1116 static string MakeSolutionName (string profileTag)
1118 return "net" + profileTag + ".sln";
1121 static void FillSolution (SlnGenerator solution, string profileString, IEnumerable<MsbuildGenerator> projects, Func<MsbuildGenerator.VsCsproj, bool> additionalFilter = null)
1123 foreach (var generator in projects) {
1124 var vsCsproj = generator.Csproj;
1125 if (!vsCsproj.library.Contains (profileString))
1126 continue;
1127 if (additionalFilter != null && !additionalFilter (vsCsproj))
1128 continue;
1129 var csprojFilename = vsCsproj.csProjFilename;
1130 if (!solution.ContainsProjectIdentifier (vsCsproj.library)) {
1131 solution.Add (vsCsproj);
1132 RecursiveAddProj (solution, vsCsproj);
1137 static void RecursiveAddProj (SlnGenerator solution, MsbuildGenerator.VsCsproj vsCsproj, int recursiveDepth = 1)
1139 const int max_recursive = 16;
1140 if (recursiveDepth > max_recursive) throw new Exception (string.Format ("Reached {0} levels of project dependency", max_recursive));
1141 foreach (var projRef in vsCsproj.projReferences) {
1142 if (!solution.ContainsProjectIdentifier (projRef.library)) {
1143 solution.Add (projRef);
1144 RecursiveAddProj (solution, projRef, recursiveDepth + 1);
1149 static void WriteSolution (SlnGenerator sln_gen, string slnfilename)
1151 Console.WriteLine (String.Format ("Writing solution {1}, with {0} projects", sln_gen.Count, slnfilename));
1152 sln_gen.Write (slnfilename);
1155 static bool IsCommonLibrary (MsbuildGenerator.VsCsproj proj)
1157 var library = proj.library;
1158 //if (library.Contains ("-basic"))
1159 // return true;
1160 //if (library.Contains ("-build"))
1161 // return true;
1162 //if (library.StartsWith ("corlib"))
1163 // return true;
1164 if (library.StartsWith ("System-"))
1165 return true;
1166 if (library.StartsWith ("System.Xml"))
1167 return true;
1168 if (library.StartsWith ("System.Secu"))
1169 return true;
1170 if (library.StartsWith ("System.Configuration"))
1171 return true;
1172 if (library.StartsWith ("System.Core"))
1173 return true;
1174 //if (library.StartsWith ("Mono."))
1175 // return true;
1177 return false;