plugin-driver.c

     
   1  //! @file plugin-driver.c
   2  //! @author J. Marcel van der Veer
   3  
   4  //! @section Copyright
   5  //!
   6  //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
   7  //! Copyright 2001-2023 J. Marcel van der Veer [algol68g@xs4all.nl].
   8  
   9  //! @section License
  10  //!
  11  //! This program is free software; you can redistribute it and/or modify it 
  12  //! under the terms of the GNU General Public License as published by the 
  13  //! Free Software Foundation; either version 3 of the License, or 
  14  //! (at your option) any later version.
  15  //!
  16  //! This program is distributed in the hope that it will be useful, but 
  17  //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  18  //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
  19  //! more details. You should have received a copy of the GNU General Public 
  20  //! License along with this program. If not, see [http://www.gnu.org/licenses/].
  21  
  22  //! @section Synopsis
  23  //!
  24  //! Plugin compiler driver.
  25  
  26  #include "a68g.h"
  27  
  28  #include "a68g-optimiser.h"
  29  #include "a68g-options.h"
  30  #include "a68g-parser.h"
  31  #include "a68g-plugin.h"
  32  #include "a68g-genie.h"
  33  
  34  //! @brief Emit code for plugin compiler.
  35  
  36  void plugin_driver_code (void)
  37  {
  38    if (ERROR_COUNT (&A68_JOB) == 0 && OPTION_OPT_LEVEL (&A68_JOB) > NO_OPTIMISE) {
  39      announce_phase ("plugin code generator");
  40      int num = 0;
  41      renumber_nodes (TOP_NODE (&A68_JOB), &num);
  42      A68 (node_register) = (NODE_T **) get_heap_space ((size_t) num * sizeof (NODE_T));
  43      ABEND (A68 (node_register) == NO_VAR, ERROR_ACTION, __func__);
  44      register_nodes (TOP_NODE (&A68_JOB));
  45      FILE_OBJECT_FD (&A68_JOB) = open (FILE_OBJECT_NAME (&A68_JOB), O_WRONLY | O_CREAT | O_TRUNC, A68_PROTECTION);
  46      ABEND (FILE_OBJECT_FD (&A68_JOB) == -1, ERROR_ACTION, FILE_OBJECT_NAME (&A68_JOB));
  47      FILE_OBJECT_OPENED (&A68_JOB) = A68_TRUE;
  48      plugin_driver_emit (FILE_OBJECT_FD (&A68_JOB));
  49      ASSERT (close (FILE_OBJECT_FD (&A68_JOB)) == 0);
  50      FILE_OBJECT_OPENED (&A68_JOB) = A68_FALSE;
  51    }
  52  }
  53  
  54  //! @brief Compile emitted code.
  55  
  56  void plugin_driver_compile (void)
  57  {
  58  #if defined (BUILD_A68_COMPILER)
  59  // Compilation on Linux, BSD.
  60  // Build shared library using gcc or clang.
  61  // TODO: One day this should be all portable between platforms.
  62  // Only compile if the A68 compiler found no errors (constant folder for instance).
  63    if (ERROR_COUNT (&A68_JOB) == 0 && OPTION_OPT_LEVEL (&A68_JOB) > 0 && !OPTION_RUN_SCRIPT (&A68_JOB)) {
  64      BUFFER cmd, options;
  65      BUFCLR (cmd);
  66      BUFCLR (options);
  67      if (OPTION_RERUN (&A68_JOB) == A68_FALSE) {
  68        announce_phase ("plugin compiler");
  69        errno = 0;
  70        ASSERT (snprintf (options, SNPRINTF_SIZE, "%s %s", optimisation_option (), A68_GCC_OPTIONS) >= 0);
  71  #if defined (HAVE_PIC)
  72        bufcat (options, " ", BUFFER_SIZE);
  73        bufcat (options, HAVE_PIC, BUFFER_SIZE);
  74  #endif
  75        ASSERT (snprintf (cmd, SNPRINTF_SIZE, "%s -I%s %s -c -o \"%s\" \"%s\"", C_COMPILER, INCLUDEDIR, options, FILE_BINARY_NAME (&A68_JOB), FILE_OBJECT_NAME (&A68_JOB)) >= 0);
  76        ABEND (system (cmd) != 0, ERROR_ACTION, cmd);
  77        ASSERT (snprintf (cmd, SNPRINTF_SIZE, "ld -export-dynamic -shared -o \"%s\" \"%s\"", FILE_PLUGIN_NAME (&A68_JOB), FILE_BINARY_NAME (&A68_JOB)) >= 0);
  78        ABEND (system (cmd) != 0, ERROR_ACTION, cmd);
  79        a68_rm (FILE_BINARY_NAME (&A68_JOB));
  80      }
  81    }
  82  #endif
  83  }
  84  
  85  //! @brief Start interpreter with compiled plugin.
  86  
  87  void plugin_driver_genie (void)
  88  {
  89  #if defined (BUILD_A68_COMPILER)
  90    if (OPTION_RUN_SCRIPT (&A68_JOB)) {
  91      rewrite_script_source ();
  92    }
  93    void *compile_plugin = NULL;
  94    if (OPTION_OPT_LEVEL (&A68_JOB) > 0) {
  95      char plugin_name[BUFFER_SIZE];
  96      void *a68_plugin;
  97      struct stat srcstat, objstat;
  98      int ret;
  99      announce_phase ("plugin dynamic linker");
 100      ASSERT (snprintf (plugin_name, SNPRINTF_SIZE, "%s", FILE_PLUGIN_NAME (&A68_JOB)) >= 0);
 101  // Correction when pwd is outside LD_PLUGIN_PATH.
 102  // The DL cannot be loaded if it is.
 103      if (strcmp (plugin_name, a68_basename (plugin_name)) == 0) {
 104        ASSERT (snprintf (plugin_name, SNPRINTF_SIZE, "./%s", FILE_PLUGIN_NAME (&A68_JOB)) >= 0);
 105      }
 106  // Check whether we are doing something rash.
 107      ret = stat (FILE_SOURCE_NAME (&A68_JOB), &srcstat);
 108      ABEND (ret != 0, ERROR_ACTION, FILE_SOURCE_NAME (&A68_JOB));
 109      ret = stat (plugin_name, &objstat);
 110      ABEND (ret != 0, ERROR_ACTION, plugin_name);
 111      if (OPTION_RERUN (&A68_JOB)) {
 112        ABEND (ST_MTIME (&srcstat) > ST_MTIME (&objstat), "plugin outdates source", "cannot RERUN");
 113      }
 114  // First load a68g itself so compiler code can resolve a68g symbols.
 115      a68_plugin = dlopen (NULL, RTLD_NOW | RTLD_GLOBAL);
 116      ABEND (a68_plugin == NULL, ERROR_CANNOT_OPEN_PLUGIN, dlerror ());
 117  // Then load compiler code.
 118      compile_plugin = dlopen (plugin_name, RTLD_NOW | RTLD_GLOBAL);
 119      ABEND (compile_plugin == NULL, ERROR_CANNOT_OPEN_PLUGIN, dlerror ());
 120    }
 121    genie (compile_plugin);
 122  // Unload compiler plugin.
 123    if (compile_plugin != (void *) NULL) {
 124      int ret = dlclose (compile_plugin);
 125      ABEND (ret != 0, ERROR_ACTION, dlerror ());
 126    }
 127  #endif
 128  }
 129  
 130  //! @brief Clean files from plugin compiler.
 131  
 132  void plugin_driver_clean (int emitted)
 133  {
 134  #if defined (BUILD_A68_COMPILER)
 135    announce_phase ("clean up intermediate files");
 136    if (OPTION_OPT_LEVEL (&A68_JOB) >= OPTIMISE_0 && OPTION_REGRESSION_TEST (&A68_JOB) && !OPTION_KEEP (&A68_JOB)) {
 137      if (emitted) {
 138        a68_rm (FILE_OBJECT_NAME (&A68_JOB));
 139      }
 140      a68_rm (FILE_PLUGIN_NAME (&A68_JOB));
 141    }
 142    if (OPTION_RUN_SCRIPT (&A68_JOB) && !OPTION_KEEP (&A68_JOB)) {
 143      if (emitted) {
 144        a68_rm (FILE_OBJECT_NAME (&A68_JOB));
 145      }
 146      a68_rm (FILE_SOURCE_NAME (&A68_JOB));
 147      a68_rm (FILE_PLUGIN_NAME (&A68_JOB));
 148    } else if (OPTION_COMPILE (&A68_JOB)) {
 149      build_script ();
 150      if (!OPTION_KEEP (&A68_JOB)) {
 151        if (emitted) {
 152          a68_rm (FILE_OBJECT_NAME (&A68_JOB));
 153        }
 154        a68_rm (FILE_PLUGIN_NAME (&A68_JOB));
 155      }
 156    } else if (OPTION_OPT_LEVEL (&A68_JOB) == OPTIMISE_0 && !OPTION_KEEP (&A68_JOB)) {
 157      if (emitted) {
 158        a68_rm (FILE_OBJECT_NAME (&A68_JOB));
 159      }
 160      a68_rm (FILE_PLUGIN_NAME (&A68_JOB));
 161    } else if (OPTION_OPT_LEVEL (&A68_JOB) > OPTIMISE_0 && !OPTION_KEEP (&A68_JOB)) {
 162      if (emitted) {
 163        a68_rm (FILE_OBJECT_NAME (&A68_JOB));
 164      }
 165    } else if (OPTION_RERUN (&A68_JOB) && !OPTION_KEEP (&A68_JOB)) {
 166      if (emitted) {
 167        a68_rm (FILE_OBJECT_NAME (&A68_JOB));
 168      }
 169    }
 170  #else
 171    (void) emitted;
 172  #endif
 173  }