Include <config.h> unconditionally.
[gnulib.git] / lib / csharpexec.c
1 /* Execute a C# program.
2    Copyright (C) 2003-2006 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2003.
4
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 2, or (at your option)
8    any later version.
9
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.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19 #include <config.h>
20 #include <alloca.h>
21
22 /* Specification.  */
23 #include "csharpexec.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #include "execute.h"
29 #include "sh-quote.h"
30 #include "xallocsa.h"
31 #include "error.h"
32 #include "gettext.h"
33
34 /* Handling of MONO_PATH is just like Java CLASSPATH.  */
35 #define CLASSPATHVAR "MONO_PATH"
36 #define new_classpath new_monopath
37 #define set_classpath set_monopath
38 #define reset_classpath reset_monopath
39 #include "classpath.h"
40 #include "classpath.c"
41 #undef reset_classpath
42 #undef set_classpath
43 #undef new_classpath
44 #undef CLASSPATHVAR
45
46 /* Handling of clix' PATH variable is just like Java CLASSPATH.  */
47 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__
48   /* Win32, Cygwin */
49   #define CLASSPATHVAR "PATH"
50 #elif defined __APPLE__ && defined __MACH__
51   /* MacOS X */
52   #define CLASSPATHVAR "DYLD_LIBRARY_PATH"
53 #else
54   /* Normal Unix */
55   #define CLASSPATHVAR "LD_LIBRARY_PATH"
56 #endif
57 #define new_classpath new_clixpath
58 #define set_classpath set_clixpath
59 #define reset_classpath reset_clixpath
60 #include "classpath.h"
61 #include "classpath.c"
62 #undef reset_classpath
63 #undef set_classpath
64 #undef new_classpath
65 #undef CLASSPATHVAR
66
67 #define _(str) gettext (str)
68
69
70 /* Survey of CIL interpreters.
71
72    Program    from
73
74    ilrun      pnet
75    mono       mono
76    clix       sscli
77
78    With Mono, the MONO_PATH is a colon separated list of pathnames. (On
79    Windows: semicolon separated list of pathnames.)
80
81    We try the CIL interpreters in the following order:
82      1. "ilrun", because it is a completely free system.
83      2. "mono", because it is a partially free system but doesn't integrate
84         well with Unix.
85      3. "clix", although it is not free, because it is a kind of "reference
86         implementation" of C#.
87    But the order can be changed through the --enable-csharp configuration
88    option.
89  */
90
91 static int
92 execute_csharp_using_pnet (const char *assembly_path,
93                            const char * const *libdirs,
94                            unsigned int libdirs_count,
95                            const char * const *args, unsigned int nargs,
96                            bool verbose, bool quiet,
97                            execute_fn *executer, void *private_data)
98 {
99   static bool ilrun_tested;
100   static bool ilrun_present;
101
102   if (!ilrun_tested)
103     {
104       /* Test for presence of ilrun:
105          "ilrun --version >/dev/null 2>/dev/null"  */
106       char *argv[3];
107       int exitstatus;
108
109       argv[0] = "ilrun";
110       argv[1] = "--version";
111       argv[2] = NULL;
112       exitstatus = execute ("ilrun", "ilrun", argv, false, false, true, true,
113                             true, false);
114       ilrun_present = (exitstatus == 0);
115       ilrun_tested = true;
116     }
117
118   if (ilrun_present)
119     {
120       unsigned int argc;
121       char **argv;
122       char **argp;
123       unsigned int i;
124       bool err;
125
126       argc = 1 + 2 * libdirs_count + 1 + nargs;
127       argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
128
129       argp = argv;
130       *argp++ = "ilrun";
131       for (i = 0; i < libdirs_count; i++)
132         {
133           *argp++ = "-L";
134           *argp++ = (char *) libdirs[i];
135         }
136       *argp++ = (char *) assembly_path;
137       for (i = 0; i < nargs; i++)
138         *argp++ = (char *) args[i];
139       *argp = NULL;
140       /* Ensure argv length was correctly calculated.  */
141       if (argp - argv != argc)
142         abort ();
143
144       if (verbose)
145         {
146           char *command = shell_quote_argv (argv);
147           printf ("%s\n", command);
148           free (command);
149         }
150
151       err = executer ("ilrun", "ilrun", argv, private_data);
152
153       freesa (argv);
154
155       return err;
156     }
157   else
158     return -1;
159 }
160
161 static int
162 execute_csharp_using_mono (const char *assembly_path,
163                            const char * const *libdirs,
164                            unsigned int libdirs_count,
165                            const char * const *args, unsigned int nargs,
166                            bool verbose, bool quiet,
167                            execute_fn *executer, void *private_data)
168 {
169   static bool mono_tested;
170   static bool mono_present;
171
172   if (!mono_tested)
173     {
174       /* Test for presence of mono:
175          "mono --version >/dev/null 2>/dev/null"  */
176       char *argv[3];
177       int exitstatus;
178
179       argv[0] = "mono";
180       argv[1] = "--version";
181       argv[2] = NULL;
182       exitstatus = execute ("mono", "mono", argv, false, false, true, true,
183                             true, false);
184       mono_present = (exitstatus == 0);
185       mono_tested = true;
186     }
187
188   if (mono_present)
189     {
190       char *old_monopath;
191       char **argv = (char **) xallocsa ((2 + nargs + 1) * sizeof (char *));
192       unsigned int i;
193       bool err;
194
195       /* Set MONO_PATH.  */
196       old_monopath = set_monopath (libdirs, libdirs_count, false, verbose);
197
198       argv[0] = "mono";
199       argv[1] = (char *) assembly_path;
200       for (i = 0; i <= nargs; i++)
201         argv[2 + i] = (char *) args[i];
202
203       if (verbose)
204         {
205           char *command = shell_quote_argv (argv);
206           printf ("%s\n", command);
207           free (command);
208         }
209
210       err = executer ("mono", "mono", argv, private_data);
211
212       /* Reset MONO_PATH.  */
213       reset_monopath (old_monopath);
214
215       freesa (argv);
216
217       return err;
218     }
219   else
220     return -1;
221 }
222
223 static int
224 execute_csharp_using_sscli (const char *assembly_path,
225                             const char * const *libdirs,
226                             unsigned int libdirs_count,
227                             const char * const *args, unsigned int nargs,
228                             bool verbose, bool quiet,
229                             execute_fn *executer, void *private_data)
230 {
231   static bool clix_tested;
232   static bool clix_present;
233
234   if (!clix_tested)
235     {
236       /* Test for presence of clix:
237          "clix >/dev/null 2>/dev/null ; test $? = 1"  */
238       char *argv[2];
239       int exitstatus;
240
241       argv[0] = "clix";
242       argv[1] = NULL;
243       exitstatus = execute ("clix", "clix", argv, false, false, true, true,
244                             true, false);
245       clix_present = (exitstatus == 0 || exitstatus == 1);
246       clix_tested = true;
247     }
248
249   if (clix_present)
250     {
251       char *old_clixpath;
252       char **argv = (char **) xallocsa ((2 + nargs + 1) * sizeof (char *));
253       unsigned int i;
254       bool err;
255
256       /* Set clix' PATH variable.  */
257       old_clixpath = set_clixpath (libdirs, libdirs_count, false, verbose);
258
259       argv[0] = "clix";
260       argv[1] = (char *) assembly_path;
261       for (i = 0; i <= nargs; i++)
262         argv[2 + i] = (char *) args[i];
263
264       if (verbose)
265         {
266           char *command = shell_quote_argv (argv);
267           printf ("%s\n", command);
268           free (command);
269         }
270
271       err = executer ("clix", "clix", argv, private_data);
272
273       /* Reset clix' PATH variable.  */
274       reset_clixpath (old_clixpath);
275
276       freesa (argv);
277
278       return err;
279     }
280   else
281     return -1;
282 }
283
284 bool
285 execute_csharp_program (const char *assembly_path,
286                         const char * const *libdirs,
287                         unsigned int libdirs_count,
288                         const char * const *args,
289                         bool verbose, bool quiet,
290                         execute_fn *executer, void *private_data)
291 {
292   unsigned int nargs;
293   int result;
294
295   /* Count args.  */
296   {
297     const char * const *arg;
298
299     for (nargs = 0, arg = args; *arg != NULL; nargs++, arg++)
300      ;
301   }
302
303   /* First try the C# implementation specified through --enable-csharp.  */
304 #if CSHARP_CHOICE_PNET
305   result = execute_csharp_using_pnet (assembly_path, libdirs, libdirs_count,
306                                       args, nargs, verbose, quiet,
307                                       executer, private_data);
308   if (result >= 0)
309     return (bool) result;
310 #endif
311
312 #if CSHARP_CHOICE_MONO
313   result = execute_csharp_using_mono (assembly_path, libdirs, libdirs_count,
314                                       args, nargs, verbose, quiet,
315                                       executer, private_data);
316   if (result >= 0)
317     return (bool) result;
318 #endif
319
320   /* Then try the remaining C# implementations in our standard order.  */
321 #if !CSHARP_CHOICE_PNET
322   result = execute_csharp_using_pnet (assembly_path, libdirs, libdirs_count,
323                                       args, nargs, verbose, quiet,
324                                       executer, private_data);
325   if (result >= 0)
326     return (bool) result;
327 #endif
328
329 #if !CSHARP_CHOICE_MONO
330   result = execute_csharp_using_mono (assembly_path, libdirs, libdirs_count,
331                                       args, nargs, verbose, quiet,
332                                       executer, private_data);
333   if (result >= 0)
334     return (bool) result;
335 #endif
336
337   result = execute_csharp_using_sscli (assembly_path, libdirs, libdirs_count,
338                                        args, nargs, verbose, quiet,
339                                        executer, private_data);
340   if (result >= 0)
341     return (bool) result;
342
343   if (!quiet)
344     error (0, 0, _("C# virtual machine not found, try installing pnet"));
345   return true;
346 }