1 /* Compile a C# program.
2 Copyright (C) 2003-2019 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
, DEV_NULL
, true, true, false,
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
, false, true, true, fd
);
198 /* Read the subprocess output, copying it to stderr. Drop the last
199 line if it starts with "Compilation succeeded". */
200 fp
= fdopen (fd
[0], "r");
202 error (EXIT_FAILURE
, errno
, _("fdopen() failed"));
203 line
[0] = NULL
; linesize
[0] = 0;
204 line
[1] = NULL
; linesize
[1] = 0;
208 linelen
[l
] = getline (&line
[l
], &linesize
[l
], fp
);
209 if (linelen
[l
] == (size_t)(-1))
213 fwrite (line
[l
], 1, linelen
[l
], stderr
);
217 && !(linelen
[l
] >= 21
218 && memcmp (line
[l
], "Compilation succeeded", 21) == 0))
219 fwrite (line
[l
], 1, linelen
[l
], stderr
);
226 /* Remove zombie process from process list, and retrieve exit status. */
228 wait_subprocess (child
, "mcs", false, false, true, true, NULL
);
230 for (i
= 1 + (output_is_library
? 1 : 0);
231 i
< 1 + (output_is_library
? 1 : 0)
232 + 1 + libdirs_count
+ libraries_count
;
235 for (i
= 0; i
< sources_count
; i
++)
236 if (argv
[argc
- sources_count
+ i
] != sources
[i
])
237 freea (argv
[argc
- sources_count
+ i
]);
240 return (exitstatus
!= 0);
247 compile_csharp_using_sscli (const char * const *sources
,
248 unsigned int sources_count
,
249 const char * const *libdirs
,
250 unsigned int libdirs_count
,
251 const char * const *libraries
,
252 unsigned int libraries_count
,
253 const char *output_file
, bool output_is_library
,
254 bool optimize
, bool debug
,
257 static bool csc_tested
;
258 static bool csc_present
;
262 /* Test for presence of csc:
263 "csc -help >/dev/null 2>/dev/null \
264 && ! { csc -help 2>/dev/null | grep -i chicken > /dev/null; }" */
273 child
= create_pipe_in ("csc", "csc", argv
, DEV_NULL
, true, true, false,
278 /* Read the subprocess output, and test whether it contains the
284 while (safe_read (fd
[0], &c
[count
], 1) > 0)
286 if (c
[count
] >= 'A' && c
[count
] <= 'Z')
287 c
[count
] += 'a' - 'A';
291 if (memcmp (c
, "chicken", 7) == 0)
293 c
[0] = c
[1]; c
[1] = c
[2]; c
[2] = c
[3];
294 c
[3] = c
[4]; c
[4] = c
[5]; c
[5] = c
[6];
301 /* Remove zombie process from process list, and retrieve exit
304 wait_subprocess (child
, "csc", false, true, true, false, NULL
);
320 1 + 1 + 1 + libdirs_count
+ libraries_count
321 + (optimize
? 1 : 0) + (debug
? 1 : 0) + sources_count
;
322 argv
= (char **) xmalloca ((argc
+ 1) * sizeof (char *));
327 (char *) (output_is_library
? "-target:library" : "-target:exe");
329 char *option
= (char *) xmalloca (5 + strlen (output_file
) + 1);
330 memcpy (option
, "-out:", 5);
331 strcpy (option
+ 5, output_file
);
334 for (i
= 0; i
< libdirs_count
; i
++)
336 char *option
= (char *) xmalloca (5 + strlen (libdirs
[i
]) + 1);
337 memcpy (option
, "-lib:", 5);
338 strcpy (option
+ 5, libdirs
[i
]);
341 for (i
= 0; i
< libraries_count
; i
++)
343 char *option
= (char *) xmalloca (11 + strlen (libraries
[i
]) + 4 + 1);
344 memcpy (option
, "-reference:", 11);
345 memcpy (option
+ 11, libraries
[i
], strlen (libraries
[i
]));
346 strcpy (option
+ 11 + strlen (libraries
[i
]), ".dll");
350 *argp
++ = "-optimize+";
353 for (i
= 0; i
< sources_count
; i
++)
355 const char *source_file
= sources
[i
];
356 if (strlen (source_file
) >= 10
357 && memcmp (source_file
+ strlen (source_file
) - 10, ".resources",
360 char *option
= (char *) xmalloca (10 + strlen (source_file
) + 1);
362 memcpy (option
, "-resource:", 10);
363 strcpy (option
+ 10, source_file
);
367 *argp
++ = (char *) source_file
;
370 /* Ensure argv length was correctly calculated. */
371 if (argp
- argv
!= argc
)
376 char *command
= shell_quote_argv (argv
);
377 printf ("%s\n", command
);
381 exitstatus
= execute ("csc", "csc", argv
, false, false, false, false,
384 for (i
= 2; i
< 3 + libdirs_count
+ libraries_count
; i
++)
386 for (i
= 0; i
< sources_count
; i
++)
387 if (argv
[argc
- sources_count
+ i
] != sources
[i
])
388 freea (argv
[argc
- sources_count
+ i
]);
391 return (exitstatus
!= 0);
398 compile_csharp_class (const char * const *sources
,
399 unsigned int sources_count
,
400 const char * const *libdirs
,
401 unsigned int libdirs_count
,
402 const char * const *libraries
,
403 unsigned int libraries_count
,
404 const char *output_file
,
405 bool optimize
, bool debug
,
408 bool output_is_library
=
409 (strlen (output_file
) >= 4
410 && memcmp (output_file
+ strlen (output_file
) - 4, ".dll", 4) == 0);
413 /* First try the C# implementation specified through --enable-csharp. */
414 #if CSHARP_CHOICE_MONO
415 result
= compile_csharp_using_mono (sources
, sources_count
,
416 libdirs
, libdirs_count
,
417 libraries
, libraries_count
,
418 output_file
, output_is_library
,
419 optimize
, debug
, verbose
);
421 return (bool) result
;
424 /* Then try the remaining C# implementations in our standard order. */
425 #if !CSHARP_CHOICE_MONO
426 result
= compile_csharp_using_mono (sources
, sources_count
,
427 libdirs
, libdirs_count
,
428 libraries
, libraries_count
,
429 output_file
, output_is_library
,
430 optimize
, debug
, verbose
);
432 return (bool) result
;
435 result
= compile_csharp_using_sscli (sources
, sources_count
,
436 libdirs
, libdirs_count
,
437 libraries
, libraries_count
,
438 output_file
, output_is_library
,
439 optimize
, debug
, verbose
);
441 return (bool) result
;
443 error (0, 0, _("C# compiler not found, try installing mono"));