(root)/
gettext-0.22.4/
gettext-tools/
gnulib-lib/
csharpcomp.c
       1  /* Compile a C# program.
       2     Copyright (C) 2003-2023 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,
      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,
     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,
     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  }