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 var fullPath
= Path
.GetDirectoryName (filename
) + "/";
75 using (var sln
= new StreamWriter (filename
)) {
77 sln
.WriteLine (header
);
78 foreach (var proj
in libraries
) {
79 var unixProjFile
= proj
.csProjFilename
.Replace ("\\", "/");
80 var fullProjPath
= Path
.GetFullPath (unixProjFile
);
81 sln
.WriteLine (project_start
, proj
.library
, MsbuildGenerator
.GetRelativePath (fullPath
, fullProjPath
), proj
.projectGuid
);
82 sln
.WriteLine (project_end
);
84 sln
.WriteLine ("Global");
86 sln
.WriteLine ("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution");
87 sln
.WriteLine ("\t\tDebug|Any CPU = Debug|Any CPU");
88 sln
.WriteLine ("\t\tRelease|Any CPU = Release|Any CPU");
89 sln
.WriteLine ("\tEndGlobalSection");
91 sln
.WriteLine ("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution");
92 foreach (var proj
in libraries
) {
93 var guid
= proj
.projectGuid
;
94 sln
.WriteLine ("\t\t{0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU", guid
);
95 sln
.WriteLine ("\t\t{0}.Debug|Any CPU.Build.0 = Debug|Any CPU", guid
);
96 sln
.WriteLine ("\t\t{0}.Release|Any CPU.ActiveCfg = Release|Any CPU", guid
);
97 sln
.WriteLine ("\t\t{0}.Release|Any CPU.Build.0 = Release|Any CPU", guid
);
99 sln
.WriteLine ("\tEndGlobalSection");
101 sln
.WriteLine ("\tGlobalSection(SolutionProperties) = preSolution");
102 sln
.WriteLine ("\t\tHideSolutionNode = FALSE");
103 sln
.WriteLine ("\tEndGlobalSection");
105 sln
.WriteLine ("EndGlobal");
109 internal bool ContainsProjectIdentifier (string projId
)
111 return libraries
.FindIndex (x
=> (x
.library
== projId
)) >= 0;
114 public int Count { get { return libraries.Count; }
}
117 class MsbuildGenerator
{
118 static readonly string NewLine
= SlnGenerator
.NewLine
;
119 static XmlNamespaceManager xmlns
;
121 public const string profile_2_0
= "_2_0";
122 public const string profile_3_5
= "_3_5";
123 public const string profile_4_0
= "_4_0";
124 public const string profile_4_x
= "_4_x";
128 Console
.WriteLine ("Invalid argument");
131 static string template
;
132 static MsbuildGenerator ()
134 using (var input
= new StreamReader ("csproj.tmpl")) {
135 template
= input
.ReadToEnd ();
138 xmlns
= new XmlNamespaceManager (new NameTable ());
139 xmlns
.AddNamespace ("x", "http://schemas.microsoft.com/developer/msbuild/2003");
142 // The directory as specified in order.xml
149 public string CsprojFilename
;
152 // Our base directory, this is relative to our exectution point mono/msvc/scripts
156 public string LibraryOutput
, AbsoluteLibraryOutput
;
158 public MsbuildGenerator (XElement xproject
)
160 this.xproject
= xproject
;
161 dir
= xproject
.Attribute ("dir").Value
;
162 library
= xproject
.Attribute ("library").Value
;
163 CsprojFilename
= "..\\..\\mcs\\" + dir
+ "\\" + library
+ ".csproj";
164 LibraryOutput
= xproject
.Element ("library_output").Value
;
166 projectGuid
= LookupOrGenerateGuid ();
167 fx_version
= xproject
.Element ("fx_version").Value
;
168 Csproj
= new VsCsproj () {
169 csProjFilename
= this.CsprojFilename
,
170 projectGuid
= this.projectGuid
,
171 library_output
= this.LibraryOutput
,
172 fx_version
= double.Parse (fx_version
),
173 library
= this.library
,
174 MsbuildGenerator
= this
179 class_dir
= "../class/";
180 base_dir
= "../../mcs/mcs";
184 foreach (char c
in dir
) {
186 mcs_topdir
= "..//" + mcs_topdir
;
188 class_dir
= mcs_topdir
.Substring (3);
190 base_dir
= Path
.Combine ("..", "..", "mcs", dir
);
192 AbsoluteLibraryOutput
= Path
.GetFullPath (Path
.Combine (base_dir
, LibraryOutput
));
195 string LookupOrGenerateGuid ()
197 var projectFile
= NativeName (CsprojFilename
);
198 if (File
.Exists (projectFile
)){
199 var doc
= XDocument
.Load (projectFile
);
200 return doc
.XPathSelectElement ("x:Project/x:PropertyGroup/x:ProjectGuid", xmlns
).Value
;
202 return "{" + Guid.NewGuid ().ToString ().ToUpper () + "}";
207 StringBuilder defines
= new StringBuilder ();
208 bool Optimize
= true;
209 bool want_debugging_support
= false;
211 Dictionary
<string, string> embedded_resources
= new Dictionary
<string, string> ();
212 List
<string> warning_as_error
= new List
<string> ();
213 List
<int> ignore_warning
= new List
<int> ();
214 bool load_default_config
= true;
216 List
<string> references
= new List
<string> ();
217 List
<string> libs
= new List
<string> ();
218 List
<string> reference_aliases
= new List
<string> ();
219 bool showWarnings
= true;
222 #pragma warning disable 0219, 0414
223 int WarningLevel
= 4;
225 bool Checked
= false;
226 bool WarningsAreErrors
;
227 bool VerifyClsCompliance
= true;
228 string win32IconFile
;
229 string StrongNameKeyFile
;
230 bool copyLocal
= true;
231 Target Target
= Target
.Library
;
232 string TargetExt
= ".exe";
234 string StrongNameKeyContainer
;
235 bool StrongNameDelaySign
= false;
236 LanguageVersion Version
= LanguageVersion
.Default
;
239 // Class directory, relative to
241 #pragma warning restore 0219,414
243 readonly char [] argument_value_separator
= new char [] { ';', ',' }
;
246 // This parses the -arg and /arg options to the compiler, even if the strings
247 // in the following text use "/arg" on the strings.
249 bool CSCParseOption (string option
, ref string [] args
)
251 int idx
= option
.IndexOf (':');
258 arg
= option
.Substring (0, idx
);
260 value = option
.Substring (idx
+ 1);
263 switch (arg
.ToLower (CultureInfo
.InvariantCulture
)) {
275 Target
= Target
.WinExe
;
279 Target
= Target
.Library
;
284 Target
= Target
.Module
;
285 TargetExt
= ".netmodule";
294 if (value.Length
== 0) {
296 Environment
.Exit (1);
314 case "/incremental+":
315 case "/incremental-":
321 if (value.Length
== 0) {
323 Environment
.Exit (1);
326 foreach (string d
in value.Split (argument_value_separator
)) {
327 if (defines
.Length
!= 0)
328 defines
.Append (";");
337 // We should collect data, runtime, etc and store in the file specified
341 case "/linkresource":
344 bool embeded
= arg
[1] == 'r' || arg
[1] == 'R';
345 string [] s
= value.Split (argument_value_separator
);
348 if (s
[0].Length
== 0)
350 embedded_resources
[s
[0]] = Path
.GetFileName (s
[0]);
353 embedded_resources
[s
[0]] = s
[1];
356 Console
.WriteLine ("Does not support this method yet: {0}", arg
);
357 Environment
.Exit (1);
360 Console
.WriteLine ("Wrong number of arguments for option `{0}'", option
);
361 Environment
.Exit (1);
368 Console
.WriteLine ("/recurse not supported");
369 Environment
.Exit (1);
374 if (value.Length
== 0) {
375 Console
.WriteLine ("-reference requires an argument");
376 Environment
.Exit (1);
379 string [] refs
= value.Split (argument_value_separator
);
380 foreach (string r
in refs
) {
382 int index
= val
.IndexOf ('=');
384 reference_aliases
.Add (r
);
389 references
.Add (val
);
402 Console
.WriteLine ("{0} = not supported", arg
);
410 win32IconFile
= value;
414 want_debugging_support
= false;
419 want_debugging_support
= true;
436 VerifyClsCompliance
= false;
449 case "/warnaserror+":
450 if (value.Length
== 0) {
451 WarningsAreErrors
= true;
453 foreach (string wid
in value.Split (argument_value_separator
))
454 warning_as_error
.Add (wid
);
459 // Console.WriteLine ("Warning ignoring /runtime:v4");
462 case "/warnaserror-":
463 if (value.Length
== 0) {
464 WarningsAreErrors
= false;
466 foreach (string wid
in value.Split (argument_value_separator
))
467 warning_as_error
.Remove (wid
);
472 WarningLevel
= Int32
.Parse (value);
478 if (value.Length
== 0) {
479 Console
.WriteLine ("/nowarn requires an argument");
480 Environment
.Exit (1);
483 warns
= value.Split (argument_value_separator
);
484 foreach (string wc
in warns
) {
486 if (wc
.Trim ().Length
== 0)
489 int warn
= Int32
.Parse (wc
);
491 throw new ArgumentOutOfRangeException ("warn");
493 ignore_warning
.Add (warn
);
495 Console
.WriteLine (String
.Format ("`{0}' is not a valid warning number", wc
));
496 Environment
.Exit (1);
503 load_default_config
= false;
519 if (value == String
.Empty
) {
520 Console
.WriteLine ("{0} requires an argument", arg
);
521 Environment
.Exit (1);
523 StrongNameKeyFile
= value;
525 case "/keycontainer":
526 if (value == String
.Empty
) {
527 Console
.WriteLine ("{0} requires an argument", arg
);
528 Environment
.Exit (1);
530 StrongNameKeyContainer
= value;
534 StrongNameDelaySign
= true;
537 StrongNameDelaySign
= false;
541 switch (value.ToLower (CultureInfo
.InvariantCulture
)) {
543 Version
= LanguageVersion
.ISO_1
;
547 Version
= LanguageVersion
.Default
;
550 Version
= LanguageVersion
.ISO_2
;
553 Version
= LanguageVersion
.Future
;
556 Console
.WriteLine ("Invalid option `{0}' for /langversion. It must be either `ISO-1', `ISO-2' or `Default'", value);
557 Environment
.Exit (1);
567 case "/deterministic":
570 case "/runtimemetadataversion":
573 case "/-getresourcestrings":
577 Console
.WriteLine ("Failing with : {0}", arg
);
581 static string [] LoadArgs (string file
)
584 var args
= new List
<string> ();
587 f
= new StreamReader (file
);
592 StringBuilder sb
= new StringBuilder ();
594 while ((line
= f
.ReadLine ()) != null) {
597 for (int i
= 0; i
< t
; i
++) {
600 if (c
== '"' || c
== '\'') {
603 for (i
++; i
< t
; i
++) {
610 } else if (c
== ' ') {
612 args
.Add (sb
.ToString ());
619 args
.Add (sb
.ToString ());
624 string [] ret_value
= new string [args
.Count
];
625 args
.CopyTo (ret_value
, 0);
630 static string Load (string f
)
632 var native
= NativeName (f
);
634 if (File
.Exists (native
)) {
635 using (var sr
= new StreamReader (native
)) {
636 return sr
.ReadToEnd ();
642 public static string NativeName (string path
)
644 if (System
.IO
.Path
.DirectorySeparatorChar
== '/')
645 return path
.Replace ("\\", "/");
647 return path
.Replace ("/", "\\");
650 public class VsCsproj
{
651 public string projectGuid
;
652 public string output
;
653 public string library_output
;
654 public string csProjFilename
;
655 public double fx_version
;
656 public List
<VsCsproj
> projReferences
= new List
<VsCsproj
> ();
657 public string library
;
658 public MsbuildGenerator MsbuildGenerator
;
661 public VsCsproj Csproj
;
663 void AppendResource (StringBuilder resources
, string source
, string logical
)
665 resources
.AppendFormat (" <EmbeddedResource Include=\"{0}\">" + NewLine
, source
);
666 resources
.AppendFormat (" <LogicalName>{0}</LogicalName>" + NewLine
, logical
);
667 resources
.AppendFormat (" </EmbeddedResource>" + NewLine
);
670 public VsCsproj
Generate (string library_output
, Dictionary
<string,MsbuildGenerator
> projects
, bool showWarnings
= false)
672 var generatedProjFile
= NativeName (Csproj
.csProjFilename
);
673 //Console.WriteLine ("Generating: {0}", generatedProjFile);
675 string boot
, flags
, output_name
, built_sources
, response
, profile
, reskey
;
677 boot
= xproject
.Element ("boot").Value
;
678 flags
= xproject
.Element ("flags").Value
;
679 output_name
= xproject
.Element ("output").Value
;
680 if (output_name
.EndsWith (".exe"))
682 built_sources
= xproject
.Element ("built_sources").Value
;
683 response
= xproject
.Element ("response").Value
;
684 reskey
= xproject
.Element ("resources").Value
;
686 profile
= xproject
.Element ("profile").Value
;
687 if (string.IsNullOrEmpty (response
)) {
688 // Address the issue where entries are missing the fx_version
689 // Should be fixed in the Makefile or elsewhere; this is a workaround
690 //<fx_version>basic</fx_version>
691 //<profile>./../build/deps/mcs.exe.sources.response</profile>
692 //<response></response>
694 profile
= fx_version
;
695 if (response
.Contains ("build") || response
.Contains ("basic") || response
.Contains (profile_2_0
)) {
697 if (response
.Contains (profile_2_0
)) profile
= "net_2_0";
698 } if (response
.Contains ("build") || response
.Contains ("basic") || response
.Contains (profile_2_0
)) {
700 } else if (response
.Contains (profile_3_5
)) {
703 } else if (response
.Contains (profile_4_0
)) {
706 } else if (response
.Contains (profile_4_x
)) {
712 // Prebuild code, might be in inputs, check:
713 // inputs/LIBRARY.pre
715 string prebuild
= GenerateStep (library
, ".pre", "PreBuildEvent");
716 string postbuild
= GenerateStep (library
, ".post", "PostBuildEvent");
718 var all_args
= new Queue
<string []> ();
719 all_args
.Enqueue (flags
.Split ());
720 while (all_args
.Count
> 0) {
721 string [] f
= all_args
.Dequeue ();
723 for (int i
= 0; i
< f
.Length
; i
++) {
724 if (f
[i
].Length
> 0 && f
[i
][0] == '-')
725 f
[i
] = "/" + f
[i
].Substring (1);
727 if (f
[i
] [0] == '@') {
728 string [] extra_args
;
729 string response_file
= f
[i
].Substring (1);
731 var resp_file_full
= Path
.Combine (base_dir
, response_file
);
732 extra_args
= LoadArgs (resp_file_full
);
733 if (extra_args
== null) {
734 Console
.WriteLine ($"{library_output}: Unable to open response file: {resp_file_full}");
735 Environment
.Exit (1);
738 all_args
.Enqueue (extra_args
);
742 if (CSCParseOption (f
[i
], ref f
))
744 Console
.WriteLine ("{library_output}: Failure with {0}", f
[i
]);
745 Environment
.Exit (1);
749 string [] source_files
;
750 using (var reader
= new StreamReader (NativeName (base_dir
+ "\\" + response
))) {
751 source_files
= reader
.ReadToEnd ().Split ();
754 Array
.Sort (source_files
);
756 StringBuilder sources
= new StringBuilder ();
757 foreach (string s
in source_files
) {
761 string src
= s
.Replace ("/", "\\");
762 if (src
.StartsWith (@"Test\..\"))
763 src
= src
.Substring (8, src
.Length
- 8);
765 sources
.AppendFormat (" <Compile Include=\"{0}\" />" + NewLine
, src
);
768 source_files
= built_sources
.Split ();
769 Array
.Sort (source_files
);
771 foreach (string s
in source_files
) {
775 string src
= s
.Replace ("/", "\\");
776 if (src
.StartsWith (@"Test\..\"))
777 src
= src
.Substring (8, src
.Length
- 8);
779 sources
.AppendFormat (" <Compile Include=\"{0}\" />" + NewLine
, src
);
781 sources
.Remove (sources
.Length
- 1, 1);
783 //if (library == "corlib-build") // otherwise, does not compile on fx_version == 4.0
785 // references.Add("System.dll");
786 // references.Add("System.Xml.dll");
789 //if (library == "System.Core-build") // otherwise, slow compile. May be a transient need.
791 // this.ignore_warning.Add(1685);
792 // this.ignore_warning.Add(0436);
795 var refs
= new StringBuilder ();
797 bool is_test
= response
.Contains ("_test_");
799 // F:\src\mono\mcs\class\lib\net_2_0\nunit.framework.dll
800 // F:\src\mono\mcs\class\SomeProject\SomeProject_test_-net_2_0.csproj
801 var nunitLibPath
= string.Format (@"..\lib\{0}\nunit.framework.dll", profile
);
802 refs
.Append (string.Format (" <Reference Include=\"{0}\" />" + NewLine
, nunitLibPath
));
806 // Generate resource referenced from the command line
808 var resources
= new StringBuilder ();
809 if (embedded_resources
.Count
> 0) {
810 foreach (var dk
in embedded_resources
) {
812 if (source
.EndsWith (".resources"))
813 source
= source
.Replace (".resources", ".resx");
815 // try to find a pre-built resource, and use that instead of trying to build it
816 if (source
.EndsWith (".resx")) {
817 var probe_prebuilt
= Path
.Combine (base_dir
, source
.Replace (".resx", ".resources.prebuilt"));
818 if (File
.Exists (probe_prebuilt
)) {
820 source
= GetRelativePath (base_dir
+ "/", probe_prebuilt
);
823 AppendResource (resources
, source
, dk
.Value
);
827 // Generate resources that were part of the explicit <resource> node
829 if (reskey
!= null && reskey
!= ""){
830 var pairs
= reskey
.Split (' ', '\n', '\t');
831 foreach (var pair
in pairs
){
832 var p
= pair
.IndexOf (",");
834 Console
.Error
.WriteLine ($"Found a resource without a filename: {pairs} for {Csproj.csProjFilename}");
835 Environment
.Exit (1);
837 AppendResource (resources
, pair
.Substring (p
+1), pair
.Substring (0, p
) + ".resources");
840 if (resources
.Length
> 0){
841 resources
.Insert (0, " <ItemGroup>" + NewLine
);
842 resources
.AppendFormat (" </ItemGroup>" + NewLine
);
845 if (references
.Count
> 0 || reference_aliases
.Count
> 0) {
846 // -r:mscorlib.dll -r:System.dll
847 //<ProjectReference Include="..\corlib\corlib-basic.csproj">
848 // <Project>{155aef28-c81f-405d-9072-9d52780e3e70}</Project>
849 // <Name>corlib-basic</Name>
850 //</ProjectReference>
851 //<ProjectReference Include="..\System\System-basic.csproj">
852 // <Project>{2094e859-db2f-481f-9630-f89d31d9ed48}</Project>
853 // <Name>System-basic</Name>
854 //</ProjectReference>
855 var refdistinct
= references
.Distinct ();
856 foreach (string reference
in refdistinct
) {
858 var match
= GetMatchingCsproj (library_output
, reference
, projects
);
860 AddProjectReference (refs
, Csproj
, match
, reference
, null);
863 Console
.WriteLine ("{0}: Could not find a matching project reference for {1}", library
, Path
.GetFileName (reference
));
864 Console
.WriteLine (" --> Adding reference with hintpath instead");
866 refs
.Append (" <Reference Include=\"" + reference
+ "\">" + NewLine
);
867 refs
.Append (" <SpecificVersion>False</SpecificVersion>" + NewLine
);
868 refs
.Append (" <HintPath>" + reference
+ "</HintPath>" + NewLine
);
869 refs
.Append (" <Private>False</Private>" + NewLine
);
870 refs
.Append (" </Reference>" + NewLine
);
874 foreach (string r
in reference_aliases
) {
875 int index
= r
.IndexOf ('=');
876 string alias = r
.Substring (0, index
);
877 string assembly
= r
.Substring (index
+ 1);
878 var match
= GetMatchingCsproj (library_output
, assembly
, projects
, explicitPath
: true);
880 AddProjectReference (refs
, Csproj
, match
, r
, alias);
882 throw new NotSupportedException (string.Format ("From {0}, could not find a matching project reference for {1}", library
, r
));
883 refs
.Append (" <Reference Include=\"" + assembly
+ "\">" + NewLine
);
884 refs
.Append (" <SpecificVersion>False</SpecificVersion>" + NewLine
);
885 refs
.Append (" <HintPath>" + r
+ "</HintPath>" + NewLine
);
886 refs
.Append (" <Aliases>" + alias + "</Aliases>" + NewLine
);
887 refs
.Append (" </Reference>" + NewLine
);
894 // ../class/lib/build/tmp/System.Xml.dll [No longer possible, we should be removing this from order.xml]
895 // /class/lib/basic/System.Core.dll
896 // <library_output>mcs.exe</library_output>
897 string build_output_dir
;
898 if (LibraryOutput
.Contains ("/"))
899 build_output_dir
= Path
.GetDirectoryName (LibraryOutput
);
901 build_output_dir
= "bin\\Debug\\" + library
;
903 bool basic_or_build
= (library
.Contains ("-basic") || library
.Contains ("-build"));
906 // Replace the template values
909 string strongNameSection
= "";
910 if (StrongNameKeyFile
!= null){
911 strongNameSection
= String
.Format (
912 " <PropertyGroup>" + NewLine
+
913 " <SignAssembly>true</SignAssembly>" + NewLine
+
915 " </PropertyGroup>" + NewLine
+
916 " <PropertyGroup>" + NewLine
+
917 " <AssemblyOriginatorKeyFile>{0}</AssemblyOriginatorKeyFile>" + NewLine
+
918 " </PropertyGroup>", StrongNameKeyFile
, StrongNameDelaySign
? " <DelaySign>true</DelaySign>" + NewLine
: "");
920 Csproj
.output
= template
.
921 Replace ("@OUTPUTTYPE@", Target
== Target
.Library
? "Library" : "Exe").
922 Replace ("@SIGNATURE@", strongNameSection
).
923 Replace ("@PROJECTGUID@", Csproj
.projectGuid
).
924 Replace ("@DEFINES@", defines
.ToString ()).
925 Replace ("@DISABLEDWARNINGS@", string.Join (",", (from i
in ignore_warning
select i
.ToString ()).ToArray ())).
926 //Replace("@NOSTDLIB@", (basic_or_build || (!StdLib)) ? "<NoStdLib>true</NoStdLib>" : string.Empty).
927 Replace ("@NOSTDLIB@", "<NoStdLib>" + (!StdLib
).ToString () + "</NoStdLib>").
928 Replace ("@NOCONFIG@", "<NoConfig>" + (!load_default_config
).ToString () + "</NoConfig>").
929 Replace ("@ALLOWUNSAFE@", Unsafe
? "<AllowUnsafeBlocks>true</AllowUnsafeBlocks>" : "").
930 Replace ("@FX_VERSION", fx_version
).
931 Replace ("@ASSEMBLYNAME@", Path
.GetFileNameWithoutExtension (output_name
)).
932 Replace ("@OUTPUTDIR@", build_output_dir
).
933 Replace ("@OUTPUTSUFFIX@", Path
.GetFileName (build_output_dir
)).
934 Replace ("@DEFINECONSTANTS@", defines
.ToString ()).
935 Replace ("@DEBUG@", want_debugging_support
? "true" : "false").
936 Replace ("@DEBUGTYPE@", want_debugging_support
? "full" : "pdbonly").
937 Replace ("@REFERENCES@", refs
.ToString ()).
938 Replace ("@PREBUILD@", prebuild
).
939 Replace ("@POSTBUILD@", postbuild
).
940 Replace ("@STARTUPOBJECT@", main
== null ? "" : $"<StartupObject>{main}</StartupObject>").
941 //Replace ("@ADDITIONALLIBPATHS@", String.Format ("<AdditionalLibPaths>{0}</AdditionalLibPaths>", string.Join (",", libs.ToArray ()))).
942 Replace ("@ADDITIONALLIBPATHS@", String
.Empty
).
943 Replace ("@RESOURCES@", resources
.ToString ()).
944 Replace ("@OPTIMIZE@", Optimize
? "true" : "false").
945 Replace ("@SOURCES@", sources
.ToString ());
947 //Console.WriteLine ("Generated {0}", ofile.Replace ("\\", "/"));
948 using (var o
= new StreamWriter (generatedProjFile
)) {
949 o
.WriteLine (Csproj
.output
);
955 string GenerateStep (string library
, string suffix
, string eventKey
)
957 string target
= Load (library
+ suffix
);
958 string target_windows
, target_unix
;
960 int q
= library
.IndexOf ("-");
962 target
= target
+ Load (library
.Substring (0, q
) + suffix
);
964 if (target
.IndexOf ("@MONO@") != -1){
965 target_unix
= target
.Replace ("@MONO@", "mono").Replace ("@CAT@", "cat");
966 target_windows
= target
.Replace ("@MONO@", "").Replace ("@CAT@", "type");
968 target_unix
= target
.Replace ("jay.exe", "jay");
969 target_windows
= target
;
971 target_unix
= target_unix
.Replace ("@COPY@", "cp");
972 target_windows
= target_unix
.Replace ("@COPY@", "copy");
974 target_unix
= target_unix
.Replace ("\r", "");
975 const string condition_unix
= "Condition=\" '$(OS)' != 'Windows_NT' \"";
976 const string condition_windows
= "Condition=\" '$(OS)' == 'Windows_NT' \"";
978 $" <{eventKey} {condition_unix}>\n{target_unix}\n </{eventKey}>{NewLine}" +
979 $" <{eventKey} {condition_windows}>{NewLine}{target_windows}{NewLine} </{eventKey}>";
983 void AddProjectReference (StringBuilder refs
, VsCsproj result
, MsbuildGenerator match
, string r
, string alias)
985 refs
.AppendFormat (" <ProjectReference Include=\"{0}\">{1}", GetRelativePath (result
.csProjFilename
, match
.CsprojFilename
), NewLine
);
986 refs
.Append (" <Project>" + match
.projectGuid
+ "</Project>" + NewLine
);
987 refs
.Append (" <Name>" + Path
.GetFileNameWithoutExtension (match
.CsprojFilename
.Replace ('\\', Path
.DirectorySeparatorChar
)) + "</Name>" + NewLine
);
989 refs
.Append (" <Aliases>" + alias + "</Aliases>");
990 refs
.Append (" </ProjectReference>" + NewLine
);
991 if (!result
.projReferences
.Contains (match
.Csproj
))
992 result
.projReferences
.Add (match
.Csproj
);
995 public static string GetRelativePath (string from, string to
)
997 from = from.Replace ("\\", "/");
998 to
= to
.Replace ("\\", "/");
999 var fromUri
= new Uri (Path
.GetFullPath (from));
1000 var toUri
= new Uri (Path
.GetFullPath (to
));
1002 var ret
= fromUri
.MakeRelativeUri (toUri
).ToString ().Replace ("%5C", "\x5c");
1006 MsbuildGenerator
GetMatchingCsproj (string library_output
, string dllReferenceName
, Dictionary
<string,MsbuildGenerator
> projects
, bool explicitPath
= false)
1008 // libDir would be "./../../class/lib/net_4_x for example
1010 if (!dllReferenceName
.EndsWith (".dll") && !dllReferenceName
.EndsWith (".exe"))
1011 dllReferenceName
+= ".dll";
1013 var probe
= Path
.GetFullPath (Path
.Combine (base_dir
, dllReferenceName
));
1014 foreach (var project
in projects
){
1015 if (probe
== project
.Value
.AbsoluteLibraryOutput
)
1016 return project
.Value
;
1019 // not explicit, search for the library in the lib path order specified
1021 foreach (var libDir
in libs
) {
1022 var abs
= Path
.GetFullPath (Path
.Combine (base_dir
, libDir
));
1023 foreach (var project
in projects
){
1024 probe
= Path
.Combine (abs
, dllReferenceName
);
1026 if (probe
== project
.Value
.AbsoluteLibraryOutput
)
1027 return project
.Value
;
1031 // Last attempt, try to find the library in all the projects
1032 foreach (var project
in projects
) {
1033 if (project
.Value
.AbsoluteLibraryOutput
.EndsWith (dllReferenceName
))
1034 return project
.Value
;
1037 var ljoined
= String
.Join (", ", libs
);
1038 Console
.WriteLine ($"{library_output}: did not find referenced {dllReferenceName} with libs={ljoined}");
1039 foreach (var p
in projects
) {
1040 Console
.WriteLine ("{0}", p
.Value
.AbsoluteLibraryOutput
);
1047 public class Driver
{
1049 static IEnumerable
<XElement
> GetProjects (bool full
= false)
1051 XDocument doc
= XDocument
.Load ("order.xml");
1052 foreach (XElement project
in doc
.Root
.Elements ()) {
1053 string dir
= project
.Attribute ("dir").Value
;
1054 string library
= project
.Attribute ("library").Value
;
1055 var profile
= project
.Element ("profile").Value
;
1058 // Skip facades for now, the tool doesn't know how to deal with them yet.
1059 if (dir
.Contains ("Facades"))
1062 // These are currently broken, skip until they're fixed.
1063 if (dir
.StartsWith ("mcs") || dir
.Contains ("apigen"))
1067 // Do only class libraries for now
1069 if (!(dir
.StartsWith ("class") || dir
.StartsWith ("mcs") || dir
.StartsWith ("basic")))
1073 if (!library
.Contains ("tests"))
1074 yield return project
;
1079 // Do not do 2.1, it is not working yet
1080 // Do not do basic, as there is no point (requires a system mcs to be installed).
1082 if (library
.Contains ("moonlight") || library
.Contains ("-basic") || library
.EndsWith ("bootstrap") || library
.Contains ("build"))
1085 // The next ones are to make debugging easier for now
1086 if (profile
== "basic")
1089 // For now -- problem is, our resolver currently only considers the assembly name, and we ahve
1090 // conflicing 2.0 and 2.4 versions so for now, we just skip the nunit20 versions
1091 if (dir
.Contains ("nunit20"))
1095 if (profile
!= "net_4_x" || library
.Contains ("tests"))
1098 //Console.WriteLine ("Going to handle {0}", library);
1099 yield return project
;
1103 static void Main (string [] args
)
1105 if (!File
.Exists ("genproj.cs")) {
1106 Console
.WriteLine ("This command must be executed from mono/msvc/scripts");
1107 Environment
.Exit (1);
1110 if (args
.Length
== 1 && args
[0].ToLower ().Contains ("-h")) {
1111 Console
.WriteLine ("Usage:");
1112 Console
.WriteLine ("genproj.exe [visual_studio_release] [output_full_solutions]");
1113 Console
.WriteLine ("If output_full_solutions is false, only the main System*.dll");
1114 Console
.WriteLine (" assemblies (and dependencies) is included in the solution.");
1115 Console
.WriteLine ("Example:");
1116 Console
.WriteLine ("genproj.exe 2012 false");
1117 Console
.WriteLine ("genproj.exe with no arguments is equivalent to 'genproj.exe 2012 true'\n\n");
1118 Console
.WriteLine ("genproj.exe deps");
1119 Console
.WriteLine ("Generates a Makefile dependency file from the projects input");
1120 Environment
.Exit (0);
1123 var slnVersion
= (args
.Length
> 0) ? args
[0] : "2012";
1124 bool fullSolutions
= (args
.Length
> 1) ? bool.Parse (args
[1]) : true;
1126 // To generate makefile depenedencies
1127 var makefileDeps
= (args
.Length
> 0 && args
[0] == "deps");
1129 var sln_gen
= new SlnGenerator (slnVersion
);
1130 var four_five_sln_gen
= new SlnGenerator (slnVersion
);
1131 var projects
= new Dictionary
<string,MsbuildGenerator
> ();
1133 var duplicates
= new List
<string> ();
1134 foreach (var project
in GetProjects (makefileDeps
)) {
1135 var library_output
= project
.Element ("library_output").Value
;
1136 projects
[library_output
] = new MsbuildGenerator (project
);
1138 foreach (var project
in GetProjects (makefileDeps
)){
1139 var library_output
= project
.Element ("library_output").Value
;
1140 //Console.WriteLine ("=== {0} ===", library_output);
1141 var gen
= projects
[library_output
];
1143 var csproj
= gen
.Generate (library_output
, projects
);
1144 var csprojFilename
= csproj
.csProjFilename
;
1145 if (!sln_gen
.ContainsProjectIdentifier (csproj
.library
)) {
1146 sln_gen
.Add (csproj
);
1148 duplicates
.Add (csprojFilename
);
1151 } catch (Exception e
) {
1152 Console
.WriteLine ("Error in {0}\n{1}", project
, e
);
1156 Func
<MsbuildGenerator
.VsCsproj
, bool> additionalFilter
;
1157 additionalFilter
= fullSolutions
? (Func
<MsbuildGenerator
.VsCsproj
, bool>)null : IsCommonLibrary
;
1159 FillSolution (four_five_sln_gen
, MsbuildGenerator
.profile_4_x
, projects
.Values
, additionalFilter
);
1161 if (duplicates
.Count () > 0) {
1162 var sb
= new StringBuilder ();
1163 sb
.AppendLine ("WARNING: Skipped some project references, apparent duplicates in order.xml:");
1164 foreach (var item
in duplicates
) {
1165 sb
.AppendLine (item
);
1167 Console
.WriteLine (sb
.ToString ());
1170 WriteSolution (four_five_sln_gen
, Path
.Combine ("..", "..", MakeSolutionName (MsbuildGenerator
.profile_4_x
)));
1173 const string classDirPrefix
= "./../../";
1174 Console
.WriteLine ("here {0}", sln_gen
.libraries
.Count
);
1175 foreach (var p
in sln_gen
.libraries
){
1176 string rebasedOutput
= RebaseToClassDirectory (MsbuildGenerator
.GetRelativePath ("../../mcs/class", p
.library_output
));
1178 Console
.Write ("{0}: ", rebasedOutput
);
1179 foreach (var r
in p
.projReferences
){
1180 var lo
= r
.library_output
;
1181 if (lo
.StartsWith (classDirPrefix
))
1182 lo
= lo
.Substring (classDirPrefix
.Length
);
1184 lo
= "<<ERROR-dependency is not a class library>>";
1185 Console
.Write ("{0} ", lo
);
1187 Console
.Write ("\n\t(cd {0}; make {1})", p
.MsbuildGenerator
.dir
, p
.library_output
);
1188 Console
.WriteLine ("\n");
1192 // A few other optional solutions
1193 // Solutions with 'everything' and the most common libraries used in development may be of interest
1194 //WriteSolution (sln_gen, "./mcs_full.sln");
1195 //WriteSolution (small_full_sln_gen, "small_full.sln");
1196 // The following may be useful if lacking visual studio or MonoDevelop, to bootstrap mono compiler self-hosting
1197 //WriteSolution (basic_sln_gen, "mcs_basic.sln");
1198 //WriteSolution (build_sln_gen, "mcs_build.sln");
1201 // Rebases a path, assuming that execution is taking place in the "class" subdirectory,
1202 // so it strips ../class/ from a path, which is a no-op
1203 static string RebaseToClassDirectory (string path
)
1205 const string prefix
= "../class/";
1206 int p
= path
.IndexOf (prefix
);
1209 return path
.Substring (0, p
) + path
.Substring (p
+prefix
.Length
);
1213 static string MakeSolutionName (string profileTag
)
1215 return "net" + profileTag
+ ".sln";
1218 static void FillSolution (SlnGenerator solution
, string profileString
, IEnumerable
<MsbuildGenerator
> projects
, Func
<MsbuildGenerator
.VsCsproj
, bool> additionalFilter
= null)
1220 foreach (var generator
in projects
) {
1221 var vsCsproj
= generator
.Csproj
;
1222 if (!vsCsproj
.library
.Contains (profileString
))
1224 if (additionalFilter
!= null && !additionalFilter (vsCsproj
))
1226 var csprojFilename
= vsCsproj
.csProjFilename
;
1227 if (!solution
.ContainsProjectIdentifier (vsCsproj
.library
)) {
1228 solution
.Add (vsCsproj
);
1229 RecursiveAddProj (solution
, vsCsproj
);
1234 static void RecursiveAddProj (SlnGenerator solution
, MsbuildGenerator
.VsCsproj vsCsproj
, int recursiveDepth
= 1)
1236 const int max_recursive
= 16;
1237 if (recursiveDepth
> max_recursive
) throw new Exception (string.Format ("Reached {0} levels of project dependency", max_recursive
));
1238 foreach (var projRef
in vsCsproj
.projReferences
) {
1239 if (!solution
.ContainsProjectIdentifier (projRef
.library
)) {
1240 solution
.Add (projRef
);
1241 RecursiveAddProj (solution
, projRef
, recursiveDepth
+ 1);
1246 static void WriteSolution (SlnGenerator sln_gen
, string slnfilename
)
1248 Console
.WriteLine (String
.Format ("Writing solution {1}, with {0} projects", sln_gen
.Count
, slnfilename
));
1249 sln_gen
.Write (slnfilename
);
1252 static bool IsCommonLibrary (MsbuildGenerator
.VsCsproj proj
)
1254 var library
= proj
.library
;
1255 //if (library.Contains ("-basic"))
1257 //if (library.Contains ("-build"))
1259 //if (library.StartsWith ("corlib"))
1261 if (library
.StartsWith ("System-"))
1263 if (library
.StartsWith ("System.Xml"))
1265 if (library
.StartsWith ("System.Secu"))
1267 if (library
.StartsWith ("System.Configuration"))
1269 if (library
.StartsWith ("System.Core"))
1271 //if (library.StartsWith ("Mono."))