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 static class KnownProject
{
29 public static readonly KnownProjectInfo
30 Genconsts
= new KnownProjectInfo
{
32 Path
= @"$(SolutionDir)\msvc\scripts\genconsts.csproj",
33 Guid
= "{702AE2C0-71DD-4112-9A06-E4FABCA59986}"
35 Stringreplacer
= new KnownProjectInfo
{
36 Name
= "cil-stringreplacer",
37 Path
= @"$(SolutionDir)\mcs\tools\cil-stringreplacer\cil-stringreplacer.csproj",
38 Guid
= "{53c50ffa-8b39-4c70-8ba8-caa70c41a47b}"
40 Jay
= new KnownProjectInfo
{
42 Path
= @"$(SolutionDir)\mcs\jay\jay.vcxproj",
43 Guid
= "{5d485d32-3b9f-4287-ab24-c8da5b89f537}"
45 Culevel
= new KnownProjectInfo
{
47 Path
= @"$(SolutionDir)\mcs\tools\culevel\culevel.csproj",
48 Guid
= "{E8E246BD-CD0C-4734-A3C2-7F44796EC47B}"
52 public class KnownProjectInfo
{
58 public class SlnGenerator
{
59 public static readonly string NewLine
= "\r\n"; //Environment.NewLine; // "\n";
60 public SlnGenerator (string slnVersion
)
62 Console
.Error
.WriteLine("// Requested sln version is {0}", slnVersion
);
63 this.header
= MakeHeader ("12.00", "15", "15.0.0.0");
66 const string project_start
= "Project(\"{0}\") = \"{1}\", \"{2}\", \"{3}\""; // Note: No need to double up on {} around {2}
67 const string project_end
= "EndProject";
69 public static readonly List
<string> profiles
= new List
<string> {
82 public static readonly HashSet
<string> observedProfiles
= new HashSet
<string> {
86 public const string csproj_type_guid
= "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}";
87 public const string vcxproj_type_guid
= "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}";
89 public static Dictionary
<string, HashSet
<string>> profilesByGuid
= new Dictionary
<string, HashSet
<string>> ();
90 public List
<MsbuildGenerator
.VsCsproj
> libraries
= new List
<MsbuildGenerator
.VsCsproj
> ();
93 string MakeHeader (string formatVersion
, string yearTag
, string minimumVersion
)
95 return string.Format (
96 "Microsoft Visual Studio Solution File, Format Version {0}" + NewLine
+
97 "# Visual Studio {1}" + NewLine
+
98 "MinimumVisualStudioVersion = {2}",
99 formatVersion
, yearTag
,
104 public void Add (MsbuildGenerator
.VsCsproj vsproj
)
107 libraries
.Add (vsproj
);
108 } catch (Exception ex
) {
109 Console
.Error
.WriteLine ($"// Error while adding library: {ex.Message}");
113 private void WriteProjectReference (StreamWriter sln
, string prefixGuid
, string library
, string relativePath
, string projectGuid
, string[] dependencyGuids
)
116 library
= library
.Replace("-net_4_x", "");
117 sln
.WriteLine (project_start
, prefixGuid
, library
, relativePath
, projectGuid
);
119 if (dependencyGuids
!= null && dependencyGuids
.Length
> 0) {
120 sln
.WriteLine ("\tProjectSection(ProjectDependencies) = postProject");
121 foreach (var guid
in dependencyGuids
)
122 sln
.WriteLine ("\t\t{0} = {0}", guid
);
123 sln
.WriteLine ("\tEndProjectSection");
126 sln
.WriteLine (project_end
);
129 private void WriteProjectReference (StreamWriter sln
, string slnFullPath
, MsbuildGenerator
.VsCsproj proj
)
131 var unixProjFile
= proj
.csProjFilename
.Replace ("\\", "/");
132 var fullProjPath
= Path
.GetFullPath (unixProjFile
).Replace ("\\", "/");
133 var relativePath
= MsbuildGenerator
.GetRelativePath (slnFullPath
, fullProjPath
);
134 string[] dependencyGuids
= null;
136 WriteProjectReference(sln
, csproj_type_guid
, proj
.library
, relativePath
, proj
.projectGuid
, dependencyGuids
);
139 private void WriteProjectConfigurationPlatforms (StreamWriter sln
, string guid
, string defaultPlatform
, bool forceBuild
)
141 var fallbackProfileNames
= new List
<string> ();
142 var didBuildAnyProfile
= false;
144 foreach (var profile
in profiles
) {
145 if (!observedProfiles
.Contains (profile
) && !forceBuild
)
148 var platformToBuild
= profile
;
149 var isBuildEnabled
= true;
151 HashSet
<string> projectProfiles
;
153 !profilesByGuid
.TryGetValue (guid
, out projectProfiles
) ||
154 !projectProfiles
.Contains (platformToBuild
)
156 fallbackProfileNames
.Add (platformToBuild
);
157 platformToBuild
= defaultPlatform
;
158 isBuildEnabled
= forceBuild
;
162 didBuildAnyProfile
= true;
164 sln
.WriteLine ("\t\t{0}.Debug|{1}.ActiveCfg = Debug|{2}", guid
, profile
, platformToBuild
);
166 sln
.WriteLine ("\t\t{0}.Debug|{1}.Build.0 = Debug|{2}", guid
, profile
, platformToBuild
);
167 sln
.WriteLine ("\t\t{0}.Release|{1}.ActiveCfg = Release|{2}", guid
, profile
, platformToBuild
);
169 sln
.WriteLine ("\t\t{0}.Release|{1}.Build.0 = Release|{2}", guid
, profile
, platformToBuild
);
172 if (!didBuildAnyProfile
)
173 Console
.Error
.WriteLine($"// Project {guid} not set to build in any profile");
175 if (fallbackProfileNames
.Count
> 0)
176 Console
.Error
.WriteLine ($"// Project {guid} does not have profile(s) {string.Join(", ", fallbackProfileNames)} so using {defaultPlatform}");
179 public void Write (string filename
)
181 var fullPath
= Path
.GetDirectoryName (filename
) + "/";
183 using (var sln
= new StreamWriter (filename
)) {
185 sln
.WriteLine (header
);
187 // Manually insert jay's vcxproj. We depend on jay.exe to perform build steps later.
188 WriteProjectReference (sln
, vcxproj_type_guid
, "jay", "mcs/jay/jay.vcxproj", KnownProject
.Jay
.Guid
, null);
190 // Manually insert genconsts. This is used to generate Consts.cs.
191 WriteProjectReference (sln
, csproj_type_guid
, "genconsts", "msvc/scripts/genconsts.csproj", KnownProject
.Genconsts
.Guid
, null);
193 // Manually insert cil-stringreplacer. We can't trivially do this through the order.xml flow and it has a custom csproj.
194 WriteProjectReference (sln
, csproj_type_guid
, "cil-stringreplacer", "mcs/tools/cil-stringreplacer/cil-stringreplacer.csproj", KnownProject
.Stringreplacer
.Guid
, null);
196 foreach (var proj
in libraries
) {
197 WriteProjectReference (sln
, fullPath
, proj
);
200 sln
.WriteLine ("Global");
202 sln
.WriteLine ("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution");
203 foreach (var profile
in profiles
) {
204 if (!observedProfiles
.Contains (profile
))
207 sln
.WriteLine ("\t\tDebug|{0} = Debug|{0}", profile
, profile
);
208 sln
.WriteLine ("\t\tRelease|{0} = Release|{0}", profile
, profile
);
210 sln
.WriteLine ("\tEndGlobalSection");
212 sln
.WriteLine ("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution");
214 // Manually insert configurations for the special projects that always build
215 WriteProjectConfigurationPlatforms (sln
, KnownProject
.Jay
.Guid
, "Win32", true);
216 WriteProjectConfigurationPlatforms (sln
, KnownProject
.Genconsts
.Guid
, "x86", true);
217 WriteProjectConfigurationPlatforms (sln
, KnownProject
.Stringreplacer
.Guid
, "AnyCPU", true);
219 foreach (var proj
in libraries
) {
220 WriteProjectConfigurationPlatforms (sln
, proj
.projectGuid
, "net_4_x", false);
223 sln
.WriteLine ("\tEndGlobalSection");
225 sln
.WriteLine ("\tGlobalSection(SolutionProperties) = preSolution");
226 sln
.WriteLine ("\t\tHideSolutionNode = FALSE");
227 sln
.WriteLine ("\tEndGlobalSection");
229 sln
.WriteLine ("EndGlobal");
233 internal bool ContainsProjectIdentifier (string projId
)
235 return libraries
.FindIndex (x
=> (x
.library
== projId
)) >= 0;
238 public int Count { get { return libraries.Count; }
}
241 public class MsbuildGenerator
{
242 static readonly string NewLine
= SlnGenerator
.NewLine
;
243 static XmlNamespaceManager xmlns
;
245 public const string profile_2_0
= "_2_0";
246 public const string profile_3_5
= "_3_5";
247 public const string profile_4_0
= "_4_0";
248 public const string profile_4_x
= "_4_x";
250 public static readonly (string, string)[] fixed_guids
= new [] {
251 ("tools/culevel/culevel.csproj", KnownProject
.Culevel
.Guid
)
256 Console
.Error
.WriteLine ("// Invalid argument");
259 static string template
;
260 static MsbuildGenerator ()
262 using (var input
= new StreamReader ("csproj.tmpl")) {
263 template
= input
.ReadToEnd ();
266 xmlns
= new XmlNamespaceManager (new NameTable ());
267 xmlns
.AddNamespace ("x", "http://schemas.microsoft.com/developer/msbuild/2003");
270 // The directory as specified in order.xml
277 public string CsprojFilename
;
280 // Our base directory, this is relative to our exectution point mono/msvc/scripts
284 static readonly Dictionary
<string, string> GuidForCsprojCache
= new Dictionary
<string, string> ();
286 public string LibraryOutput
, AbsoluteLibraryOutput
;
288 public MsbuildGenerator (XElement xproject
)
290 this.xproject
= xproject
;
291 dir
= xproject
.Attribute ("dir").Value
;
292 library
= xproject
.Attribute ("library").Value
;
294 var profileIndex
= library
.LastIndexOf("-");
295 var libraryWithoutProfile
= library
.Substring(0, profileIndex
);
296 CsprojFilename
= "..\\..\\mcs\\" + dir
+ "\\" + libraryWithoutProfile
+ ".csproj";
297 LibraryOutput
= xproject
.Element ("library_output").Value
;
299 projectGuid
= LookupOrGenerateGuid ();
300 fx_version
= xproject
.Element ("fx_version").Value
;
301 Csproj
= new VsCsproj () {
302 csProjFilename
= this.CsprojFilename
,
303 projectGuid
= this.projectGuid
,
304 library_output
= this.LibraryOutput
,
305 fx_version
= double.Parse (fx_version
),
306 library
= this.library
,
307 MsbuildGenerator
= this
312 class_dir
= "../class/";
313 base_dir
= "../../mcs/mcs";
317 foreach (char c
in dir
) {
319 mcs_topdir
= "..//" + mcs_topdir
;
321 class_dir
= mcs_topdir
.Substring (3);
323 base_dir
= Path
.Combine ("..", "..", "mcs", dir
);
325 AbsoluteLibraryOutput
= Path
.GetFullPath (Path
.Combine (base_dir
, LibraryOutput
));
328 string LookupOrGenerateGuid ()
330 var projectFile
= NativeName (CsprojFilename
);
331 string guidKey
= Path
.GetFullPath (projectFile
);
333 foreach (var fg
in fixed_guids
) {
334 if (guidKey
.EndsWith (fg
.Item1
)) {
335 Console
.WriteLine($"Using fixed guid {fg.Item2} for {fg.Item1}");
341 GuidForCsprojCache
.TryGetValue (projectFile
, out result
);
343 if (String
.IsNullOrEmpty(result
) && File
.Exists (projectFile
)){
345 var doc
= XDocument
.Load (projectFile
);
346 result
= doc
.XPathSelectElement ("x:Project/x:PropertyGroup/x:ProjectGuid", xmlns
).Value
;
347 } catch (Exception exc
) {
348 Console
.Error
.WriteLine($"// Failed to parse guid from {projectFile}: {exc.Message}");
352 if (String
.IsNullOrEmpty(result
))
353 result
= "{" + Guid.NewGuid ().ToString ().ToUpper () + "}";
355 GuidForCsprojCache
[projectFile
] = result
;
361 StringBuilder defines
= new StringBuilder ();
362 bool Optimize
= true;
363 bool want_debugging_support
= false;
365 SortedDictionary
<string, string> embedded_resources
= new SortedDictionary
<string, string> ();
366 List
<string> warning_as_error
= new List
<string> ();
367 List
<int> ignore_warning
= new List
<int> ();
368 bool load_default_config
= true;
370 List
<string> references
= new List
<string> ();
371 List
<string> libs
= new List
<string> ();
372 List
<string> reference_aliases
= new List
<string> ();
373 bool showWarnings
= true;
376 #pragma warning disable 0219, 0414
377 int WarningLevel
= 4;
379 bool Checked
= false;
380 bool WarningsAreErrors
;
381 bool VerifyClsCompliance
= true;
382 string win32IconFile
;
383 string StrongNameKeyFile
;
384 bool copyLocal
= true;
385 Target Target
= Target
.Library
;
386 string TargetExt
= ".exe";
388 string StrongNameKeyContainer
;
389 bool StrongNameDelaySign
= false;
390 string LangVersion
= "default";
393 // Class directory, relative to
395 #pragma warning restore 0219,414
397 readonly char [] argument_value_separator
= new char [] { ';', ',' }
;
400 // This parses the -arg and /arg options to the compiler, even if the strings
401 // in the following text use "/arg" on the strings.
403 bool CSCParseOption (string option
, ref string [] args
)
405 int idx
= option
.IndexOf (':');
412 arg
= option
.Substring (0, idx
);
414 value = option
.Substring (idx
+ 1);
417 switch (arg
.ToLower (CultureInfo
.InvariantCulture
)) {
429 Target
= Target
.WinExe
;
433 Target
= Target
.Library
;
438 Target
= Target
.Module
;
439 TargetExt
= ".netmodule";
448 if (value.Length
== 0) {
450 Environment
.Exit (1);
468 case "/incremental+":
469 case "/incremental-":
475 if (value.Length
== 0) {
477 Environment
.Exit (1);
480 foreach (string d
in value.Split (argument_value_separator
)) {
481 if (defines
.Length
!= 0)
482 defines
.Append (";");
491 // We should collect data, runtime, etc and store in the file specified
495 case "/linkresource":
498 bool embeded
= arg
[1] == 'r' || arg
[1] == 'R';
499 string [] s
= value.Split (argument_value_separator
);
502 if (s
[0].Length
== 0)
504 embedded_resources
[s
[0]] = Path
.GetFileName (s
[0]);
507 embedded_resources
[s
[0]] = s
[1];
510 Console
.Error
.WriteLine ("// Does not support this method yet: {0}", arg
);
511 Environment
.Exit (1);
514 Console
.Error
.WriteLine ("// Wrong number of arguments for option `{0}'", option
);
515 Environment
.Exit (1);
522 Console
.Error
.WriteLine ("// /recurse not supported");
523 Environment
.Exit (1);
528 if (value.Length
== 0) {
529 Console
.Error
.WriteLine ("// /reference requires an argument");
530 Environment
.Exit (1);
533 string [] refs
= value.Split (argument_value_separator
);
534 foreach (string r
in refs
) {
536 int index
= val
.IndexOf ('=');
538 reference_aliases
.Add (r
);
543 references
.Add (val
);
556 Console
.Error
.WriteLine ("// {0} = not supported", arg
);
564 win32IconFile
= value;
568 want_debugging_support
= false;
573 want_debugging_support
= true;
590 VerifyClsCompliance
= false;
603 case "/warnaserror+":
604 if (value.Length
== 0) {
605 WarningsAreErrors
= true;
607 foreach (string wid
in value.Split (argument_value_separator
))
608 warning_as_error
.Add (wid
);
613 // Console.WriteLine ("Warning ignoring /runtime:v4");
616 case "/warnaserror-":
617 if (value.Length
== 0) {
618 WarningsAreErrors
= false;
620 foreach (string wid
in value.Split (argument_value_separator
))
621 warning_as_error
.Remove (wid
);
626 WarningLevel
= Int32
.Parse (value);
632 if (value.Length
== 0) {
633 Console
.Error
.WriteLine ("// /nowarn requires an argument");
634 Environment
.Exit (1);
637 warns
= value.Split (argument_value_separator
);
638 foreach (string wc
in warns
) {
640 if (wc
.Trim ().Length
== 0)
643 int warn
= Int32
.Parse (wc
);
645 throw new ArgumentOutOfRangeException ("warn");
647 ignore_warning
.Add (warn
);
649 Console
.Error
.WriteLine ($"// `{wc}' is not a valid warning number");
650 Environment
.Exit (1);
657 load_default_config
= false;
673 if (value == String
.Empty
) {
674 Console
.Error
.WriteLine ($"// {arg} requires an argument");
675 Environment
.Exit (1);
677 StrongNameKeyFile
= value;
679 case "/keycontainer":
680 if (value == String
.Empty
) {
681 Console
.Error
.WriteLine ($"// {arg} requires an argument");
682 Environment
.Exit (1);
684 StrongNameKeyContainer
= value;
688 StrongNameDelaySign
= true;
691 StrongNameDelaySign
= false;
705 case "/deterministic":
708 case "/runtimemetadataversion":
711 case "/-getresourcestrings":
718 Console
.Error
.WriteLine ($"// Failing with : {arg}");
722 static string [] LoadArgs (string file
)
725 var args
= new List
<string> ();
728 f
= new StreamReader (file
);
733 StringBuilder sb
= new StringBuilder ();
735 while ((line
= f
.ReadLine ()) != null) {
738 for (int i
= 0; i
< t
; i
++) {
741 if (c
== '"' || c
== '\'') {
744 for (i
++; i
< t
; i
++) {
751 } else if (c
== ' ') {
753 args
.Add (sb
.ToString ());
760 args
.Add (sb
.ToString ());
765 string [] ret_value
= new string [args
.Count
];
766 args
.CopyTo (ret_value
, 0);
771 static string Load (string f
)
773 var native
= NativeName (f
);
775 if (File
.Exists (native
)) {
776 using (var sr
= new StreamReader (native
)) {
777 return sr
.ReadToEnd ();
783 public static string NativeName (string path
)
785 if (System
.IO
.Path
.DirectorySeparatorChar
== '/')
786 return path
.Replace ("\\", "/");
788 return path
.Replace ("/", "\\");
791 public class VsCsproj
{
792 public string projectGuid
;
793 public string output
;
794 public string library_output
;
795 public string csProjFilename
;
796 public double fx_version
;
797 public List
<VsCsproj
> projReferences
= new List
<VsCsproj
> ();
798 public string library
;
799 public MsbuildGenerator MsbuildGenerator
;
800 public string preBuildEvent
, postBuildEvent
;
803 public VsCsproj Csproj
;
805 void AppendResource (StringBuilder resources
, string source
, string logical
)
807 source
= NativeName (source
);
808 resources
.AppendFormat (" <EmbeddedResource Include=\"{0}\">" + NewLine
, source
);
809 resources
.AppendFormat (" <LogicalName>{0}</LogicalName>" + NewLine
, logical
);
810 resources
.AppendFormat (" </EmbeddedResource>" + NewLine
);
813 internal string GetProjectFilename ()
815 return NativeName (Csproj
.csProjFilename
);
818 public void EraseExisting ()
820 var generatedProjFile
= GetProjectFilename();
821 if (File
.Exists(generatedProjFile
))
822 File
.Delete(generatedProjFile
);
825 SourcesParser _SourcesParser
= null;
827 private SourcesParser
GetSourcesParser () {
828 if (_SourcesParser
!= null)
829 return _SourcesParser
;
831 var platformsFolder
= Path
.GetFullPath ("../../mcs/build/platforms");
832 var profilesFolder
= Path
.GetFullPath ("../../mcs/build/profiles");
834 SourcesParser
.TraceLevel
= 0;
835 return _SourcesParser
= new SourcesParser (platformsFolder
, profilesFolder
);
838 private ParseResult
ReadSources (string sourcesFileName
) {
839 var libraryDirectory
= Path
.GetDirectoryName (GetProjectFilename ());
841 // HACK: Sometimes the sources path contains a relative path like ../../x
842 if (sourcesFileName
.Contains ("/") || sourcesFileName
.Contains ("\\")) {
843 libraryDirectory
= Path
.Combine (libraryDirectory
, Path
.GetDirectoryName (sourcesFileName
));
844 sourcesFileName
= Path
.GetFileName (sourcesFileName
);
847 libraryDirectory
= Path
.GetFullPath (libraryDirectory
);
849 // HACK: executable.make generates sources paths containing .sources already
850 var libraryName
= sourcesFileName
.Replace (".sources", "");
852 var parser
= GetSourcesParser ();
853 var result
= parser
.Parse (libraryDirectory
, libraryName
);
855 if (result
.SourcesFiles
.Count
== 0)
856 Console
.Error
.WriteLine ($"// No sources files found for '{sourcesFileName}', looked in '{libraryDirectory}' for {libraryName}");
861 private string FixupSourceName (string s
) {
862 string src
= s
.Replace ("/", "\\");
863 if (src
.StartsWith (@"Test\..\"))
864 src
= src
.Substring (8, src
.Length
- 8);
869 private bool IsValidProfile (string output_name
, string profile
) {
870 return SlnGenerator
.profiles
.Contains (profile
);
873 private void GenerateSourceItems (XmlWriter writer
, IEnumerable
<string> fileNames
, HashSet
<string> commonFiles
) {
874 foreach (var file
in fileNames
.OrderBy (f
=> f
, StringComparer
.Ordinal
)) {
875 // FIXME: Is this needed?
876 if ((commonFiles
!= null) && commonFiles
.Contains (file
))
879 writer
.WriteStartElement ("Compile");
880 writer
.WriteAttributeString ("Include", file
);
881 writer
.WriteEndElement ();
885 private void GenerateProjectDependency (XmlWriter xmlWriter
, KnownProjectInfo project
) {
886 xmlWriter
.WriteStartElement ("ProjectReference");
887 xmlWriter
.WriteAttributeString ("Include", project
.Path
);
888 xmlWriter
.WriteElementString ("Name", project
.Name
);
889 xmlWriter
.WriteElementString ("Project", project
.Guid
);
890 xmlWriter
.WriteElementString ("ReferenceOutputAssembly", "false");
891 xmlWriter
.WriteElementString ("CopyToOutputDirectory", "Never");
892 xmlWriter
.WriteElementString ("Private", "false");
893 xmlWriter
.WriteEndElement();
896 private void GenerateProjectDependencies (
898 HashSet
<string> commonFiles
,
899 string prebuild
, string postbuild
901 var prebuild_postbuild
= (prebuild
+ Environment
.NewLine
+ postbuild
)
902 .Replace ("\\", "/");
904 if (commonFiles
.Any (f
=> f
.EndsWith("build\\common\\Consts.cs")))
905 GenerateProjectDependency (writer
, KnownProject
.Genconsts
);
907 if (prebuild_postbuild
.Contains ("jay.exe"))
908 GenerateProjectDependency (writer
, KnownProject
.Jay
);
910 if (prebuild_postbuild
.Contains ("culevel.exe"))
911 GenerateProjectDependency (writer
, KnownProject
.Culevel
);
913 if (prebuild_postbuild
.Contains ("cil-stringreplacer.exe"))
914 GenerateProjectDependency (writer
, KnownProject
.Stringreplacer
);
917 private StringBuilder
GenerateSourceItemGroups (
920 string sources_file_name
,
921 string groupConditional
,
922 string prebuild
, string postbuild
924 var result
= new StringBuilder ();
925 var xmlWriterSettings
= new XmlWriterSettings () {
926 ConformanceLevel
= ConformanceLevel
.Fragment
,
927 WriteEndDocumentOnClose
= true,
928 CheckCharacters
= true,
929 Encoding
= Encoding
.UTF8
,
932 NewLineChars
= NewLine
,
933 NewLineHandling
= NewLineHandling
.Replace
,
934 NewLineOnAttributes
= false,
935 OmitXmlDeclaration
= true
937 var xmlWriter
= XmlWriter
.Create (result
, xmlWriterSettings
);
938 var parseResult
= ReadSources (sources_file_name
);
940 var hostPlatformNames
= GetSourcesParser ().AllHostPlatformNames
;
942 var nullExclusions
= new SourcesFile ("null", true);
944 if (parseResult
.TargetDictionary
.Count
== 0)
947 var targetFileSets
= (from target
in parseResult
.Targets
948 where (target
.Key
.profile
== null) || IsValidProfile (output_name
, target
.Key
.profile
)
949 let matches
= parseResult
.GetMatches (target
)
950 .Select (m
=> FixupSourceName (m
.RelativePath
))
951 .OrderBy (s
=> s
, StringComparer
.Ordinal
)
953 let fileNames
= new HashSet
<string> (matches
)
954 orderby target
.Key
.profile
descending, target
.Key
.hostPlatform
descending
955 select (key
: target
.Key
, fileNames
: fileNames
)).ToList ();
957 var commonFiles
= targetFileSets
.Aggregate (
958 (HashSet
<string>)null,
959 (files
, targetSet
) => {
961 files
= new HashSet
<string> (targetSet
.fileNames
, StringComparer
.Ordinal
);
963 files
.IntersectWith (targetSet
.fileNames
);
968 xmlWriter
.WriteComment ("Common files");
969 xmlWriter
.WriteStartElement ("ItemGroup");
970 GenerateSourceItems (xmlWriter
, commonFiles
, null);
972 GenerateProjectDependencies (xmlWriter
, commonFiles
, prebuild
, postbuild
);
974 xmlWriter
.WriteEndElement ();
975 xmlWriter
.WriteComment ("End of common files");
977 // FIXME: Is this right if the profile/platform pair are not null,null? It probably is
978 if (targetFileSets
.Count
!= 1) {
979 var profileGroups
= (from tfs
in targetFileSets
980 group tfs by tfs
.key
.profile
into sets
981 select sets
).ToList ();
983 xmlWriter
.WriteComment ("Per-profile files");
984 if (profileGroups
.Count
> 1)
985 xmlWriter
.WriteStartElement ("Choose");
987 foreach (var profileGroup
in profileGroups
) {
988 if (profileGroups
.Count
== 1) {
989 } else if (profileGroup
.Key
== null) {
990 xmlWriter
.WriteStartElement ("Otherwise");
992 xmlWriter
.WriteStartElement ("When");
993 xmlWriter
.WriteAttributeString ("Condition", $"'$(Platform)' == '{profileGroup.Key}'");
996 var hostPlatforms
= profileGroup
.ToList ();
997 if (hostPlatforms
.Count
== 1) {
998 xmlWriter
.WriteStartElement ("ItemGroup");
999 GenerateSourceItems (xmlWriter
, hostPlatforms
[0].fileNames
, commonFiles
);
1000 xmlWriter
.WriteEndElement ();
1002 xmlWriter
.WriteComment ("Per-host-platform files");
1003 xmlWriter
.WriteStartElement ("Choose");
1005 foreach (var set in hostPlatforms
) {
1006 if (set.key
.hostPlatform
== null) {
1007 xmlWriter
.WriteStartElement ("Otherwise");
1009 xmlWriter
.WriteStartElement ("When");
1010 xmlWriter
.WriteAttributeString ("Condition", $"'$(HostPlatform)' == '{set.key.hostPlatform}'");
1013 xmlWriter
.WriteStartElement ("ItemGroup");
1014 GenerateSourceItems (xmlWriter
, set.fileNames
, commonFiles
);
1015 xmlWriter
.WriteEndElement ();
1017 xmlWriter
.WriteEndElement();
1020 xmlWriter
.WriteEndElement ();
1021 xmlWriter
.WriteComment ("End of per-host-platform files");
1024 if (profileGroups
.Count
> 1)
1025 xmlWriter
.WriteEndElement ();
1028 if (profileGroups
.Count
> 1)
1029 xmlWriter
.WriteEndElement ();
1030 xmlWriter
.WriteComment ("End of per-profile files");
1038 public VsCsproj
Generate (string library_output
, Dictionary
<string,MsbuildGenerator
> projects
, out string profile
, bool showWarnings
= false)
1040 var generatedProjFile
= GetProjectFilename();
1041 var updatingExistingProject
= File
.Exists(generatedProjFile
);
1043 if (!updatingExistingProject
)
1044 Console
.WriteLine ($"Generating {generatedProjFile}");
1046 string boot
, flags
, output_name
, built_sources
, response
, reskey
, sources_file_name
;
1048 boot
= xproject
.Element ("boot").Value
;
1049 flags
= xproject
.Element ("flags").Value
;
1050 sources_file_name
= xproject
.Element ("sources").Value
;
1051 output_name
= xproject
.Element ("output").Value
;
1052 if (output_name
.EndsWith (".exe"))
1053 Target
= Target
.Exe
;
1054 built_sources
= xproject
.Element ("built_sources").Value
.Trim ();
1055 response
= xproject
.Element ("response").Value
;
1056 reskey
= xproject
.Element ("resources").Value
;
1058 profile
= xproject
.Element ("profile").Value
;
1059 if (string.IsNullOrEmpty (response
)) {
1060 // Address the issue where entries are missing the fx_version
1061 // Should be fixed in the Makefile or elsewhere; this is a workaround
1062 //<fx_version>basic</fx_version>
1063 //<profile>./../build/deps/mcs.exe.sources.response</profile>
1064 //<response></response>
1066 profile
= fx_version
;
1067 if (response
.Contains ("build") || response
.Contains ("basic") || response
.Contains (profile_2_0
)) {
1069 if (response
.Contains (profile_2_0
)) profile
= "net_2_0";
1070 } if (response
.Contains ("build") || response
.Contains ("basic") || response
.Contains (profile_2_0
)) {
1072 } else if (response
.Contains (profile_3_5
)) {
1074 profile
= "net_3_5";
1075 } else if (response
.Contains (profile_4_0
)) {
1077 profile
= "net_4_0";
1078 } else if (response
.Contains (profile_4_x
)) {
1079 fx_version
= "4.6.2";
1080 profile
= "net_4_x";
1082 Console
.WriteLine ($"Using response fallback for {output_name}: {response}");
1085 // Prebuild code, might be in inputs, check:
1086 // inputs/LIBRARY.pre
1088 string prebuild
= GenerateStep (library
, ".pre", "PreBuildEvent");
1089 string postbuild
= GenerateStep (library
, ".post", "PostBuildEvent");
1091 var all_args
= new Queue
<string []> ();
1092 all_args
.Enqueue (flags
.Split ());
1093 while (all_args
.Count
> 0) {
1094 string [] f
= all_args
.Dequeue ();
1096 for (int i
= 0; i
< f
.Length
; i
++) {
1097 if (f
[i
].Length
> 0 && f
[i
][0] == '-')
1098 f
[i
] = "/" + f
[i
].Substring (1);
1100 if (f
[i
] [0] == '@') {
1101 string [] extra_args
;
1102 string response_file
= f
[i
].Substring (1);
1104 var resp_file_full
= Path
.Combine (base_dir
, response_file
);
1105 extra_args
= LoadArgs (resp_file_full
);
1106 if (extra_args
== null) {
1107 Console
.Error
.WriteLine ($"// {library_output}: Unable to open response file: {resp_file_full}");
1108 Environment
.Exit (1);
1111 all_args
.Enqueue (extra_args
);
1115 if (CSCParseOption (f
[i
], ref f
))
1117 Console
.Error
.WriteLine ($"// {library_output}: Failure with {f [i]}");
1118 Environment
.Exit (1);
1122 var groupConditional
= $"Condition=\" '$(Platform)' == '{profile}' \"";
1125 updatingExistingProject
1126 ? new StringBuilder ()
1127 : GenerateSourceItemGroups (
1128 output_name
, profile
,
1129 sources_file_name
, groupConditional
,
1133 //if (library == "corlib-build") // otherwise, does not compile on fx_version == 4.0
1135 // references.Add("System.dll");
1136 // references.Add("System.Xml.dll");
1139 //if (library == "System.Core-build") // otherwise, slow compile. May be a transient need.
1141 // this.ignore_warning.Add(1685);
1142 // this.ignore_warning.Add(0436);
1145 var refs
= new StringBuilder ();
1147 refs
.Append ($" <ItemGroup {groupConditional}>{NewLine}");
1149 if (response
.Contains ("_test")) {
1150 refs
.Append ($@" <Reference Include=""nunitlite"">{NewLine}");
1151 refs
.Append ($@" <HintPath>..\lib\{profile}\nunitlite.dll</HintPath>{NewLine}");
1152 refs
.Append ($@" <Private>False</Private>{NewLine}");
1153 refs
.Append ($@" </Reference>{NewLine}");
1158 // Generate resource referenced from the command line
1160 var resources
= new StringBuilder ();
1161 if (embedded_resources
.Count
> 0) {
1162 foreach (var dk
in embedded_resources
) {
1163 var source
= dk
.Key
;
1164 if (source
.EndsWith (".resources"))
1165 source
= source
.Replace (".resources", ".resx");
1167 // try to find a pre-built resource, and use that instead of trying to build it
1168 if (source
.EndsWith (".resx")) {
1169 var probe_prebuilt
= Path
.Combine (base_dir
, source
.Replace (".resx", ".resources.prebuilt"));
1170 if (File
.Exists (probe_prebuilt
)) {
1172 source
= GetRelativePath (base_dir
+ "/", probe_prebuilt
);
1175 AppendResource (resources
, source
, dk
.Value
);
1179 // Generate resources that were part of the explicit <resource> node
1181 if (reskey
!= null && reskey
!= ""){
1182 var pairs
= reskey
.Split (' ', '\n', '\t');
1183 foreach (var pair
in pairs
){
1184 var p
= pair
.IndexOf (",");
1186 Console
.Error
.WriteLine ($"// Found a resource without a filename: {pairs} for {Csproj.csProjFilename}");
1187 Environment
.Exit (1);
1189 AppendResource (resources
, pair
.Substring (p
+1), pair
.Substring (0, p
) + ".resources");
1192 if (resources
.Length
> 0){
1193 resources
.Insert (0, $" <ItemGroup {groupConditional}>{NewLine}");
1194 resources
.Append (" </ItemGroup>" + NewLine
);
1197 if (references
.Count
> 0 || reference_aliases
.Count
> 0) {
1198 // -r:mscorlib.dll -r:System.dll
1199 //<ProjectReference Include="..\corlib\corlib-basic.csproj">
1200 // <Project>{155aef28-c81f-405d-9072-9d52780e3e70}</Project>
1201 // <Name>corlib-basic</Name>
1202 //</ProjectReference>
1203 //<ProjectReference Include="..\System\System-basic.csproj">
1204 // <Project>{2094e859-db2f-481f-9630-f89d31d9ed48}</Project>
1205 // <Name>System-basic</Name>
1206 //</ProjectReference>
1207 var refdistinct
= references
.Distinct ();
1208 foreach (string reference
in refdistinct
) {
1210 var match
= GetMatchingCsproj (library_output
, reference
, projects
);
1211 if (match
!= null) {
1212 AddProjectReference (refs
, Csproj
, match
, reference
, null);
1215 Console
.Error
.WriteLine ($"{library}: Could not find a matching project reference for {Path.GetFileName (reference)}");
1216 Console
.Error
.WriteLine (" --> Adding reference with hintpath instead");
1218 var externalDrawing
= (reference
== Environment
.GetEnvironmentVariable ("EXTERNAL_FACADE_DRAWING_REFERENCE"));
1219 refs
.Append (" <Reference Include=\"" + (externalDrawing
? "$(EXTERNAL_FACADE_DRAWING_REFERENCE)" : reference
) + "\">" + NewLine
);
1220 refs
.Append (" <SpecificVersion>False</SpecificVersion>" + NewLine
);
1221 refs
.Append (" <HintPath>" + (externalDrawing
? "$(EXTERNAL_FACADE_DRAWING_REFERENCE)" : reference
) + "</HintPath>" + NewLine
);
1222 refs
.Append (" <Private>False</Private>" + NewLine
);
1223 refs
.Append (" </Reference>" + NewLine
);
1227 foreach (string r
in reference_aliases
) {
1228 int index
= r
.IndexOf ('=');
1229 string alias = r
.Substring (0, index
);
1230 string assembly
= r
.Substring (index
+ 1);
1231 var match
= GetMatchingCsproj (library_output
, assembly
, projects
, explicitPath
: true);
1232 if (match
!= null) {
1233 AddProjectReference (refs
, Csproj
, match
, r
, alias);
1235 throw new NotSupportedException (string.Format ("From {0}, could not find a matching project reference for {1}", library
, r
));
1236 refs
.Append (" <Reference Include=\"" + assembly
+ "\">" + NewLine
);
1237 refs
.Append (" <SpecificVersion>False</SpecificVersion>" + NewLine
);
1238 refs
.Append (" <HintPath>" + r
+ "</HintPath>" + NewLine
);
1239 refs
.Append (" <Aliases>" + alias + "</Aliases>" + NewLine
);
1240 refs
.Append (" </Reference>" + NewLine
);
1246 refs
.Append (" </ItemGroup>");
1249 // ../class/lib/build/tmp/System.Xml.dll [No longer possible, we should be removing this from order.xml]
1250 // /class/lib/basic/System.Core.dll
1251 // <library_output>mcs.exe</library_output>
1252 string build_output_dir
, intermediate_output_dir
;
1253 if (LibraryOutput
.Contains ("/")) {
1254 build_output_dir
= Path
.GetDirectoryName (LibraryOutput
);
1255 intermediate_output_dir
= build_output_dir
.Substring (0, build_output_dir
.IndexOf("/class/lib") + 7) + "obj";
1258 build_output_dir
= "bin\\Debug\\" + library
;
1259 intermediate_output_dir
= "obj\\Debug\\" + library
;
1262 if (build_output_dir
.Contains ("-linux") || build_output_dir
.Contains ("-macos") || build_output_dir
.Contains ("-win32") || build_output_dir
.Contains ("-unix"))
1263 build_output_dir
= build_output_dir
1264 .Replace ("-linux", "-$(HostPlatform)")
1265 .Replace ("-macos", "-$(HostPlatform)")
1266 .Replace ("-win32", "-$(HostPlatform)")
1267 .Replace ("-unix", "-$(HostPlatform)");
1269 bool basic_or_build
= (library
.Contains ("-basic") || library
.Contains ("-build"));
1271 // If an EXE is built with nostdlib, it won't work unless run with mono.exe. This stops our build steps
1272 // from working in visual studio (because we already replace @MONO@ with '' on Windows.)
1274 if (Target
!= Target
.Library
)
1277 // We have our target framework set to 4.5 in many places because broken scripts check for files with 4.5
1278 // in the path, even though we compile code that uses 4.6 features. So we need to manually fix that here.
1280 if (fx_version
== "4.5")
1281 fx_version
= "4.6.2";
1283 // The VS2017 signing system fails to sign using this key for some reason, so for now,
1284 // just disable code signing for the nunit assemblies. It's not important.
1285 // I'd rather fix this by updating the makefiles but it seems to be impossible to disable
1286 // code signing in our make system...
1288 if (StrongNameKeyFile
?.Contains("nunit.snk") ?? false)
1289 StrongNameKeyFile
= null;
1292 // Replace the template values
1295 string strongNameSection
= "";
1296 if (StrongNameKeyFile
!= null){
1297 strongNameSection
= String
.Format (
1298 " <SignAssembly>true</SignAssembly>" + NewLine
+
1300 " <AssemblyOriginatorKeyFile>{0}</AssemblyOriginatorKeyFile>",
1301 StrongNameKeyFile
, StrongNameDelaySign
? " <DelaySign>true</DelaySign>" + NewLine
: "");
1304 string assemblyName
= Path
.GetFileNameWithoutExtension (output_name
);
1305 var outputSuffix
= Path
.GetFileName (build_output_dir
);
1307 string textToUpdate
= updatingExistingProject
1308 ? File
.ReadAllText(generatedProjFile
)
1311 var properties
= new StringBuilder ();
1312 properties
.Append ($" <PropertyGroup {groupConditional}>{NewLine}");
1313 properties
.Append ($" <OutputPath>{build_output_dir}</OutputPath>{NewLine}");
1314 properties
.Append ($" <IntermediateOutputPath>{intermediate_output_dir}/$(AssemblyName)-{outputSuffix}</IntermediateOutputPath>{NewLine}");
1315 properties
.Append ($" <DefineConstants>{defines.ToString ()}</DefineConstants>{NewLine}");
1316 properties
.Append ($" </PropertyGroup>{NewLine}");
1318 var prebuild_postbuild
= new StringBuilder ();
1319 if (!String
.IsNullOrWhiteSpace(prebuild
) || !String
.IsNullOrWhiteSpace(postbuild
))Â
{
1320 prebuild_postbuild
.Append ($" <PropertyGroup>{NewLine}");
1321 prebuild_postbuild
.Append (prebuild
);
1322 prebuild_postbuild
.Append (postbuild
);
1323 prebuild_postbuild
.Append ($" </PropertyGroup>{NewLine}");
1326 var builtSources
= new StringBuilder ();
1327 if (built_sources
.Length
> 0) {
1328 builtSources
.Append ($" <ItemGroup Condition=\" '$(Platform)' == '{profile}' \">{NewLine}");
1329 foreach (var fileName
in built_sources
.Split ()) {
1330 var fixedFileName
= FixupSourceName (fileName
);
1331 builtSources
.Append ($" <Compile Include=\"{fixedFileName}\" />{NewLine}");
1333 builtSources
.Append ($" </ItemGroup>{NewLine}");
1336 Csproj
.output
= textToUpdate
.
1337 Replace ("@OUTPUTTYPE@", Target
== Target
.Library
? "Library" : "Exe").
1338 Replace ("@SIGNATURE@", strongNameSection
).
1339 Replace ("@PROJECTGUID@", Csproj
.projectGuid
).
1340 Replace ("@DEFINES@", defines
.ToString ()).
1341 Replace ("@DISABLEDWARNINGS@", string.Join (",", (from i
in ignore_warning
select i
.ToString ()).ToArray ())).
1342 Replace ("@LANGVERSION@", LangVersion
).
1343 //Replace("@NOSTDLIB@", (basic_or_build || (!StdLib)) ? "<NoStdLib>true</NoStdLib>" : string.Empty).
1344 Replace ("@NOSTDLIB@", "<NoStdLib>" + (!StdLib
).ToString () + "</NoStdLib>").
1345 Replace ("@NOCONFIG@", "<NoConfig>" + (!load_default_config
).ToString () + "</NoConfig>").
1346 Replace ("@ALLOWUNSAFE@", Unsafe
? "<AllowUnsafeBlocks>true</AllowUnsafeBlocks>" : "").
1347 Replace ("@FX_VERSION@", fx_version
).
1348 Replace ("@ASSEMBLYNAME@", assemblyName
).
1349 Replace ("@DEBUG@", want_debugging_support
? "true" : "false").
1350 Replace ("@DEBUGTYPE@", want_debugging_support
? "full" : "pdbonly").
1351 Replace ("@PREBUILD_POSTBUILD@", prebuild_postbuild
.ToString ()).
1352 Replace ("@STARTUPOBJECT@", main
== null ? "" : $"<StartupObject>{main}</StartupObject>").
1353 //Replace ("@ADDITIONALLIBPATHS@", String.Format ("<AdditionalLibPaths>{0}</AdditionalLibPaths>", string.Join (",", libs.ToArray ()))).
1354 Replace ("@ADDITIONALLIBPATHS@", String
.Empty
).
1355 Replace ("@OPTIMIZE@", Optimize
? "true" : "false").
1356 Replace ("@METADATAVERSION@", assemblyName
== "mscorlib" ? "<RuntimeMetadataVersion>Mono</RuntimeMetadataVersion>" : "");
1358 var propertiesPlaceholder
= "<!-- @ALL_PROFILE_PROPERTIES@ -->";
1359 var refsPlaceholder
= "<!-- @ALL_REFERENCES@ -->";
1360 var resourcesPlaceholder
= "<!-- @ALL_RESOURCES@ -->";
1361 var sourcesPlaceholder
= "<!-- @ALL_SOURCES@ -->";
1362 var builtSourcesPlaceholder
= "<!-- @BUILT_SOURCES@ -->";
1364 Csproj
.output
= Csproj
.output
.
1365 Replace (propertiesPlaceholder
, properties
.ToString () + NewLine
+ propertiesPlaceholder
).
1366 Replace (refsPlaceholder
, refs
.ToString () + NewLine
+ refsPlaceholder
).
1367 Replace (resourcesPlaceholder
, resources
.ToString () + NewLine
+ resourcesPlaceholder
).
1368 Replace (sourcesPlaceholder
, sources
.ToString () + NewLine
+ sourcesPlaceholder
).
1369 Replace (builtSourcesPlaceholder
, builtSources
.ToString () + NewLine
+ builtSourcesPlaceholder
);
1371 Csproj
.preBuildEvent
= prebuild
;
1372 Csproj
.postBuildEvent
= postbuild
;
1374 //Console.WriteLine ("Generated {0}", ofile.Replace ("\\", "/"));
1375 // Console.WriteLine("Writing {0}", generatedProjFile);
1376 using (var o
= new StreamWriter (generatedProjFile
)) {
1377 o
.WriteLine (Csproj
.output
);
1383 string GenerateStep (string library
, string suffix
, string eventKey
)
1385 string target
= Load (library
+ suffix
);
1386 string target_windows
, target_unix
;
1388 int q
= library
.IndexOf ("-");
1390 target
= target
+ Load (library
.Substring (0, q
) + suffix
);
1392 target_unix
= target
.Replace ("@MONO@", "mono").Replace ("@CAT@", "cat");
1393 target_windows
= target
.Replace ("@MONO@", "").Replace ("@CAT@", "type");
1395 target_unix
= target_unix
.Replace ("\\jay\\jay.exe", "\\jay\\jay");
1397 target_unix
= target_unix
.Replace ("@COPY@", "cp");
1398 target_windows
= target_windows
.Replace ("@COPY@", "copy");
1400 target_unix
= target_unix
.Replace ("\r", "");
1401 const string condition_unix
= "Condition=\" '$(OS)' != 'Windows_NT' \"";
1402 const string condition_windows
= "Condition=\" '$(OS)' == 'Windows_NT' \"";
1404 var result
= new StringBuilder ();
1405 if (!String
.IsNullOrWhiteSpace (target_unix
))
1406 result
.Append ($" <{eventKey} {condition_unix}>{target_unix.Trim ()}</{eventKey}>{NewLine}");
1407 if (!String
.IsNullOrWhiteSpace (target_windows
))
1408 result
.Append ($" <{eventKey} {condition_windows}>{target_windows.Trim ()}</{eventKey}>{NewLine}");
1409 return result
.ToString ();
1412 void AddProjectReference (StringBuilder refs
, VsCsproj result
, MsbuildGenerator match
, string r
, string alias)
1414 refs
.AppendFormat (" <ProjectReference Include=\"{0}\"", GetRelativePath (result
.csProjFilename
, match
.CsprojFilename
));
1415 if (alias != null) {
1416 refs
.Append (">" + NewLine
);
1417 refs
.Append (" <Aliases>" + alias + "</Aliases>" + NewLine
);
1418 refs
.Append (" </ProjectReference>" + NewLine
);
1421 refs
.Append (" />" + NewLine
);
1423 if (!result
.projReferences
.Contains (match
.Csproj
))
1424 result
.projReferences
.Add (match
.Csproj
);
1427 public static string GetRelativePath (string from, string to
)
1429 from = from.Replace ("\\", "/");
1430 to
= to
.Replace ("\\", "/");
1431 var fromUri
= new Uri (Path
.GetFullPath (from));
1432 var toUri
= new Uri (Path
.GetFullPath (to
));
1434 var ret
= fromUri
.MakeRelativeUri (toUri
).ToString ().Replace ("%5C", "\x5c");
1438 MsbuildGenerator
GetMatchingCsproj (string library_output
, string dllReferenceName
, Dictionary
<string,MsbuildGenerator
> projects
, bool explicitPath
= false)
1440 // libDir would be "./../../class/lib/net_4_x for example
1442 if (!dllReferenceName
.EndsWith (".dll") && !dllReferenceName
.EndsWith (".exe"))
1443 dllReferenceName
+= ".dll";
1445 var probe
= Path
.GetFullPath (Path
.Combine (base_dir
, dllReferenceName
));
1446 foreach (var project
in projects
){
1447 if (probe
== project
.Value
.AbsoluteLibraryOutput
)
1448 return project
.Value
;
1451 // not explicit, search for the library in the lib path order specified
1453 foreach (var libDir
in libs
) {
1454 var abs
= Path
.GetFullPath (Path
.Combine (base_dir
, libDir
));
1455 foreach (var project
in projects
){
1456 probe
= Path
.Combine (abs
, dllReferenceName
);
1458 if (probe
== project
.Value
.AbsoluteLibraryOutput
)
1459 return project
.Value
;
1463 // Last attempt, try to find the library in all the projects
1464 foreach (var project
in projects
) {
1465 if (project
.Value
.AbsoluteLibraryOutput
.EndsWith (dllReferenceName
))
1466 return project
.Value
;
1469 var ljoined
= String
.Join (", ", libs
);
1470 Console
.Error
.WriteLine ($"// {library_output}: did not find referenced {dllReferenceName} with libs={ljoined}");
1472 // FIXME: This is incredibly noisy and generates a billion lines of output
1474 foreach (var p
in projects
) {
1475 Console
.Error
.WriteLine ("{0}", p
.Value
.AbsoluteLibraryOutput
);
1483 public static class Driver
{
1484 static IEnumerable
<XElement
> GetProjects (bool withTests
= false)
1486 XDocument doc
= XDocument
.Load ("order.xml");
1487 foreach (XElement project
in doc
.Root
.Elements ()) {
1488 string dir
= project
.Attribute ("dir").Value
;
1489 string library
= project
.Attribute ("library").Value
;
1490 var profile
= project
.Element ("profile").Value
;
1493 // Do not do 2.1, it is not working yet
1494 // Do not do basic, as there is no point (requires a system mcs to be installed).
1496 if (library
.Contains ("moonlight") || library
.Contains ("-basic") || library
.EndsWith ("bootstrap") || library
.Contains ("build"))
1499 // The next ones are to make debugging easier for now
1500 if (profile
== "basic")
1503 // For now -- problem is, our resolver currently only considers the assembly name, and we ahve
1504 // conflicing 2.0 and 2.4 versions so for now, we just skip the nunit20 versions
1505 if (dir
.Contains ("nunit20"))
1508 if (library
.Contains ("tests") && !withTests
)
1511 yield return project
;
1515 public static void Main (string [] args
)
1517 if (!File
.Exists ("genproj.cs")) {
1518 Console
.Error
.WriteLine ("This command must be executed from mono/msvc/scripts");
1519 Environment
.Exit (1);
1522 if (args
.Length
== 1) {
1523 switch (args
[0].ToLower()) {
1527 Console
.Error
.WriteLine ("Usage:");
1528 Console
.Error
.WriteLine ("genproj.exe [visual_studio_release] [output_full_solutions] [with_tests]");
1529 Console
.Error
.WriteLine ("If output_full_solutions is false, only the main System*.dll");
1530 Console
.Error
.WriteLine (" assemblies (and dependencies) is included in the solution.");
1531 Console
.Error
.WriteLine ("Example:");
1532 Console
.Error
.WriteLine (" genproj.exe 2012 false false");
1533 Console
.Error
.WriteLine ("genproj.exe with no arguments is equivalent to 'genproj.exe 2012 true false'\n\n");
1534 Console
.Error
.WriteLine ("genproj.exe deps");
1535 Console
.Error
.WriteLine ("Generates a Makefile dependency file from the projects input");
1536 Environment
.Exit (0);
1541 var slnVersion
= (args
.Length
> 0) ? args
[0] : "2012";
1542 bool fullSolutions
= (args
.Length
> 1) ? bool.Parse (args
[1]) : true;
1543 bool withTests
= (args
.Length
> 2) ? bool.Parse (args
[2]) : false;
1545 // To generate makefile depenedencies
1546 var makefileDeps
= (args
.Length
> 0 && args
[0] == "deps");
1548 var sln_gen
= new SlnGenerator (slnVersion
);
1549 var four_five_sln_gen
= new SlnGenerator (slnVersion
);
1550 var projects
= new Dictionary
<string,MsbuildGenerator
> ();
1552 var duplicates
= new List
<string> ();
1553 Console
.Error
.WriteLine("// Deleting existing project files");
1554 foreach (var project
in GetProjects (withTests
)) {
1555 var library_output
= project
.Element ("library_output").Value
;
1557 var gen
= new MsbuildGenerator (project
);
1558 projects
[library_output
] = gen
;
1559 gen
.EraseExisting ();
1561 Console
.Error
.WriteLine("// Generating project files");
1562 foreach (var project
in GetProjects (withTests
)){
1563 var library_output
= project
.Element ("library_output").Value
;
1564 // Console.WriteLine ("=== {0} ===", library_output);
1565 var gen
= projects
[library_output
];
1568 var csproj
= gen
.Generate (library_output
, projects
, out profileName
);
1569 var csprojFilename
= csproj
.csProjFilename
;
1570 if (!sln_gen
.ContainsProjectIdentifier (csproj
.library
)) {
1571 sln_gen
.Add (csproj
);
1573 duplicates
.Add (csprojFilename
);
1576 if (profileName
== null) {
1577 Console
.Error
.WriteLine ($"// {library_output} has no profile");
1579 HashSet
<string> profileNames
;
1580 if (!SlnGenerator
.profilesByGuid
.TryGetValue (csproj
.projectGuid
, out profileNames
))
1581 SlnGenerator
.profilesByGuid
[csproj
.projectGuid
] = profileNames
= new HashSet
<string>();
1583 profileNames
.Add (profileName
);
1584 SlnGenerator
.observedProfiles
.Add (profileName
);
1586 } catch (Exception e
) {
1587 Console
.Error
.WriteLine ("// Error in {0}\n{1}", project
, e
);
1591 Console
.WriteLine ("Deduplicating project references");
1593 foreach (var csprojFile
in projects
.Values
.Select (x
=> x
.GetProjectFilename ()).Distinct ())
1595 // Console.WriteLine ("Deduplicating: " + csprojFile);
1596 DeduplicateProjectReferences (csprojFile
);
1599 Func
<MsbuildGenerator
.VsCsproj
, bool> additionalFilter
;
1600 additionalFilter
= fullSolutions
? (Func
<MsbuildGenerator
.VsCsproj
, bool>)null : IsCommonLibrary
;
1602 FillSolution (four_five_sln_gen
, MsbuildGenerator
.profile_4_x
, projects
.Values
, additionalFilter
);
1604 if (duplicates
.Count () > 0) {
1605 var sb
= new StringBuilder ();
1606 sb
.AppendLine ("// WARNING: Skipped some project references, apparent duplicates in order.xml:");
1607 foreach (var item
in duplicates
) {
1608 sb
.AppendLine ($"// {item}");
1610 Console
.Error
.WriteLine (sb
.ToString ());
1613 WriteSolution (four_five_sln_gen
, Path
.Combine ("..", "..", "bcl.sln"));
1616 const string classDirPrefix
= "./../../";
1617 Console
.WriteLine ("here {0}", sln_gen
.libraries
.Count
);
1618 foreach (var p
in sln_gen
.libraries
){
1619 string rebasedOutput
= RebaseToClassDirectory (MsbuildGenerator
.GetRelativePath ("../../mcs/class", p
.library_output
));
1621 Console
.Write ("{0}: ", rebasedOutput
);
1622 foreach (var r
in p
.projReferences
){
1623 var lo
= r
.library_output
;
1624 if (lo
.StartsWith (classDirPrefix
))
1625 lo
= lo
.Substring (classDirPrefix
.Length
);
1627 lo
= "<<ERROR-dependency is not a class library>>";
1628 Console
.Write ("{0} ", lo
);
1630 Console
.Write ("\n\t(cd {0}; make {1})", p
.MsbuildGenerator
.dir
, p
.library_output
);
1631 Console
.WriteLine ("\n");
1635 // A few other optional solutions
1636 // Solutions with 'everything' and the most common libraries used in development may be of interest
1637 //WriteSolution (sln_gen, "./mcs_full.sln");
1638 //WriteSolution (small_full_sln_gen, "small_full.sln");
1639 // The following may be useful if lacking visual studio or MonoDevelop, to bootstrap mono compiler self-hosting
1640 //WriteSolution (basic_sln_gen, "mcs_basic.sln");
1641 //WriteSolution (build_sln_gen, "mcs_build.sln");
1644 static void DeduplicateProjectReferences (string csprojFilename
)
1646 XmlDocument doc
= new XmlDocument ();
1647 doc
.Load (csprojFilename
);
1648 XmlNamespaceManager mgr
= new XmlNamespaceManager (doc
.NameTable
);
1649 mgr
.AddNamespace ("x", "http://schemas.microsoft.com/developer/msbuild/2003");
1651 XmlNode root
= doc
.DocumentElement
;
1652 var allProjectReferences
= new Dictionary
<string, List
<string>> ();
1654 ProcessCompileOrProjectReferenceItems (mgr
, root
,
1655 (source
, platform
) => {},
1656 // grab all project references across all platforms
1657 (projRef
, platform
) =>
1659 if (!allProjectReferences
.ContainsKey (platform
))
1660 allProjectReferences
[platform
] = new List
<string> ();
1661 allProjectReferences
[platform
].Add (projRef
.Attributes
["Include"].Value
);
1664 if (allProjectReferences
.Count
> 1)
1666 // find the project references which are common across all platforms
1667 var commonProjectReferences
= allProjectReferences
.Values
.First ();
1668 foreach (var l
in allProjectReferences
.Values
.Skip (1))
1669 commonProjectReferences
= commonProjectReferences
.Intersect (l
).ToList ();
1671 if (commonProjectReferences
.Count
> 0)
1673 // remove common project references from the individual platforms
1674 ProcessCompileOrProjectReferenceItems (mgr
, root
, null, (projRef
, platform
) =>
1676 var parent
= projRef
.ParentNode
;
1677 if (commonProjectReferences
.Contains (projRef
.Attributes
["Include"].Value
))
1678 parent
.RemoveChild (projRef
);
1680 if (!parent
.HasChildNodes
)
1681 parent
.ParentNode
.RemoveChild (parent
);
1684 // add common project references as ItemGroup
1685 XmlNode commonProjRefsComment
= root
.SelectSingleNode ("//comment()[. = ' @COMMON_PROJECT_REFERENCES@ ']");
1686 XmlElement commonProjRefsElement
= doc
.CreateElement ("ItemGroup", root
.NamespaceURI
);
1688 foreach (var s
in commonProjectReferences
)
1690 var c
= doc
.CreateElement ("ProjectReference", root
.NamespaceURI
);
1691 var v
= doc
.CreateAttribute ("Include");
1693 c
.Attributes
.Append (v
);
1695 commonProjRefsElement
.AppendChild (c
);
1697 root
.ReplaceChild (commonProjRefsElement
, commonProjRefsComment
);
1701 using (var w
= XmlWriter
.Create (csprojFilename
, new XmlWriterSettings { NewLineChars = SlnGenerator.NewLine, Indent = true }
))
1705 static void ProcessCompileOrProjectReferenceItems (XmlNamespaceManager mgr
, XmlNode x
, Action
<XmlNode
, string> compileAction
, Action
<XmlNode
, string> projRefAction
)
1707 foreach (XmlNode n
in x
.SelectNodes("//x:ItemGroup[@Condition]", mgr
))
1709 if (n
.Attributes
.Count
== 0)
1712 var platform
= n
.Attributes
["Condition"].Value
;
1714 if (!platform
.Contains("$(Platform)"))
1717 var compileItems
= n
.SelectNodes("./x:Compile[@Include]", mgr
);
1719 if (compileAction
!= null && compileItems
.Count
!= 0) {
1720 foreach (XmlNode source
in compileItems
)
1721 compileAction(source
, platform
);
1724 var projRefItems
= n
.SelectNodes("./x:ProjectReference[@Include]", mgr
);
1726 if (projRefAction
!= null && projRefItems
.Count
!= 0) {
1727 foreach (XmlNode proj
in projRefItems
) {
1728 // we don't bother to process ProjectReferences with Aliases
1729 if (!proj
.HasChildNodes
)
1730 projRefAction(proj
, platform
);
1736 // Rebases a path, assuming that execution is taking place in the "class" subdirectory,
1737 // so it strips ../class/ from a path, which is a no-op
1738 static string RebaseToClassDirectory (string path
)
1740 const string prefix
= "../class/";
1741 int p
= path
.IndexOf (prefix
);
1744 return path
.Substring (0, p
) + path
.Substring (p
+prefix
.Length
);
1748 static void FillSolution (SlnGenerator solution
, string profileString
, IEnumerable
<MsbuildGenerator
> projects
, Func
<MsbuildGenerator
.VsCsproj
, bool> additionalFilter
= null)
1750 foreach (var generator
in projects
) {
1751 var vsCsproj
= generator
.Csproj
;
1752 if (!vsCsproj
.library
.Contains (profileString
))
1754 if (additionalFilter
!= null && !additionalFilter (vsCsproj
))
1756 var csprojFilename
= vsCsproj
.csProjFilename
;
1757 if (!solution
.ContainsProjectIdentifier (vsCsproj
.library
)) {
1758 solution
.Add (vsCsproj
);
1759 RecursiveAddProj (solution
, vsCsproj
);
1764 static void RecursiveAddProj (SlnGenerator solution
, MsbuildGenerator
.VsCsproj vsCsproj
, int recursiveDepth
= 1)
1766 const int max_recursive
= 16;
1767 if (recursiveDepth
> max_recursive
) throw new Exception (string.Format ("Reached {0} levels of project dependency", max_recursive
));
1768 foreach (var projRef
in vsCsproj
.projReferences
) {
1769 if (!solution
.ContainsProjectIdentifier (projRef
.library
)) {
1770 solution
.Add (projRef
);
1771 RecursiveAddProj (solution
, projRef
, recursiveDepth
+ 1);
1776 static void WriteSolution (SlnGenerator sln_gen
, string slnfilename
)
1778 Console
.WriteLine (String
.Format ("// Writing solution {1}, with {0} projects", sln_gen
.Count
, slnfilename
));
1779 sln_gen
.Write (slnfilename
);
1782 static bool IsCommonLibrary (MsbuildGenerator
.VsCsproj proj
)
1784 var library
= proj
.library
;
1785 //if (library.Contains ("-basic"))
1787 //if (library.Contains ("-build"))
1789 //if (library.StartsWith ("corlib"))
1791 if (library
.StartsWith ("System-"))
1793 if (library
.StartsWith ("System.Xml"))
1795 if (library
.StartsWith ("System.Secu"))
1797 if (library
.StartsWith ("System.Configuration"))
1799 if (library
.StartsWith ("System.Core"))
1801 //if (library.StartsWith ("Mono."))