root/maint/gnulib/lib/csharpcomp.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. compile_csharp_using_mono
  2. compile_csharp_using_sscli
  3. compile_csharp_class

   1 /* Compile a C# program.
   2    Copyright (C) 2003-2021 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 <https://www.gnu.org/licenses/>.  */
  17 
  18 #include <config.h>
  19 #include <alloca.h>
  20 
  21 /* Specification.  */
  22 #include "csharpcomp.h"
  23 
  24 #include <errno.h>
  25 #include <stdio.h>
  26 #include <stdlib.h>
  27 #include <string.h>
  28 
  29 #include "execute.h"
  30 #include "spawn-pipe.h"
  31 #include "wait-process.h"
  32 #include "sh-quote.h"
  33 #include "safe-read.h"
  34 #include "xmalloca.h"
  35 #include "error.h"
  36 #include "gettext.h"
  37 
  38 #define _(str) gettext (str)
  39 
  40 
  41 /* Survey of C# compilers.
  42 
  43    Program    from
  44 
  45    mcs        mono
  46    csc        sscli
  47 
  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
  56    option.
  57  */
  58 
  59 static int
  60 compile_csharp_using_mono (const char * const *sources,
     /* [previous][next][first][last][top][bottom][index][help] */
  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,
  68                            bool verbose)
  69 {
  70   static bool mcs_tested;
  71   static bool mcs_present;
  72 
  73   if (!mcs_tested)
  74     {
  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"  */
  79       const char *argv[3];
  80       pid_t child;
  81       int fd[1];
  82       int exitstatus;
  83 
  84       argv[0] = "mcs";
  85       argv[1] = "--version";
  86       argv[2] = NULL;
  87       child = create_pipe_in ("mcs", "mcs", argv, NULL,
  88                               DEV_NULL, true, true, false, fd);
  89       mcs_present = false;
  90       if (child != -1)
  91         {
  92           /* Read the subprocess output, and test whether it contains the
  93              string "Mono".  */
  94           char c[4];
  95           size_t count = 0;
  96 
  97           while (safe_read (fd[0], &c[count], 1) > 0)
  98             {
  99               count++;
 100               if (count == 4)
 101                 {
 102                   if (memcmp (c, "Mono", 4) == 0)
 103                     mcs_present = true;
 104                   c[0] = c[1]; c[1] = c[2]; c[2] = c[3];
 105                   count--;
 106                 }
 107             }
 108 
 109           close (fd[0]);
 110 
 111           /* Remove zombie process from process list, and retrieve exit
 112              status.  */
 113           exitstatus =
 114             wait_subprocess (child, "mcs", false, true, true, false, NULL);
 115           if (exitstatus != 0)
 116             mcs_present = false;
 117         }
 118       mcs_tested = true;
 119     }
 120 
 121   if (mcs_present)
 122     {
 123       unsigned int argc;
 124       const char **argv;
 125       const char **argp;
 126       pid_t child;
 127       int fd[1];
 128       FILE *fp;
 129       char *line[2];
 130       size_t linesize[2];
 131       size_t linelen[2];
 132       unsigned int l;
 133       int exitstatus;
 134       unsigned int i;
 135 
 136       argc =
 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 *));
 140 
 141       argp = argv;
 142       *argp++ = "mcs";
 143       if (output_is_library)
 144         *argp++ = "-target:library";
 145       {
 146         char *option = (char *) xmalloca (5 + strlen (output_file) + 1);
 147         memcpy (option, "-out:", 5);
 148         strcpy (option + 5, output_file);
 149         *argp++ = option;
 150       }
 151       for (i = 0; i < libdirs_count; i++)
 152         {
 153           char *option = (char *) xmalloca (5 + strlen (libdirs[i]) + 1);
 154           memcpy (option, "-lib:", 5);
 155           strcpy (option + 5, libdirs[i]);
 156           *argp++ = option;
 157         }
 158       for (i = 0; i < libraries_count; i++)
 159         {
 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");
 164           *argp++ = option;
 165         }
 166       if (debug)
 167         *argp++ = "-debug";
 168       for (i = 0; i < sources_count; i++)
 169         {
 170           const char *source_file = sources[i];
 171           if (strlen (source_file) >= 10
 172               && memcmp (source_file + strlen (source_file) - 10, ".resources",
 173                          10) == 0)
 174             {
 175               char *option = (char *) xmalloca (10 + strlen (source_file) + 1);
 176 
 177               memcpy (option, "-resource:", 10);
 178               strcpy (option + 10, source_file);
 179               *argp++ = option;
 180             }
 181           else
 182             *argp++ = source_file;
 183         }
 184       *argp = NULL;
 185       /* Ensure argv length was correctly calculated.  */
 186       if (argp - argv != argc)
 187         abort ();
 188 
 189       if (verbose)
 190         {
 191           char *command = shell_quote_argv (argv);
 192           printf ("%s\n", command);
 193           free (command);
 194         }
 195 
 196       child = create_pipe_in ("mcs", "mcs", argv, NULL,
 197                               NULL, false, true, true, fd);
 198 
 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");
 202       if (fp == NULL)
 203         error (EXIT_FAILURE, errno, _("fdopen() failed"));
 204       line[0] = NULL; linesize[0] = 0;
 205       line[1] = NULL; linesize[1] = 0;
 206       l = 0;
 207       for (;;)
 208         {
 209           linelen[l] = getline (&line[l], &linesize[l], fp);
 210           if (linelen[l] == (size_t)(-1))
 211             break;
 212           l = (l + 1) % 2;
 213           if (line[l] != NULL)
 214             fwrite (line[l], 1, linelen[l], stderr);
 215         }
 216       l = (l + 1) % 2;
 217       if (line[l] != NULL
 218           && !(linelen[l] >= 21
 219                && memcmp (line[l], "Compilation succeeded", 21) == 0))
 220         fwrite (line[l], 1, linelen[l], stderr);
 221       if (line[0] != NULL)
 222         free (line[0]);
 223       if (line[1] != NULL)
 224         free (line[1]);
 225       fclose (fp);
 226 
 227       /* Remove zombie process from process list, and retrieve exit status.  */
 228       exitstatus =
 229         wait_subprocess (child, "mcs", false, false, true, true, NULL);
 230 
 231       for (i = 1 + (output_is_library ? 1 : 0);
 232            i < 1 + (output_is_library ? 1 : 0)
 233                + 1 + libdirs_count + libraries_count;
 234            i++)
 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]);
 239       freea (argv);
 240 
 241       return (exitstatus != 0);
 242     }
 243   else
 244     return -1;
 245 }
 246 
 247 static int
 248 compile_csharp_using_sscli (const char * const *sources,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
 256                             bool verbose)
 257 {
 258   static bool csc_tested;
 259   static bool csc_present;
 260 
 261   if (!csc_tested)
 262     {
 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; }"  */
 266       const char *argv[3];
 267       pid_t child;
 268       int fd[1];
 269       int exitstatus;
 270 
 271       argv[0] = "csc";
 272       argv[1] = "-help";
 273       argv[2] = NULL;
 274       child = create_pipe_in ("csc", "csc", argv, NULL,
 275                               DEV_NULL, true, true, false, fd);
 276       csc_present = false;
 277       if (child != -1)
 278         {
 279           /* Read the subprocess output, and test whether it contains the
 280              string "chicken".  */
 281           char c[7];
 282           size_t count = 0;
 283 
 284           csc_present = true;
 285           while (safe_read (fd[0], &c[count], 1) > 0)
 286             {
 287               if (c[count] >= 'A' && c[count] <= 'Z')
 288                 c[count] += 'a' - 'A';
 289               count++;
 290               if (count == 7)
 291                 {
 292                   if (memcmp (c, "chicken", 7) == 0)
 293                     csc_present = false;
 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];
 296                   count--;
 297                 }
 298             }
 299 
 300           close (fd[0]);
 301 
 302           /* Remove zombie process from process list, and retrieve exit
 303              status.  */
 304           exitstatus =
 305             wait_subprocess (child, "csc", false, true, true, false, NULL);
 306           if (exitstatus != 0)
 307             csc_present = false;
 308         }
 309       csc_tested = true;
 310     }
 311 
 312   if (csc_present)
 313     {
 314       unsigned int argc;
 315       const char **argv;
 316       const char **argp;
 317       int exitstatus;
 318       unsigned int i;
 319 
 320       argc =
 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 *));
 324 
 325       argp = argv;
 326       *argp++ = "csc";
 327       *argp++ = (output_is_library ? "-target:library" : "-target:exe");
 328       {
 329         char *option = (char *) xmalloca (5 + strlen (output_file) + 1);
 330         memcpy (option, "-out:", 5);
 331         strcpy (option + 5, output_file);
 332         *argp++ = option;
 333       }
 334       for (i = 0; i < libdirs_count; i++)
 335         {
 336           char *option = (char *) xmalloca (5 + strlen (libdirs[i]) + 1);
 337           memcpy (option, "-lib:", 5);
 338           strcpy (option + 5, libdirs[i]);
 339           *argp++ = option;
 340         }
 341       for (i = 0; i < libraries_count; i++)
 342         {
 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");
 347           *argp++ = option;
 348         }
 349       if (optimize)
 350         *argp++ = "-optimize+";
 351       if (debug)
 352         *argp++ = "-debug+";
 353       for (i = 0; i < sources_count; i++)
 354         {
 355           const char *source_file = sources[i];
 356           if (strlen (source_file) >= 10
 357               && memcmp (source_file + strlen (source_file) - 10, ".resources",
 358                          10) == 0)
 359             {
 360               char *option = (char *) xmalloca (10 + strlen (source_file) + 1);
 361 
 362               memcpy (option, "-resource:", 10);
 363               strcpy (option + 10, source_file);
 364               *argp++ = option;
 365             }
 366           else
 367             *argp++ = source_file;
 368         }
 369       *argp = NULL;
 370       /* Ensure argv length was correctly calculated.  */
 371       if (argp - argv != argc)
 372         abort ();
 373 
 374       if (verbose)
 375         {
 376           char *command = shell_quote_argv (argv);
 377           printf ("%s\n", command);
 378           free (command);
 379         }
 380 
 381       exitstatus = execute ("csc", "csc", argv, NULL,
 382                             false, false, false, false,
 383                             true, true, NULL);
 384 
 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]);
 390       freea (argv);
 391 
 392       return (exitstatus != 0);
 393     }
 394   else
 395     return -1;
 396 }
 397 
 398 bool
 399 compile_csharp_class (const char * const *sources,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
 407                       bool verbose)
 408 {
 409   bool output_is_library =
 410     (strlen (output_file) >= 4
 411      && memcmp (output_file + strlen (output_file) - 4, ".dll", 4) == 0);
 412   int result;
 413 
 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);
 421   if (result >= 0)
 422     return (bool) result;
 423 #endif
 424 
 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);
 432   if (result >= 0)
 433     return (bool) result;
 434 #endif
 435 
 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);
 441   if (result >= 0)
 442     return (bool) result;
 443 
 444   error (0, 0, _("C# compiler not found, try installing mono"));
 445   return true;
 446 }

/* [previous][next][first][last][top][bottom][index][help] */