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