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 91static int 92execute_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 161static int 162execute_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 223static int 224execute_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 284bool 285execute_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} 347