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 }