1 /* Compile a C# program.
2 Copyright (C) 2003-2020 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
22 #include "csharpcomp.h"
30 #include "spawn-pipe.h"
31 #include "wait-process.h"
33 #include "safe-read.h"
38 #define _(str) gettext (str)
41 /* Survey of C# compilers.
48 We try the CIL interpreters in the following order:
49 1. "mcs", because it is a free system but doesn't integrate so well
50 with Unix. (Command line options start with / instead of -. Errors go
51 to stdout instead of stderr. Source references are printed as
52 "file(lineno)" instead of "file:lineno:".)
53 2. "csc", although it is not free, because it is a kind of "reference
54 implementation" of C#.
55 But the order can be changed through the --enable-csharp configuration
60 compile_csharp_using_mono (const char * const *sources
,
61 unsigned int sources_count
,
62 const char * const *libdirs
,
63 unsigned int libdirs_count
,
64 const char * const *libraries
,
65 unsigned int libraries_count
,
66 const char *output_file
, bool output_is_library
,
67 bool optimize
, bool debug
,
70 static bool mcs_tested
;
71 static bool mcs_present
;
75 /* Test for presence of mcs:
76 "mcs --version >/dev/null 2>/dev/null"
77 and (to exclude an unrelated 'mcs' program on QNX 6)
78 "mcs --version 2>/dev/null | grep Mono >/dev/null" */
85 argv
[1] = "--version";
87 child
= create_pipe_in ("mcs", "mcs", argv
, NULL
,
88 DEV_NULL
, true, true, false, fd
);
92 /* Read the subprocess output, and test whether it contains the
97 while (safe_read (fd
[0], &c
[count
], 1) > 0)
102 if (memcmp (c
, "Mono", 4) == 0)
104 c
[0] = c
[1]; c
[1] = c
[2]; c
[2] = c
[3];
111 /* Remove zombie process from process list, and retrieve exit
114 wait_subprocess (child
, "mcs", false, true, true, false, NULL
);
137 1 + (output_is_library
? 1 : 0) + 1 + libdirs_count
+ libraries_count
138 + (debug
? 1 : 0) + sources_count
;
139 argv
= (char **) xmalloca ((argc
+ 1) * sizeof (char *));
143 if (output_is_library
)
144 *argp
++ = "-target:library";
146 char *option
= (char *) xmalloca (5 + strlen (output_file
) + 1);
147 memcpy (option
, "-out:", 5);
148 strcpy (option
+ 5, output_file
);
151 for (i
= 0; i
< libdirs_count
; i
++)
153 char *option
= (char *) xmalloca (5 + strlen (libdirs
[i
]) + 1);
154 memcpy (option
, "-lib:", 5);
155 strcpy (option
+ 5, libdirs
[i
]);
158 for (i
= 0; i
< libraries_count
; i
++)
160 char *option
= (char *) xmalloca (11 + strlen (libraries
[i
]) + 4 + 1);
161 memcpy (option
, "-reference:", 11);
162 memcpy (option
+ 11, libraries
[i
], strlen (libraries
[i
]));
163 strcpy (option
+ 11 + strlen (libraries
[i
]), ".dll");
168 for (i
= 0; i
< sources_count
; i
++)
170 const char *source_file
= sources
[i
];
171 if (strlen (source_file
) >= 10
172 && memcmp (source_file
+ strlen (source_file
) - 10, ".resources",
175 char *option
= (char *) xmalloca (10 + strlen (source_file
) + 1);
177 memcpy (option
, "-resource:", 10);
178 strcpy (option
+ 10, source_file
);
182 *argp
++ = (char *) source_file
;
185 /* Ensure argv length was correctly calculated. */
186 if (argp
- argv
!= argc
)
191 char *command
= shell_quote_argv (argv
);
192 printf ("%s\n", command
);
196 child
= create_pipe_in ("mcs", "mcs", argv
, NULL
,
197 NULL
, false, true, true, fd
);
199 /* Read the subprocess output, copying it to stderr. Drop the last
200 line if it starts with "Compilation succeeded". */
201 fp
= fdopen (fd
[0], "r");
203 error (EXIT_FAILURE
, errno
, _("fdopen() failed"));
204 line
[0] = NULL
; linesize
[0] = 0;
205 line
[1] = NULL
; linesize
[1] = 0;
209 linelen
[l
] = getline (&line
[l
], &linesize
[l
], fp
);
210 if (linelen
[l
] == (size_t)(-1))
214 fwrite (line
[l
], 1, linelen
[l
], stderr
);
218 && !(linelen
[l
] >= 21
219 && memcmp (line
[l
], "Compilation succeeded", 21) == 0))
220 fwrite (line
[l
], 1, linelen
[l
], stderr
);
227 /* Remove zombie process from process list, and retrieve exit status. */
229 wait_subprocess (child
, "mcs", false, false, true, true, NULL
);
231 for (i
= 1 + (output_is_library
? 1 : 0);
232 i
< 1 + (output_is_library
? 1 : 0)
233 + 1 + libdirs_count
+ libraries_count
;
236 for (i
= 0; i
< sources_count
; i
++)
237 if (argv
[argc
- sources_count
+ i
] != sources
[i
])
238 freea (argv
[argc
- sources_count
+ i
]);
241 return (exitstatus
!= 0);
248 compile_csharp_using_sscli (const char * const *sources
,
249 unsigned int sources_count
,
250 const char * const *libdirs
,
251 unsigned int libdirs_count
,
252 const char * const *libraries
,
253 unsigned int libraries_count
,
254 const char *output_file
, bool output_is_library
,
255 bool optimize
, bool debug
,
258 static bool csc_tested
;
259 static bool csc_present
;
263 /* Test for presence of csc:
264 "csc -help >/dev/null 2>/dev/null \
265 && ! { csc -help 2>/dev/null | grep -i chicken > /dev/null; }" */
274 child
= create_pipe_in ("csc", "csc", argv
, NULL
,
275 DEV_NULL
, true, true, false, fd
);
279 /* Read the subprocess output, and test whether it contains the
285 while (safe_read (fd
[0], &c
[count
], 1) > 0)
287 if (c
[count
] >= 'A' && c
[count
] <= 'Z')
288 c
[count
] += 'a' - 'A';
292 if (memcmp (c
, "chicken", 7) == 0)
294 c
[0] = c
[1]; c
[1] = c
[2]; c
[2] = c
[3];
295 c
[3] = c
[4]; c
[4] = c
[5]; c
[5] = c
[6];
302 /* Remove zombie process from process list, and retrieve exit
305 wait_subprocess (child
, "csc", false, true, true, false, NULL
);
321 1 + 1 + 1 + libdirs_count
+ libraries_count
322 + (optimize
? 1 : 0) + (debug
? 1 : 0) + sources_count
;
323 argv
= (char **) xmalloca ((argc
+ 1) * sizeof (char *));
328 (char *) (output_is_library
? "-target:library" : "-target:exe");
330 char *option
= (char *) xmalloca (5 + strlen (output_file
) + 1);
331 memcpy (option
, "-out:", 5);
332 strcpy (option
+ 5, output_file
);
335 for (i
= 0; i
< libdirs_count
; i
++)
337 char *option
= (char *) xmalloca (5 + strlen (libdirs
[i
]) + 1);
338 memcpy (option
, "-lib:", 5);
339 strcpy (option
+ 5, libdirs
[i
]);
342 for (i
= 0; i
< libraries_count
; i
++)
344 char *option
= (char *) xmalloca (11 + strlen (libraries
[i
]) + 4 + 1);
345 memcpy (option
, "-reference:", 11);
346 memcpy (option
+ 11, libraries
[i
], strlen (libraries
[i
]));
347 strcpy (option
+ 11 + strlen (libraries
[i
]), ".dll");
351 *argp
++ = "-optimize+";
354 for (i
= 0; i
< sources_count
; i
++)
356 const char *source_file
= sources
[i
];
357 if (strlen (source_file
) >= 10
358 && memcmp (source_file
+ strlen (source_file
) - 10, ".resources",
361 char *option
= (char *) xmalloca (10 + strlen (source_file
) + 1);
363 memcpy (option
, "-resource:", 10);
364 strcpy (option
+ 10, source_file
);
368 *argp
++ = (char *) source_file
;
371 /* Ensure argv length was correctly calculated. */
372 if (argp
- argv
!= argc
)
377 char *command
= shell_quote_argv (argv
);
378 printf ("%s\n", command
);
382 exitstatus
= execute ("csc", "csc", argv
, NULL
,
383 false, false, false, false,
386 for (i
= 2; i
< 3 + libdirs_count
+ libraries_count
; i
++)
388 for (i
= 0; i
< sources_count
; i
++)
389 if (argv
[argc
- sources_count
+ i
] != sources
[i
])
390 freea (argv
[argc
- sources_count
+ i
]);
393 return (exitstatus
!= 0);
400 compile_csharp_class (const char * const *sources
,
401 unsigned int sources_count
,
402 const char * const *libdirs
,
403 unsigned int libdirs_count
,
404 const char * const *libraries
,
405 unsigned int libraries_count
,
406 const char *output_file
,
407 bool optimize
, bool debug
,
410 bool output_is_library
=
411 (strlen (output_file
) >= 4
412 && memcmp (output_file
+ strlen (output_file
) - 4, ".dll", 4) == 0);
415 /* First try the C# implementation specified through --enable-csharp. */
416 #if CSHARP_CHOICE_MONO
417 result
= compile_csharp_using_mono (sources
, sources_count
,
418 libdirs
, libdirs_count
,
419 libraries
, libraries_count
,
420 output_file
, output_is_library
,
421 optimize
, debug
, verbose
);
423 return (bool) result
;
426 /* Then try the remaining C# implementations in our standard order. */
427 #if !CSHARP_CHOICE_MONO
428 result
= compile_csharp_using_mono (sources
, sources_count
,
429 libdirs
, libdirs_count
,
430 libraries
, libraries_count
,
431 output_file
, output_is_library
,
432 optimize
, debug
, verbose
);
434 return (bool) result
;
437 result
= compile_csharp_using_sscli (sources
, sources_count
,
438 libdirs
, libdirs_count
,
439 libraries
, libraries_count
,
440 output_file
, output_is_library
,
441 optimize
, debug
, verbose
);
443 return (bool) result
;
445 error (0, 0, _("C# compiler not found, try installing mono"));