1 /* Compile a C# program.
2 Copyright (C) 2003-2024 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
= (const char **) xmalloca ((argc
+ 1) * sizeof (const 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
++ = 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
;
235 freea ((char *) argv
[i
]);
236 for (i
= 0; i
< sources_count
; i
++)
237 if (argv
[argc
- sources_count
+ i
] != sources
[i
])
238 freea ((char *) 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
= (const char **) xmalloca ((argc
+ 1) * sizeof (const char *));
327 *argp
++ = (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
++ = 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
, NULL
,
382 false, false, false, false,
385 for (i
= 2; i
< 3 + libdirs_count
+ libraries_count
; i
++)
386 freea ((char *) argv
[i
]);
387 for (i
= 0; i
< sources_count
; i
++)
388 if (argv
[argc
- sources_count
+ i
] != sources
[i
])
389 freea ((char *) argv
[argc
- sources_count
+ i
]);
392 return (exitstatus
!= 0);
399 compile_csharp_class (const char * const *sources
,
400 unsigned int sources_count
,
401 const char * const *libdirs
,
402 unsigned int libdirs_count
,
403 const char * const *libraries
,
404 unsigned int libraries_count
,
405 const char *output_file
,
406 bool optimize
, bool debug
,
409 bool output_is_library
=
410 (strlen (output_file
) >= 4
411 && memcmp (output_file
+ strlen (output_file
) - 4, ".dll", 4) == 0);
414 /* First try the C# implementation specified through --enable-csharp. */
415 #if CSHARP_CHOICE_MONO
416 result
= compile_csharp_using_mono (sources
, sources_count
,
417 libdirs
, libdirs_count
,
418 libraries
, libraries_count
,
419 output_file
, output_is_library
,
420 optimize
, debug
, verbose
);
422 return (bool) result
;
425 /* Then try the remaining C# implementations in our standard order. */
426 #if !CSHARP_CHOICE_MONO
427 result
= compile_csharp_using_mono (sources
, sources_count
,
428 libdirs
, libdirs_count
,
429 libraries
, libraries_count
,
430 output_file
, output_is_library
,
431 optimize
, debug
, verbose
);
433 return (bool) result
;
436 result
= compile_csharp_using_sscli (sources
, sources_count
,
437 libdirs
, libdirs_count
,
438 libraries
, libraries_count
,
439 output_file
, output_is_library
,
440 optimize
, debug
, verbose
);
442 return (bool) result
;
444 error (0, 0, _("C# compiler not found, try installing mono"));