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