2010-04-16 Sebastien Pouliot <sebastien@ximian.com>
[mono/afaerber.git] / msvc / scripts / genproj.cs
bloba42fb3ec64b0555121b171dfbaa496daa16a6ec9
1 using System;
2 using System.IO;
3 using System.Collections.Generic;
4 using System.Text;
5 using System.Globalization;
6 using System.Xml.Linq;
8 public enum Target {
9 Library, Exe, Module, WinExe
12 public enum LanguageVersion
14 ISO_1 = 1,
15 Default_MCS = 2,
16 ISO_2 = 3,
17 LINQ = 4,
18 Future = 5,
19 Default = LINQ
22 class SlnGenerator {
23 const string header = "Microsoft Visual Studio Solution File, Format Version 10.00\n" +
24 "# Visual Studio 2008";
26 const string project_start = "Project(\"{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}\") = \"{0}\", \"{1}\", \"{{{2}}}\"";
27 const string project_end = "EndProject";
29 Dictionary<string, string> libraries = new Dictionary<string, string> ();
31 public void Add (string library)
33 try {
34 libraries.Add (library, Guid.NewGuid ().ToString ().ToUpper ());
36 catch (Exception ex) {
37 Console.WriteLine (ex);
41 public void Write (string filename)
43 using (var sln = new StreamWriter (filename)) {
44 sln.WriteLine ();
45 sln.WriteLine (header);
46 foreach (var library in libraries) {
47 var library_name = Path.GetFileNameWithoutExtension (library.Key);
48 sln.WriteLine (project_start, library_name, library.Key, library.Value);
49 sln.WriteLine (project_end);
51 sln.WriteLine ("Global");
53 sln.WriteLine ("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution");
54 sln.WriteLine ("\t\tDebug|Any CPU = Debug|Any CPU");
55 sln.WriteLine ("\t\tRelease|Any CPU = Release|Any CPU");
56 sln.WriteLine ("\tEndGlobalSection");
58 sln.WriteLine ("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution");
59 foreach (var library in libraries) {
60 sln.WriteLine ("\t\t{{{0}}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU", library.Value);
61 sln.WriteLine ("\t\t{{{0}}}.Debug|Any CPU.Build.0 = Debug|Any CPU", library.Value);
62 sln.WriteLine ("\t\t{{{0}}}.Release|Any CPU.ActiveCfg = Release|Any CPU", library.Value);
63 sln.WriteLine ("\t\t{{{0}}}.Release|Any CPU.Build.0 = Release|Any CPU", library.Value);
65 sln.WriteLine ("\tEndGlobalSection");
67 sln.WriteLine ("\tGlobalSection(SolutionProperties) = preSolution");
68 sln.WriteLine ("\t\tHideSolutionNode = FALSE");
69 sln.WriteLine ("\tEndGlobalSection");
71 sln.WriteLine ("EndGlobal");
76 class MsbuildGenerator {
77 static void Usage ()
79 Console.WriteLine ("Invalid argument");
82 static string template;
83 static MsbuildGenerator ()
85 using (var input = new StreamReader ("csproj.tmpl")){
86 template = input.ReadToEnd ();
90 // The directory as specified in order.xml
91 string dir;
94 // Our base directory, this is relative to our exectution point mono/msvc/scripts
95 string base_dir;
97 string mcs_topdir;
99 // Class directory, relative to
100 string class_dir;
102 public MsbuildGenerator (string dir)
104 this.dir = dir;
106 if (dir == "mcs"){
107 mcs_topdir = "..\\";
108 class_dir = "..\\class\\";
109 base_dir = "..\\..\\..\\mcs\\mcs";
110 } else {
111 mcs_topdir = "..\\";
113 foreach (char c in dir){
114 if (c == '/')
115 mcs_topdir = "..\\" + mcs_topdir;
117 class_dir = mcs_topdir.Substring (3);
119 base_dir = "..\\..\\..\\mcs\\" + dir;
123 // Currently used
124 bool Unsafe = false;
125 StringBuilder defines = new StringBuilder ();
126 bool StdLib = true;
128 // Currently unused
129 Target Target = Target.Exe;
130 string TargetExt = ".exe";
131 string OutputFile;
132 bool Optimize = true;
133 bool VerifyClsCompliance = true;
135 string win32IconFile;
136 bool want_debugging_support = false;
137 bool Checked = false;
138 bool WarningsAreErrors;
139 Dictionary<string,string> embedded_resources = new Dictionary<string,string> ();
140 List<string> references = new List<string> ();
141 List<string> reference_aliases = new List<string> ();
142 List<string> warning_as_error = new List<string> ();
143 int WarningLevel = 4;
144 List<int> ignore_warning = new List<int> ();
145 bool load_default_config = true;
146 string StrongNameKeyFile;
147 string StrongNameKeyContainer;
148 bool StrongNameDelaySign = false;
149 LanguageVersion Version = LanguageVersion.Default;
150 string CodePage;
152 readonly char[] argument_value_separator = new char [] { ';', ',' };
155 // This parses the -arg and /arg options to the compiler, even if the strings
156 // in the following text use "/arg" on the strings.
158 bool CSCParseOption (string option, ref string [] args)
160 int idx = option.IndexOf (':');
161 string arg, value;
163 if (idx == -1){
164 arg = option;
165 value = "";
166 } else {
167 arg = option.Substring (0, idx);
169 value = option.Substring (idx + 1);
172 switch (arg.ToLower (CultureInfo.InvariantCulture)){
173 case "/nologo":
174 return true;
176 case "/t":
177 case "/target":
178 switch (value){
179 case "exe":
180 Target = Target.Exe;
181 break;
183 case "winexe":
184 Target = Target.WinExe;
185 break;
187 case "library":
188 Target = Target.Library;
189 TargetExt = ".dll";
190 break;
192 case "module":
193 Target = Target.Module;
194 TargetExt = ".netmodule";
195 break;
197 default:
198 return false;
200 return true;
202 case "/out":
203 if (value.Length == 0){
204 Usage ();
205 Environment.Exit (1);
207 OutputFile = value;
208 return true;
210 case "/o":
211 case "/o+":
212 case "/optimize":
213 case "/optimize+":
214 Optimize = true;
215 return true;
217 case "/o-":
218 case "/optimize-":
219 Optimize = false;
220 return true;
222 case "/incremental":
223 case "/incremental+":
224 case "/incremental-":
225 // nothing.
226 return true;
228 case "/d":
229 case "/define": {
230 if (value.Length == 0){
231 Usage ();
232 Environment.Exit (1);
235 foreach (string d in value.Split (argument_value_separator)){
236 if (defines.Length != 0)
237 defines.Append (";");
238 defines.Append (d);
241 return true;
244 case "/bugreport":
246 // We should collect data, runtime, etc and store in the file specified
248 return true;
249 case "/linkres":
250 case "/linkresource":
251 case "/res":
252 case "/resource":
253 bool embeded = arg [1] == 'r' || arg [1] == 'R';
254 string[] s = value.Split (argument_value_separator);
255 switch (s.Length) {
256 case 1:
257 if (s[0].Length == 0)
258 goto default;
259 embedded_resources [s[0]] = Path.GetFileName (s[0]);
260 break;
261 case 2:
262 embedded_resources [s [0]] = s [1];
263 break;
264 case 3:
265 Console.WriteLine ("Does not support this method yet: {0}", arg);
266 Environment.Exit (1);
267 break;
268 default:
269 Console.WriteLine ("Wrong number of arguments for option `{0}'", option);
270 Environment.Exit (1);
271 break;
275 return true;
277 case "/recurse":
278 Console.WriteLine ("/recurse not supported");
279 Environment.Exit (1);
280 return true;
282 case "/r":
283 case "/reference": {
284 if (value.Length == 0){
285 Console.WriteLine ("-reference requires an argument");
286 Environment.Exit (1);
289 string[] refs = value.Split (argument_value_separator);
290 foreach (string r in refs){
291 string val = r;
292 int index = val.IndexOf ('=');
293 if (index > -1) {
294 reference_aliases.Add (r);
295 continue;
298 if (val.Length != 0)
299 references.Add (val);
301 return true;
303 case "/main":
304 case "/m":
305 case "/addmodule":
306 case "/win32res":
307 case "/doc":
308 case "/lib":
310 Console.WriteLine ("{0} = not supported", arg);
311 throw new Exception ();
313 case "/win32icon": {
314 win32IconFile = value;
315 return true;
317 case "/debug-":
318 want_debugging_support = false;
319 return true;
321 case "/debug":
322 case "/debug+":
323 want_debugging_support = true;
324 return true;
326 case "/checked":
327 case "/checked+":
328 Checked = true;
329 return true;
331 case "/checked-":
332 Checked = false;
333 return true;
335 case "/clscheck":
336 case "/clscheck+":
337 return true;
339 case "/clscheck-":
340 VerifyClsCompliance = false;
341 return true;
343 case "/unsafe":
344 case "/unsafe+":
345 Unsafe = true;
346 return true;
348 case "/unsafe-":
349 Unsafe = false;
350 return true;
352 case "/warnaserror":
353 case "/warnaserror+":
354 if (value.Length == 0) {
355 WarningsAreErrors = true;
356 } else {
357 foreach (string wid in value.Split (argument_value_separator))
358 warning_as_error.Add (wid);
360 return true;
362 case "/warnaserror-":
363 if (value.Length == 0) {
364 WarningsAreErrors = false;
365 } else {
366 foreach (string wid in value.Split (argument_value_separator))
367 warning_as_error.Remove (wid);
369 return true;
371 case "/warn":
372 WarningLevel = Int32.Parse (value);
373 return true;
375 case "/nowarn": {
376 string [] warns;
378 if (value.Length == 0){
379 Console.WriteLine ("/nowarn requires an argument");
380 Environment.Exit (1);
383 warns = value.Split (argument_value_separator);
384 foreach (string wc in warns){
385 try {
386 if (wc.Trim ().Length == 0)
387 continue;
389 int warn = Int32.Parse (wc);
390 if (warn < 1) {
391 throw new ArgumentOutOfRangeException("warn");
393 ignore_warning.Add (warn);
394 } catch {
395 Console.WriteLine (String.Format("`{0}' is not a valid warning number", wc));
396 Environment.Exit (1);
399 return true;
402 case "/noconfig":
403 load_default_config = false;
404 return true;
406 case "/nostdlib":
407 case "/nostdlib+":
408 StdLib = false;
409 return true;
411 case "/nostdlib-":
412 StdLib = true;
413 return true;
415 case "/fullpaths":
416 return true;
418 case "/keyfile":
419 if (value == String.Empty) {
420 Console.WriteLine ("{0} requires an argument", arg);
421 Environment.Exit (1);
423 StrongNameKeyFile = value;
424 return true;
425 case "/keycontainer":
426 if (value == String.Empty) {
427 Console.WriteLine ("{0} requires an argument", arg);
428 Environment.Exit (1);
430 StrongNameKeyContainer = value;
431 return true;
432 case "/delaysign+":
433 StrongNameDelaySign = true;
434 return true;
435 case "/delaysign-":
436 StrongNameDelaySign = false;
437 return true;
439 case "/langversion":
440 switch (value.ToLower (CultureInfo.InvariantCulture)) {
441 case "iso-1":
442 Version = LanguageVersion.ISO_1;
443 return true;
445 case "default":
446 Version = LanguageVersion.Default;
447 return true;
448 case "iso-2":
449 Version = LanguageVersion.ISO_2;
450 return true;
451 case "future":
452 Version = LanguageVersion.Future;
453 return true;
455 Console.WriteLine ("Invalid option `{0}' for /langversion. It must be either `ISO-1', `ISO-2' or `Default'", value);
456 Environment.Exit (1);
457 return true;
459 case "/codepage":
460 CodePage = value;
461 return true;
464 return false;
467 static string [] LoadArgs (string file)
469 StreamReader f;
470 var args = new List<string> ();
471 string line;
472 try {
473 f = new StreamReader (file);
474 } catch {
475 return null;
478 StringBuilder sb = new StringBuilder ();
480 while ((line = f.ReadLine ()) != null){
481 int t = line.Length;
483 for (int i = 0; i < t; i++){
484 char c = line [i];
486 if (c == '"' || c == '\''){
487 char end = c;
489 for (i++; i < t; i++){
490 c = line [i];
492 if (c == end)
493 break;
494 sb.Append (c);
496 } else if (c == ' '){
497 if (sb.Length > 0){
498 args.Add (sb.ToString ());
499 sb.Length = 0;
501 } else
502 sb.Append (c);
504 if (sb.Length > 0){
505 args.Add (sb.ToString ());
506 sb.Length = 0;
510 string [] ret_value = new string [args.Count];
511 args.CopyTo (ret_value, 0);
513 return ret_value;
516 static string Load (string f)
518 if (File.Exists (f)){
519 using (var sr = new StreamReader (f)){
520 return sr.ReadToEnd ();
522 } else
523 return "";
526 public string Generate (XElement xproject)
528 string library = xproject.Attribute ("library").Value;
529 string boot, mcs, flags, output_name, built_sources, library_output, response;
531 boot = xproject.Element ("boot").Value;
532 mcs = xproject.Element ("mcs").Value;
533 flags = xproject.Element ("flags").Value;
534 output_name =xproject.Element ("output").Value;
535 built_sources = xproject.Element ("built_sources").Value;
536 library_output = xproject.Element ("library_output").Value;
537 response = xproject.Element ("response").Value;
540 // Prebuild code, might be in inputs, check:
541 // inputs/LIBRARY-PROFILE.pre
542 // inputs/LIBRARY.pre
544 string prebuild = Load (library + ".pre");
546 int q = library.IndexOf ("-");
547 if (q != -1)
548 prebuild = prebuild + Load (library.Substring (0, q) + ".pre");
550 var all_args = new Queue<string []> ();
551 all_args.Enqueue (flags.Split ());
552 while (all_args.Count > 0){
553 string [] f = all_args.Dequeue ();
555 for (int i = 0; i < f.Length; i++){
556 if (f [i][0] == '-')
557 f [i] = "/" + f [i].Substring (1);
559 if (f [i][0] == '@') {
560 string [] extra_args;
561 string response_file = f [i].Substring (1);
563 extra_args = LoadArgs (base_dir + "\\" + response_file);
564 if (extra_args == null) {
565 Console.WriteLine ("Unable to open response file: " + response_file);
566 Environment.Exit (1);
569 all_args.Enqueue (extra_args);
570 continue;
573 if (CSCParseOption (f [i], ref f))
574 continue;
575 Console.WriteLine ("Failure with {0}", f [i]);
576 Environment.Exit (1);
580 string [] source_files;
581 using (var reader = new StreamReader (base_dir + "\\" + response)){
582 source_files = reader.ReadToEnd ().Split ();
584 StringBuilder sources = new StringBuilder ();
585 foreach (string s in source_files){
586 if (s.Length == 0)
587 continue;
588 sources.Append (String.Format (" <Compile Include=\"{0}\" />\n", s.Replace ("/", "\\")));
590 foreach (string s in built_sources.Split ()){
591 if (s.Length == 0)
592 continue;
594 sources.Append (String.Format (" <Compile Include=\"{0}\" />\n", s.Replace ("/", "\\")));
598 // Compute the csc command that we need to use
600 // The mcs string is formatted like this:
601 // MONO_PATH=./../../class/lib/basic: /cvs/mono/runtime/mono-wrapper ./../../class/lib/basic/mcs.exe
603 // The first block is a set of MONO_PATHs, the last part is the compiler
605 if (mcs.StartsWith ("MONO_PATH="))
606 mcs = mcs.Substring (10);
608 var compiler = mcs.Substring (mcs.LastIndexOf (' ') + 1);
609 if (compiler.EndsWith ("class/lib/basic/gmcs.exe"))
610 compiler = "gmcs";
611 else if (compiler.EndsWith ("class/lib/net_2_0_bootstrap/gmcs.exe"))
612 compiler = "net_2_0_bootstrap";
613 else if (compiler.EndsWith ("mcs/gmcs.exe"))
614 compiler = "gmcs";
615 else if (compiler.EndsWith ("class/lib/moonlight_bootstrap/smcs.exe"))
616 compiler = "moonlight_bootstrap";
617 else if (compiler.EndsWith ("class/lib/moonlight_raw/smcs.exe"))
618 compiler = "moonlight_raw";
619 else if (compiler.EndsWith ("class/lib/net_4_0_bootstrap/dmcs.exe"))
620 compiler = "net_4_0_bootstrap";
621 else if (compiler.EndsWith ("class/lib/net_4_0/dmcs.exe"))
622 compiler = "dmcs";
623 else {
624 Console.WriteLine ("Can not determine compiler from {0}", compiler);
625 Environment.Exit (1);
628 var mono_paths = mcs.Substring (0, mcs.IndexOf (' ')).Split (new char [] {':'});
629 for (int i = 0; i < mono_paths.Length; i++){
630 int p = mono_paths [i].LastIndexOf ('/');
631 if (p != -1)
632 mono_paths [i] = mono_paths [i].Substring (p + 1);
635 var encoded_mono_paths = string.Join ("-", mono_paths).Replace ("--", "-");
636 var encoded_mp_compiler = (encoded_mono_paths + "-" + compiler).Replace ("--", "-");
638 string csc_tool_path = mcs_topdir + "..\\mono\\msvc\\scripts\\" + encoded_mp_compiler;
639 if (!Directory.Exists (encoded_mp_compiler)){
640 Console.WriteLine ("Created {0}", encoded_mp_compiler);
641 Directory.CreateDirectory (encoded_mp_compiler);
643 if (!File.Exists (Path.Combine (encoded_mp_compiler, "csc.exe"))){
644 File.Copy ("monowrap.exe", Path.Combine (encoded_mp_compiler, "csc.exe"));
645 File.Copy ("monowrap.pdb", Path.Combine (encoded_mp_compiler, "csc.pdb"));
648 var refs = new StringBuilder ();
650 // mcs is different that csc in this regard, somehow with -noconfig we still import System and System.XML
652 if (dir == "mcs" && !load_default_config){
653 references.Add ("System.dll");
654 references.Add ("System.Xml.dll");
657 if (references.Count > 0 || reference_aliases.Count > 0){
658 refs.Append ("<ItemGroup>\n");
659 string last = mono_paths [0].Substring (mono_paths [0].LastIndexOf ('/') + 1);
661 string hint_path = class_dir + "\\lib\\" + last;
663 foreach (string r in references){
664 refs.Append (" <Reference Include=\"" + r + "\">\n");
665 refs.Append (" <SpecificVersion>False</SpecificVersion>\n");
666 refs.Append (" <HintPath>" + hint_path + "\\" + r + "</HintPath>\n");
667 refs.Append (" </Reference>\n");
670 foreach (string r in reference_aliases){
671 int index = r.IndexOf ('=');
672 string alias = r.Substring (0, index);
673 string assembly = r.Substring (index + 1);
675 refs.Append (" <Reference Include=\"" + assembly + "\">\n");
676 refs.Append (" <SpecificVersion>False</SpecificVersion>\n");
677 refs.Append (" <HintPath>" + hint_path + "\\" + r + "</HintPath>\n");
678 refs.Append (" <Aliases>" + alias + "</Aliases>\n");
679 refs.Append (" </Reference>\n");
682 refs.Append (" </ItemGroup>\n");
685 try {
686 Path.GetDirectoryName (library_output);
687 } catch {
688 Console.WriteLine ("Error in path: {0} while processing {1}", library_output, library);
692 // Replace the template values
694 string output = template.
695 Replace ("@DEFINES@", defines.ToString ()).
696 Replace ("@NOSTDLIB@", StdLib ? "" : "<NoStdLib>true</NoStdLib>").
697 Replace ("@ALLOWUNSAFE@", Unsafe ? "<AllowUnsafeBlocks>true</AllowUnsafeBlocks>" : "").
698 Replace ("@ASSEMBLYNAME@", Path.GetFileNameWithoutExtension (output_name)).
699 Replace ("@OUTPUTDIR@", Path.GetDirectoryName (library_output)).
700 Replace ("@DEFINECONSTANTS@", defines.ToString ()).
701 Replace ("@CSCTOOLPATH@", csc_tool_path).
702 Replace ("@DEBUG@", want_debugging_support ? "true" : "false").
703 Replace ("@DEBUGTYPE@", want_debugging_support ? "full" : "pdbonly").
704 Replace ("@REFERENCES@", refs.ToString ()).
705 Replace ("@PREBUILD@", prebuild).
706 Replace ("@SOURCES@", sources.ToString ());
709 string ofile = "..\\..\\..\\mcs\\" + dir + "\\" + library + ".csproj";
710 ofile = ofile.Replace ('/', '\\');
711 //Console.WriteLine ("Generated {0}", ofile.Replace ("\\", "/"));
712 using (var o = new StreamWriter (ofile)){
713 o.WriteLine (output);
716 return ofile;
721 public class Driver {
723 static void Main (string [] args)
725 if (!File.Exists ("genproj.cs") || !File.Exists ("monowrap.cs")){
726 Console.WriteLine ("This command should be ran from mono/msvc/scripts");
727 Environment.Exit (1);
730 var sln_gen = new SlnGenerator ();
731 XDocument doc = XDocument.Load ("order.xml");
732 foreach (XElement project in doc.Root.Elements ()){
733 string dir = project.Attribute ("dir").Value;
734 string library = project.Attribute ("library").Value;
737 // Do only class libraries for now
739 if (!(dir.StartsWith ("class") || dir.StartsWith ("mcs")))
740 continue;
743 // Do not do 2.1, it is not working yet
744 // Do not do basic, as there is no point (requires a system mcs to be installed).
746 if (library.Contains ("moonlight") || library.Contains ("-basic"))
747 continue;
749 var gen = new MsbuildGenerator (dir);
750 try {
751 sln_gen.Add (gen.Generate (project));
752 } catch (Exception e) {
753 Console.WriteLine ("Error in {0}\n{1}", dir, e);
756 sln_gen.Write ("mcs_full.sln");