2 // Consumes the order.xml file that contains a list of all the assemblies to build
3 // and produces a solution and the csproj files for it
5 // Currently this hardcodes a set of assemblies to build, the net-4.x series, but
6 // it can be extended to handle the command line tools.
9 // * This fails to find matches for "System" and "System.xml" when processing the
10 // RabbitMQ executable, likely, because we do not process executables yet
12 // * Has not been tested in a while with the command line tools
16 using System.Collections.Generic;
18 using System.Globalization;
19 using System.Xml.Linq;
20 using System.Xml.XPath;
25 Library, Exe, Module, WinExe
28 public enum LanguageVersion {
38 public static readonly string NewLine = "\r\n"; //Environment.NewLine; // "\n";
39 public SlnGenerator (string formatVersion = "2012")
41 switch (formatVersion) {
43 this.header = MakeHeader ("10.00", "2008");
46 this.header = MakeHeader ("12.00", "2012");
51 const string project_start = "Project(\"{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}\") = \"{0}\", \"{1}\", \"{2}\""; // Note: No need to double up on {} around {2}
52 const string project_end = "EndProject";
54 public List<MsbuildGenerator.VsCsproj> libraries = new List<MsbuildGenerator.VsCsproj> ();
57 string MakeHeader (string formatVersion, string yearTag)
59 return string.Format ("Microsoft Visual Studio Solution File, Format Version {0}" + NewLine + "# Visual Studio {1}", formatVersion, yearTag);
62 public void Add (MsbuildGenerator.VsCsproj vsproj)
65 libraries.Add (vsproj);
66 } catch (Exception ex) {
67 Console.WriteLine (ex);
71 public void Write (string filename)
73 using (var sln = new StreamWriter (filename)) {
75 sln.WriteLine (header);
76 foreach (var proj in libraries) {
77 sln.WriteLine (project_start, proj.library, proj.csProjFilename, proj.projectGuid);
78 sln.WriteLine (project_end);
80 sln.WriteLine ("Global");
82 sln.WriteLine ("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution");
83 sln.WriteLine ("\t\tDebug|Any CPU = Debug|Any CPU");
84 sln.WriteLine ("\t\tRelease|Any CPU = Release|Any CPU");
85 sln.WriteLine ("\tEndGlobalSection");
87 sln.WriteLine ("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution");
88 foreach (var proj in libraries) {
89 var guid = proj.projectGuid;
90 sln.WriteLine ("\t\t{0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU", guid);
91 sln.WriteLine ("\t\t{0}.Debug|Any CPU.Build.0 = Debug|Any CPU", guid);
92 sln.WriteLine ("\t\t{0}.Release|Any CPU.ActiveCfg = Release|Any CPU", guid);
93 sln.WriteLine ("\t\t{0}.Release|Any CPU.Build.0 = Release|Any CPU", guid);
95 sln.WriteLine ("\tEndGlobalSection");
97 sln.WriteLine ("\tGlobalSection(SolutionProperties) = preSolution");
98 sln.WriteLine ("\t\tHideSolutionNode = FALSE");
99 sln.WriteLine ("\tEndGlobalSection");
101 sln.WriteLine ("EndGlobal");
105 internal bool ContainsProjectIdentifier (string projId)
107 return libraries.FindIndex (x => (x.library == projId)) >= 0;
110 public int Count { get { return libraries.Count; } }
113 class MsbuildGenerator {
114 static readonly string NewLine = SlnGenerator.NewLine;
115 static XmlNamespaceManager xmlns;
117 public const string profile_2_0 = "_2_0";
118 public const string profile_3_5 = "_3_5";
119 public const string profile_4_0 = "_4_0";
120 public const string profile_4_x = "_4_x";
124 Console.WriteLine ("Invalid argument");
127 static string template;
128 static MsbuildGenerator ()
130 using (var input = new StreamReader ("csproj.tmpl")) {
131 template = input.ReadToEnd ();
134 xmlns = new XmlNamespaceManager (new NameTable ());
135 xmlns.AddNamespace ("x", "http://schemas.microsoft.com/developer/msbuild/2003");
138 // The directory as specified in order.xml
145 public string CsprojFilename;
148 // Our base directory, this is relative to our exectution point mono/msvc/scripts
152 public string LibraryOutput, AbsoluteLibraryOutput;
154 public MsbuildGenerator (XElement xproject)
156 this.xproject = xproject;
157 dir = xproject.Attribute ("dir").Value;
158 library = xproject.Attribute ("library").Value;
159 CsprojFilename = "..\\..\\mcs\\" + dir + "\\" + library + ".csproj";
160 LibraryOutput = xproject.Element ("library_output").Value;
162 projectGuid = LookupOrGenerateGuid ();
163 fx_version = xproject.Element ("fx_version").Value;
164 Csproj = new VsCsproj () {
165 csProjFilename = this.CsprojFilename,
166 projectGuid = this.projectGuid,
167 library_output = this.LibraryOutput,
168 fx_version = double.Parse (fx_version),
169 library = this.library,
170 MsbuildGenerator = this
175 class_dir = "../class/";
176 base_dir = "../../mcs/mcs";
180 foreach (char c in dir) {
182 mcs_topdir = "..//" + mcs_topdir;
184 class_dir = mcs_topdir.Substring (3);
186 base_dir = Path.Combine ("..", "..", "mcs", dir);
188 AbsoluteLibraryOutput = Path.GetFullPath (Path.Combine (base_dir, LibraryOutput));
191 string LookupOrGenerateGuid ()
193 var projectFile = NativeName (CsprojFilename);
194 if (File.Exists (projectFile)){
195 var doc = XDocument.Load (projectFile);
196 return doc.XPathSelectElement ("x:Project/x:PropertyGroup/x:ProjectGuid", xmlns).Value;
198 return "{" + Guid.NewGuid ().ToString ().ToUpper () + "}";
203 StringBuilder defines = new StringBuilder ();
204 bool Optimize = true;
205 bool want_debugging_support = false;
207 Dictionary<string, string> embedded_resources = new Dictionary<string, string> ();
208 List<string> warning_as_error = new List<string> ();
209 List<int> ignore_warning = new List<int> ();
210 bool load_default_config = true;
212 List<string> references = new List<string> ();
213 List<string> libs = new List<string> ();
214 List<string> reference_aliases = new List<string> ();
215 bool showWarnings = true;
218 #pragma warning disable 0219, 0414
219 int WarningLevel = 4;
221 bool Checked = false;
222 bool WarningsAreErrors;
223 bool VerifyClsCompliance = true;
224 string win32IconFile;
225 string StrongNameKeyFile;
226 bool copyLocal = true;
227 Target Target = Target.Library;
228 string TargetExt = ".exe";
230 string StrongNameKeyContainer;
231 bool StrongNameDelaySign = false;
232 LanguageVersion Version = LanguageVersion.Default;
235 // Class directory, relative to
237 #pragma warning restore 0219,414
239 readonly char [] argument_value_separator = new char [] { ';', ',' };
242 // This parses the -arg and /arg options to the compiler, even if the strings
243 // in the following text use "/arg" on the strings.
245 bool CSCParseOption (string option, ref string [] args)
247 int idx = option.IndexOf (':');
254 arg = option.Substring (0, idx);
256 value = option.Substring (idx + 1);
259 switch (arg.ToLower (CultureInfo.InvariantCulture)) {
271 Target = Target.WinExe;
275 Target = Target.Library;
280 Target = Target.Module;
281 TargetExt = ".netmodule";
290 if (value.Length == 0) {
292 Environment.Exit (1);
310 case "/incremental+":
311 case "/incremental-":
317 if (value.Length == 0) {
319 Environment.Exit (1);
322 foreach (string d in value.Split (argument_value_separator)) {
323 if (defines.Length != 0)
324 defines.Append (";");
333 // We should collect data, runtime, etc and store in the file specified
337 case "/linkresource":
340 bool embeded = arg [1] == 'r' || arg [1] == 'R';
341 string [] s = value.Split (argument_value_separator);
344 if (s [0].Length == 0)
346 embedded_resources [s [0]] = Path.GetFileName (s [0]);
349 embedded_resources [s [0]] = s [1];
352 Console.WriteLine ("Does not support this method yet: {0}", arg);
353 Environment.Exit (1);
356 Console.WriteLine ("Wrong number of arguments for option `{0}'", option);
357 Environment.Exit (1);
364 Console.WriteLine ("/recurse not supported");
365 Environment.Exit (1);
370 if (value.Length == 0) {
371 Console.WriteLine ("-reference requires an argument");
372 Environment.Exit (1);
375 string [] refs = value.Split (argument_value_separator);
376 foreach (string r in refs) {
378 int index = val.IndexOf ('=');
380 reference_aliases.Add (r);
385 references.Add (val);
398 Console.WriteLine ("{0} = not supported", arg);
406 win32IconFile = value;
410 want_debugging_support = false;
415 want_debugging_support = true;
432 VerifyClsCompliance = false;
445 case "/warnaserror+":
446 if (value.Length == 0) {
447 WarningsAreErrors = true;
449 foreach (string wid in value.Split (argument_value_separator))
450 warning_as_error.Add (wid);
455 // Console.WriteLine ("Warning ignoring /runtime:v4");
458 case "/warnaserror-":
459 if (value.Length == 0) {
460 WarningsAreErrors = false;
462 foreach (string wid in value.Split (argument_value_separator))
463 warning_as_error.Remove (wid);
468 WarningLevel = Int32.Parse (value);
474 if (value.Length == 0) {
475 Console.WriteLine ("/nowarn requires an argument");
476 Environment.Exit (1);
479 warns = value.Split (argument_value_separator);
480 foreach (string wc in warns) {
482 if (wc.Trim ().Length == 0)
485 int warn = Int32.Parse (wc);
487 throw new ArgumentOutOfRangeException ("warn");
489 ignore_warning.Add (warn);
491 Console.WriteLine (String.Format ("`{0}' is not a valid warning number", wc));
492 Environment.Exit (1);
499 load_default_config = false;
515 if (value == String.Empty) {
516 Console.WriteLine ("{0} requires an argument", arg);
517 Environment.Exit (1);
519 StrongNameKeyFile = value;
521 case "/keycontainer":
522 if (value == String.Empty) {
523 Console.WriteLine ("{0} requires an argument", arg);
524 Environment.Exit (1);
526 StrongNameKeyContainer = value;
530 StrongNameDelaySign = true;
533 StrongNameDelaySign = false;
537 switch (value.ToLower (CultureInfo.InvariantCulture)) {
539 Version = LanguageVersion.ISO_1;
543 Version = LanguageVersion.Default;
546 Version = LanguageVersion.ISO_2;
549 Version = LanguageVersion.Future;
552 Console.WriteLine ("Invalid option `{0}' for /langversion. It must be either `ISO-1', `ISO-2' or `Default'", value);
553 Environment.Exit (1);
563 case "/-getresourcestrings":
567 Console.WriteLine ("Failing with : {0}", arg);
571 static string [] LoadArgs (string file)
574 var args = new List<string> ();
577 f = new StreamReader (file);
582 StringBuilder sb = new StringBuilder ();
584 while ((line = f.ReadLine ()) != null) {
587 for (int i = 0; i < t; i++) {
590 if (c == '"' || c == '\'') {
593 for (i++; i < t; i++) {
600 } else if (c == ' ') {
602 args.Add (sb.ToString ());
609 args.Add (sb.ToString ());
614 string [] ret_value = new string [args.Count];
615 args.CopyTo (ret_value, 0);
620 static string Load (string f)
622 var native = NativeName (f);
624 if (File.Exists (native)) {
625 using (var sr = new StreamReader (native)) {
626 return sr.ReadToEnd ();
632 public static string NativeName (string path)
634 if (System.IO.Path.DirectorySeparatorChar == '/')
635 return path.Replace ("\\", "/");
637 return path.Replace ("/", "\\");
640 public class VsCsproj {
641 public string projectGuid;
642 public string output;
643 public string library_output;
644 public string csProjFilename;
645 public double fx_version;
646 public List<VsCsproj> projReferences = new List<VsCsproj> ();
647 public string library;
648 public MsbuildGenerator MsbuildGenerator;
651 public VsCsproj Csproj;
653 public VsCsproj Generate (Dictionary<string,MsbuildGenerator> projects, bool showWarnings = false)
655 var generatedProjFile = NativeName (Csproj.csProjFilename);
656 //Console.WriteLine ("Generating: {0}", generatedProjFile);
658 string boot, flags, output_name, built_sources, response, profile;
660 boot = xproject.Element ("boot").Value;
661 flags = xproject.Element ("flags").Value;
662 output_name = xproject.Element ("output").Value;
663 if (output_name.EndsWith (".exe"))
665 built_sources = xproject.Element ("built_sources").Value;
666 response = xproject.Element ("response").Value;
668 profile = xproject.Element ("profile").Value;
669 if (string.IsNullOrEmpty (response)) {
670 // Address the issue where entries are missing the fx_version
671 // Should be fixed in the Makefile or elsewhere; this is a workaround
672 //<fx_version>basic</fx_version>
673 //<profile>./../build/deps/mcs.exe.sources.response</profile>
674 //<response></response>
676 profile = fx_version;
677 if (response.Contains ("build") || response.Contains ("basic") || response.Contains (profile_2_0)) {
679 if (response.Contains (profile_2_0)) profile = "net_2_0";
680 } if (response.Contains ("build") || response.Contains ("basic") || response.Contains (profile_2_0)) {
682 } else if (response.Contains (profile_3_5)) {
685 } else if (response.Contains (profile_4_0)) {
688 } else if (response.Contains (profile_4_x)) {
694 // Prebuild code, might be in inputs, check:
695 // inputs/LIBRARY-PROFILE.pre
696 // inputs/LIBRARY.pre
698 string prebuild = Load (library + ".pre");
699 string prebuild_windows, prebuild_unix;
701 int q = library.IndexOf ("-");
703 prebuild = prebuild + Load (library.Substring (0, q) + ".pre");
705 if (prebuild.IndexOf ("@MONO@") != -1){
706 prebuild_unix = prebuild.Replace ("@MONO@", "mono").Replace ("@CAT@", "cat");
707 prebuild_windows = prebuild.Replace ("@MONO@", "").Replace ("@CAT@", "type");
709 prebuild_unix = prebuild.Replace ("jay.exe", "jay");
710 prebuild_windows = prebuild;
713 const string condition_unix = "Condition=\" '$(OS)' != 'Windows_NT' \"";
714 const string condition_windows = "Condition=\" '$(OS)' == 'Windows_NT' \"";
716 " <PreBuildEvent " + condition_unix + ">" + NewLine + prebuild_unix + NewLine + " </PreBuildEvent>" + NewLine +
717 " <PreBuildEvent " + condition_windows + ">" + NewLine + prebuild_windows + NewLine + " </PreBuildEvent>" + NewLine;
719 var all_args = new Queue<string []> ();
720 all_args.Enqueue (flags.Split ());
721 while (all_args.Count > 0) {
722 string [] f = all_args.Dequeue ();
724 for (int i = 0; i < f.Length; i++) {
725 if (f [i].Length > 0 && f [i][0] == '-')
726 f [i] = "/" + f [i].Substring (1);
728 if (f [i] [0] == '@') {
729 string [] extra_args;
730 string response_file = f [i].Substring (1);
732 var resp_file_full = Path.Combine (base_dir, response_file);
733 extra_args = LoadArgs (resp_file_full);
734 if (extra_args == null) {
735 Console.WriteLine ("Unable to open response file: " + resp_file_full);
736 Environment.Exit (1);
739 all_args.Enqueue (extra_args);
743 if (CSCParseOption (f [i], ref f))
745 Console.WriteLine ("Failure with {0}", f [i]);
746 Environment.Exit (1);
750 string [] source_files;
751 //Console.WriteLine ("Base: {0} res: {1}", base_dir, response);
752 using (var reader = new StreamReader (NativeName (base_dir + "\\" + response))) {
753 source_files = reader.ReadToEnd ().Split ();
756 Array.Sort (source_files);
758 StringBuilder sources = new StringBuilder ();
759 foreach (string s in source_files) {
763 string src = s.Replace ("/", "\\");
764 if (src.StartsWith (@"Test\..\"))
765 src = src.Substring (8, src.Length - 8);
767 sources.AppendFormat (" <Compile Include=\"{0}\" />" + NewLine, src);
770 source_files = built_sources.Split ();
771 Array.Sort (source_files);
773 foreach (string s in source_files) {
777 string src = s.Replace ("/", "\\");
778 if (src.StartsWith (@"Test\..\"))
779 src = src.Substring (8, src.Length - 8);
781 sources.AppendFormat (" <Compile Include=\"{0}\" />" + NewLine, src);
783 sources.Remove (sources.Length - 1, 1);
785 //if (library == "corlib-build") // otherwise, does not compile on fx_version == 4.0
787 // references.Add("System.dll");
788 // references.Add("System.Xml.dll");
791 //if (library == "System.Core-build") // otherwise, slow compile. May be a transient need.
793 // this.ignore_warning.Add(1685);
794 // this.ignore_warning.Add(0436);
797 var refs = new StringBuilder ();
799 bool is_test = response.Contains ("_test_");
801 // F:\src\mono\mcs\class\lib\net_2_0\nunit.framework.dll
802 // F:\src\mono\mcs\class\SomeProject\SomeProject_test_-net_2_0.csproj
803 var nunitLibPath = string.Format (@"..\lib\{0}\nunit.framework.dll", profile);
804 refs.Append (string.Format (" <Reference Include=\"{0}\" />" + NewLine, nunitLibPath));
807 var resources = new StringBuilder ();
808 if (embedded_resources.Count > 0) {
809 resources.AppendFormat (" <ItemGroup>" + NewLine);
810 foreach (var dk in embedded_resources) {
811 resources.AppendFormat (" <EmbeddedResource Include=\"{0}\">" + NewLine, dk.Key);
812 resources.AppendFormat (" <LogicalName>{0}</LogicalName>" + NewLine, dk.Value);
813 resources.AppendFormat (" </EmbeddedResource>" + NewLine);
815 resources.AppendFormat (" </ItemGroup>" + NewLine);
819 if (references.Count > 0 || reference_aliases.Count > 0) {
820 // -r:mscorlib.dll -r:System.dll
821 //<ProjectReference Include="..\corlib\corlib-basic.csproj">
822 // <Project>{155aef28-c81f-405d-9072-9d52780e3e70}</Project>
823 // <Name>corlib-basic</Name>
824 //</ProjectReference>
825 //<ProjectReference Include="..\System\System-basic.csproj">
826 // <Project>{2094e859-db2f-481f-9630-f89d31d9ed48}</Project>
827 // <Name>System-basic</Name>
828 //</ProjectReference>
829 var refdistinct = references.Distinct ();
830 foreach (string r in refdistinct) {
832 var match = GetMatchingCsproj (r, projects);
834 AddProjectReference (refs, Csproj, match, r, null);
837 Console.WriteLine ("{0}: Could not find a matching project reference for {1}", library, Path.GetFileName (r));
838 Console.WriteLine (" --> Adding reference with hintpath instead");
840 refs.Append (" <Reference Include=\"" + r + "\">" + NewLine);
841 refs.Append (" <SpecificVersion>False</SpecificVersion>" + NewLine);
842 refs.Append (" <HintPath>" + r + "</HintPath>" + NewLine);
843 refs.Append (" <Private>False</Private>" + NewLine);
844 refs.Append (" </Reference>" + NewLine);
848 foreach (string r in reference_aliases) {
849 int index = r.IndexOf ('=');
850 string alias = r.Substring (0, index);
851 string assembly = r.Substring (index + 1);
852 var match = GetMatchingCsproj (assembly, projects, explicitPath: true);
854 AddProjectReference (refs, Csproj, match, r, alias);
856 throw new NotSupportedException (string.Format ("From {0}, could not find a matching project reference for {1}", library, r));
857 refs.Append (" <Reference Include=\"" + assembly + "\">" + NewLine);
858 refs.Append (" <SpecificVersion>False</SpecificVersion>" + NewLine);
859 refs.Append (" <HintPath>" + r + "</HintPath>" + NewLine);
860 refs.Append (" <Aliases>" + alias + "</Aliases>" + NewLine);
861 refs.Append (" </Reference>" + NewLine);
868 // ../class/lib/build/tmp/System.Xml.dll [No longer possible, we should be removing this from order.xml]
869 // /class/lib/basic/System.Core.dll
870 // <library_output>mcs.exe</library_output>
871 string build_output_dir;
872 if (LibraryOutput.Contains ("/"))
873 build_output_dir = Path.GetDirectoryName (LibraryOutput);
875 build_output_dir = "bin\\Debug\\" + library;
878 string postbuild_unix = string.Empty;
879 string postbuild_windows = string.Empty;
882 " <PostBuildEvent " + condition_unix + ">" + NewLine + postbuild_unix + NewLine + " </PostBuildEvent>" + NewLine +
883 " <PostBuildEvent " + condition_windows + ">" + NewLine + postbuild_windows + NewLine + " </PostBuildEvent>";
886 bool basic_or_build = (library.Contains ("-basic") || library.Contains ("-build"));
889 // Replace the template values
892 string strongNameSection = "";
893 if (StrongNameKeyFile != null){
894 strongNameSection = String.Format (
895 " <PropertyGroup>" + NewLine +
896 " <SignAssembly>true</SignAssembly>" + NewLine +
898 " </PropertyGroup>" + NewLine +
899 " <PropertyGroup>" + NewLine +
900 " <AssemblyOriginatorKeyFile>{0}</AssemblyOriginatorKeyFile>" + NewLine +
901 " </PropertyGroup>", StrongNameKeyFile, StrongNameDelaySign ? " <DelaySign>true</DelaySign>" + NewLine : "");
903 Csproj.output = template.
904 Replace ("@OUTPUTTYPE@", Target == Target.Library ? "Library" : "Exe").
905 Replace ("@SIGNATURE@", strongNameSection).
906 Replace ("@PROJECTGUID@", Csproj.projectGuid).
907 Replace ("@DEFINES@", defines.ToString ()).
908 Replace ("@DISABLEDWARNINGS@", string.Join (",", (from i in ignore_warning select i.ToString ()).ToArray ())).
909 //Replace("@NOSTDLIB@", (basic_or_build || (!StdLib)) ? "<NoStdLib>true</NoStdLib>" : string.Empty).
910 Replace ("@NOSTDLIB@", "<NoStdLib>" + (!StdLib).ToString () + "</NoStdLib>").
911 Replace ("@NOCONFIG@", "<NoConfig>" + (!load_default_config).ToString () + "</NoConfig>").
912 Replace ("@ALLOWUNSAFE@", Unsafe ? "<AllowUnsafeBlocks>true</AllowUnsafeBlocks>" : "").
913 Replace ("@FX_VERSION", fx_version).
914 Replace ("@ASSEMBLYNAME@", Path.GetFileNameWithoutExtension (output_name)).
915 Replace ("@OUTPUTDIR@", build_output_dir).
916 Replace ("@DEFINECONSTANTS@", defines.ToString ()).
917 Replace ("@DEBUG@", want_debugging_support ? "true" : "false").
918 Replace ("@DEBUGTYPE@", want_debugging_support ? "full" : "pdbonly").
919 Replace ("@REFERENCES@", refs.ToString ()).
920 Replace ("@PREBUILD@", prebuild).
921 Replace ("@STARTUPOBJECT@", main == null ? "" : $"<StartupObject>{main}</StartupObject>").
922 Replace ("@POSTBUILD@", postbuild).
923 //Replace ("@ADDITIONALLIBPATHS@", String.Format ("<AdditionalLibPaths>{0}</AdditionalLibPaths>", string.Join (",", libs.ToArray ()))).
924 Replace ("@ADDITIONALLIBPATHS@", String.Empty).
925 Replace ("@RESOURCES@", resources.ToString ()).
926 Replace ("@OPTIMIZE@", Optimize ? "true" : "false").
927 Replace ("@SOURCES@", sources.ToString ());
929 //Console.WriteLine ("Generated {0}", ofile.Replace ("\\", "/"));
930 using (var o = new StreamWriter (generatedProjFile)) {
931 o.WriteLine (Csproj.output);
937 void AddProjectReference (StringBuilder refs, VsCsproj result, MsbuildGenerator match, string r, string alias)
939 refs.AppendFormat (" <ProjectReference Include=\"{0}\">{1}", GetRelativePath (result.csProjFilename, match.CsprojFilename), NewLine);
940 refs.Append (" <Project>" + match.projectGuid + "</Project>" + NewLine);
941 refs.Append (" <Name>" + Path.GetFileNameWithoutExtension (match.CsprojFilename.Replace ('\\', Path.DirectorySeparatorChar)) + "</Name>" + NewLine);
943 refs.Append (" <Aliases>" + alias + "</Aliases>");
944 refs.Append (" </ProjectReference>" + NewLine);
945 if (!result.projReferences.Contains (match.Csproj))
946 result.projReferences.Add (match.Csproj);
949 public static string GetRelativePath (string from, string to)
951 from = from.Replace ("\\", "/");
952 to = to.Replace ("\\", "/");
953 var fromUri = new Uri (Path.GetFullPath (from));
954 var toUri = new Uri (Path.GetFullPath (to));
956 var ret = fromUri.MakeRelativeUri (toUri).ToString ().Replace ("%5C", "\x5c");
960 MsbuildGenerator GetMatchingCsproj (string dllReferenceName, Dictionary<string,MsbuildGenerator> projects, bool explicitPath = false)
962 // libDir would be "./../../class/lib/net_4_x for example
964 if (!dllReferenceName.EndsWith (".dll"))
965 dllReferenceName += ".dll";
967 var probe = Path.GetFullPath (Path.Combine (base_dir, dllReferenceName));
968 foreach (var project in projects){
969 if (probe == project.Value.AbsoluteLibraryOutput)
970 return project.Value;
973 // not explicit, search for the library in the lib path order specified
975 foreach (var libDir in libs) {
976 var abs = Path.GetFullPath (Path.Combine (base_dir, libDir));
977 foreach (var project in projects){
978 probe = Path.Combine (abs, dllReferenceName);
980 if (probe == project.Value.AbsoluteLibraryOutput)
981 return project.Value;
984 Console.WriteLine ("Did not find referenced {0} with libs={1}", dllReferenceName, String.Join (", ", libs));
985 foreach (var p in projects) {
986 // Console.WriteLine ("{0}", p.Value.AbsoluteLibraryOutput);
993 public class Driver {
995 static IEnumerable<XElement> GetProjects (bool full = false)
997 XDocument doc = XDocument.Load ("order.xml");
998 foreach (XElement project in doc.Root.Elements ()) {
999 string dir = project.Attribute ("dir").Value;
1000 string library = project.Attribute ("library").Value;
1001 var profile = project.Element ("profile").Value;
1003 // Skip facades for now, the tool doesn't know how to deal with them yet.
1004 if (dir.Contains ("Facades"))
1007 // These are currently broken, skip until they're fixed.
1008 if (dir.StartsWith ("mcs") || dir.Contains ("apigen"))
1012 // Do only class libraries for now
1014 if (!(dir.StartsWith ("class") || dir.StartsWith ("mcs") || dir.StartsWith ("basic")))
1018 if (!library.Contains ("tests"))
1019 yield return project;
1024 // Do not do 2.1, it is not working yet
1025 // Do not do basic, as there is no point (requires a system mcs to be installed).
1027 if (library.Contains ("moonlight") || library.Contains ("-basic") || library.EndsWith ("bootstrap") || library.Contains ("build"))
1030 // The next ones are to make debugging easier for now
1031 if (profile == "basic")
1033 if (profile != "net_4_x" || library.Contains ("tests"))
1036 yield return project;
1040 static void Main (string [] args)
1042 if (!File.Exists ("genproj.cs")) {
1043 Console.WriteLine ("This command must be executed from mono/msvc/scripts");
1044 Environment.Exit (1);
1047 if (args.Length == 1 && args [0].ToLower ().Contains ("-h")) {
1048 Console.WriteLine ("Usage:");
1049 Console.WriteLine ("genproj.exe [visual_studio_release] [output_full_solutions]");
1050 Console.WriteLine ("If output_full_solutions is false, only the main System*.dll");
1051 Console.WriteLine (" assemblies (and dependencies) is included in the solution.");
1052 Console.WriteLine ("Example:");
1053 Console.WriteLine ("genproj.exe 2012 false");
1054 Console.WriteLine ("genproj.exe with no arguments is equivalent to 'genproj.exe 2012 true'\n\n");
1055 Console.WriteLine ("genproj.exe deps");
1056 Console.WriteLine ("Generates a Makefile dependency file from the projects input");
1057 Environment.Exit (0);
1060 var slnVersion = (args.Length > 0) ? args [0] : "2012";
1061 bool fullSolutions = (args.Length > 1) ? bool.Parse (args [1]) : true;
1063 // To generate makefile depenedencies
1064 var makefileDeps = (args.Length > 0 && args [0] == "deps");
1066 var sln_gen = new SlnGenerator (slnVersion);
1067 var four_five_sln_gen = new SlnGenerator (slnVersion);
1068 var projects = new Dictionary<string,MsbuildGenerator> ();
1070 var duplicates = new List<string> ();
1071 foreach (var project in GetProjects (makefileDeps)) {
1072 var library_output = project.Element ("library_output").Value;
1073 projects [library_output] = new MsbuildGenerator (project);
1075 foreach (var project in GetProjects (makefileDeps)){
1076 var library_output = project.Element ("library_output").Value;
1077 var gen = projects [library_output];
1079 var csproj = gen.Generate (projects);
1080 var csprojFilename = csproj.csProjFilename;
1081 if (!sln_gen.ContainsProjectIdentifier (csproj.library)) {
1082 sln_gen.Add (csproj);
1084 duplicates.Add (csprojFilename);
1087 } catch (Exception e) {
1088 Console.WriteLine ("Error in {0}\n{1}", project, e);
1092 Func<MsbuildGenerator.VsCsproj, bool> additionalFilter;
1093 additionalFilter = fullSolutions ? (Func<MsbuildGenerator.VsCsproj, bool>)null : IsCommonLibrary;
1095 FillSolution (four_five_sln_gen, MsbuildGenerator.profile_4_x, projects.Values, additionalFilter);
1097 if (duplicates.Count () > 0) {
1098 var sb = new StringBuilder ();
1099 sb.AppendLine ("WARNING: Skipped some project references, apparent duplicates in order.xml:");
1100 foreach (var item in duplicates) {
1101 sb.AppendLine (item);
1103 Console.WriteLine (sb.ToString ());
1106 WriteSolution (four_five_sln_gen, MakeSolutionName (MsbuildGenerator.profile_4_x));
1109 const string classDirPrefix = "./../../";
1110 Console.WriteLine ("here {0}", sln_gen.libraries.Count);
1111 foreach (var p in sln_gen.libraries){
1112 string rebasedOutput = RebaseToClassDirectory (MsbuildGenerator.GetRelativePath ("../../mcs/class", p.library_output));
1114 Console.Write ("{0}: ", rebasedOutput);
1115 foreach (var r in p.projReferences){
1116 var lo = r.library_output;
1117 if (lo.StartsWith (classDirPrefix))
1118 lo = lo.Substring (classDirPrefix.Length);
1120 lo = "<<ERROR-dependency is not a class library>>";
1121 Console.Write ("{0} ", lo);
1123 Console.Write ("\n\t(cd {0}; make {1})", p.MsbuildGenerator.dir, p.library_output);
1124 Console.WriteLine ("\n");
1128 // A few other optional solutions
1129 // Solutions with 'everything' and the most common libraries used in development may be of interest
1130 //WriteSolution (sln_gen, "mcs_full.sln");
1131 //WriteSolution (small_full_sln_gen, "small_full.sln");
1132 // The following may be useful if lacking visual studio or MonoDevelop, to bootstrap mono compiler self-hosting
1133 //WriteSolution (basic_sln_gen, "mcs_basic.sln");
1134 //WriteSolution (build_sln_gen, "mcs_build.sln");
1137 // Rebases a path, assuming that execution is taking place in the "class" subdirectory,
1138 // so it strips ../class/ from a path, which is a no-op
1139 static string RebaseToClassDirectory (string path)
1141 const string prefix = "../class/";
1142 int p = path.IndexOf (prefix);
1145 return path.Substring (0, p) + path.Substring (p+prefix.Length);
1149 static string MakeSolutionName (string profileTag)
1151 return "net" + profileTag + ".sln";
1154 static void FillSolution (SlnGenerator solution, string profileString, IEnumerable<MsbuildGenerator> projects, Func<MsbuildGenerator.VsCsproj, bool> additionalFilter = null)
1156 foreach (var generator in projects) {
1157 var vsCsproj = generator.Csproj;
1158 if (!vsCsproj.library.Contains (profileString))
1160 if (additionalFilter != null && !additionalFilter (vsCsproj))
1162 var csprojFilename = vsCsproj.csProjFilename;
1163 if (!solution.ContainsProjectIdentifier (vsCsproj.library)) {
1164 solution.Add (vsCsproj);
1165 RecursiveAddProj (solution, vsCsproj);
1170 static void RecursiveAddProj (SlnGenerator solution, MsbuildGenerator.VsCsproj vsCsproj, int recursiveDepth = 1)
1172 const int max_recursive = 16;
1173 if (recursiveDepth > max_recursive) throw new Exception (string.Format ("Reached {0} levels of project dependency", max_recursive));
1174 foreach (var projRef in vsCsproj.projReferences) {
1175 if (!solution.ContainsProjectIdentifier (projRef.library)) {
1176 solution.Add (projRef);
1177 RecursiveAddProj (solution, projRef, recursiveDepth + 1);
1182 static void WriteSolution (SlnGenerator sln_gen, string slnfilename)
1184 Console.WriteLine (String.Format ("Writing solution {1}, with {0} projects", sln_gen.Count, slnfilename));
1185 sln_gen.Write (slnfilename);
1188 static bool IsCommonLibrary (MsbuildGenerator.VsCsproj proj)
1190 var library = proj.library;
1191 //if (library.Contains ("-basic"))
1193 //if (library.Contains ("-build"))
1195 //if (library.StartsWith ("corlib"))
1197 if (library.StartsWith ("System-"))
1199 if (library.StartsWith ("System.Xml"))
1201 if (library.StartsWith ("System.Secu"))
1203 if (library.StartsWith ("System.Configuration"))
1205 if (library.StartsWith ("System.Core"))
1207 //if (library.StartsWith ("Mono."))