Cthulhu  0.2.10
Cthulhu compiler collection
main.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-3.0-only
2 
3 #include "cmd.h"
4 
5 #include "base/util.h"
6 #include "config/config.h"
7 #include "setup/memory.h"
8 #include "format/colour.h"
9 
10 #include "cthulhu/broker/broker.h"
11 #include "cthulhu/events/events.h"
12 #include "cthulhu/check/check.h"
13 #include "io/console.h"
14 #include "arena/arena.h"
15 #include "format/notify.h"
16 #include "scan/node.h"
17 #include "std/map.h"
18 #include "std/str.h"
19 #include "std/vector.h"
20 
21 #include "argparse/argparse.h"
22 
23 #include "io/io.h"
24 
25 #include "fs/fs.h"
26 
27 #include "notify/notify.h"
28 
29 #include "cthulhu/ssa/ssa.h"
30 
31 #include "core/macros.h"
32 #include "support/loader.h"
33 #include "support/support.h"
34 
35 #include "backtrace/backtrace.h" // IWYU pragma: keep
36 #include "base/log.h" // IWYU pragma: keep
37 #include <stdio.h>
38 
39 static const frontend_t kFrontendInfo = {
40  .info = {
41  .id = "frontend/cli",
42  .name = "Cthulhu CLI",
43  .version = {
44  .license = "GPLv3",
45  .desc = "Cthulhu Compiler Collection CLI",
46  .author = "Elliot Haisley",
47  .version = CT_NEW_VERSION(0, 0, 3),
48  },
49  }
50 };
51 
52 static void parse_source(broker_t *broker, support_t *support, const char *path)
53 {
54  const node_t *node = broker_get_node(broker);
55  logger_t *reports = broker_get_logger(broker);
56  arena_t *arena = broker_get_arena(broker);
57  const char *ext = str_ext(path, arena);
58  if (ext == NULL)
59  {
60  msg_notify(reports, &kEvent_NoFileExtension, node,
61  "could not identify compiler for `%s` (no extension)", path);
62  return;
63  }
64 
65  language_runtime_t *lang = support_get_lang(support, ext);
66  if (lang == NULL)
67  {
68  const char *basepath = str_filename(path, arena);
69  event_builder_t id = msg_notify(reports, &kEvent_FailedToIdentifyLanguage, node,
70  "could not identify compiler for `%s` by extension `%s`", basepath,
71  ext);
72  msg_note(id, "extra extensions can be provided with -ext=id:ext");
73  return;
74  }
75 
76  io_t *io = io_file(path, eOsAccessRead, arena);
77  os_error_t err = io_error(io);
78  if (err != 0)
79  {
80  event_builder_t id = msg_notify(reports, &kEvent_FailedToOpenSourceFile, node,
81  "failed to open source `%s`", path);
82  msg_note(id, "error: %s", os_error_string(err, arena));
83  return;
84  }
85 
86  broker_parse(lang, io);
87 }
88 
89 static int check_reports(logger_t *logger, report_config_t config, const char *title)
90 {
91  int err = text_report(logger_get_events(logger), config, title);
92  logger_reset(logger);
93 
94  if (err != CT_EXIT_OK)
95  {
96  return err;
97  }
98 
99  return 0;
100 }
101 
102 typedef struct cli_t
103 {
108 } cli_t;
109 
110 static bool add_shared_module(cli_t *cli, const char *path, module_type_t type)
111 {
112  CT_UNUSED(path);
113  CT_UNUSED(type);
114 
115 #if CT_BUILD_SHARED
116  loaded_module_t mod = { 0 };
117 
118  if (!support_load_module(cli->support, eModLanguage, path, &mod))
119  {
120  msg_notify(cli->logger, &kEvent_ModuleConflict, broker_get_node(cli->broker), "failed to load module at `%s`", path);
121  return false;
122  }
123 
124  bt_update();
125 
126  ctu_log("loaded language module `%s`", path);
127  return true;
128 #else
129  msg_notify(cli->logger, &kEvent_DynamicLoadingDisabled, broker_get_node(cli->broker), "this distribution of cthulhu was not built with dynamic module loading");
130  return false;
131 #endif
132 }
133 
134 static bool on_add_plugin(ap_t *ap, const cfg_field_t *param, const void *value, void *data)
135 {
136  CT_UNUSED(ap);
137  CT_UNUSED(param);
138 
139  return add_shared_module(data, value, eModPlugin);
140 }
141 
142 static bool on_add_target(ap_t *ap, const cfg_field_t *param, const void *value, void *data)
143 {
144  CT_UNUSED(ap);
145  CT_UNUSED(param);
146 
147  return add_shared_module(data, value, eModTarget);
148 }
149 
150 static bool on_add_language(ap_t *ap, const cfg_field_t *param, const void *value, void *data)
151 {
152  CT_UNUSED(ap);
153  CT_UNUSED(param);
154 
155  return add_shared_module(data, value, eModLanguage);
156 }
157 
158 #define CHECK_LOG(logger, fmt) \
159  do \
160  { \
161  int err = check_reports(logger, report_config, fmt); \
162  if (err != CT_EXIT_OK) \
163  { \
164  return err; \
165  } \
166  } while (0)
167 
168 int main(int argc, const char **argv)
169 {
170  setup_default(NULL);
171 
172  arena_t *arena = ctu_default_alloc();
173  broker_t *broker = broker_new(&kFrontendInfo, arena);
174  loader_t *loader = loader_new(arena);
175  support_t *support = support_new(broker, loader, arena);
176 
178 
179  logger_t *reports = broker_get_logger(broker);
180  const node_t *node = broker_get_node(broker);
181  io_t *con = io_stdout();
182 
183  cli_t cli = {
184  .broker = broker,
185  .support = support,
186  .logger = reports,
187  .con = con
188  };
189 
190  tool_t tool = make_tool(kFrontendInfo.info.version, arena);
191 
192  ap_t *ap = tool.options.ap;
193 
194  ap_event(ap, tool.add_language, on_add_language, &cli);
195  ap_event(ap, tool.add_plugin, on_add_plugin, &cli);
196  ap_event(ap, tool.add_target, on_add_target, &cli);
197 
198  setup_init_t init = setup_parse(argc, argv, tool.options);
199  if (setup_should_exit(&init))
200  return setup_exit_code(&init);
201 
202  broker_init(broker);
203 
204  vector_t *paths = ap_get_posargs(ap);
205 
206  text_config_t text_config = {
207  .config = {
208  .zeroth_line = false,
209  },
210  .colours = &kColourDefault,
211  .io = con,
212  };
213 
214  report_config_t report_config = {
216  .text_config = text_config,
217 
218  .max_errors = cfg_int_value(tool.report_limit),
219  };
220 
221  CHECK_LOG(reports, "initializing");
222 
223  size_t total_sources = vector_len(paths);
224  if (total_sources == 0)
225  {
226  msg_notify(reports, &kEvent_NoSourceFiles, node, "no source files provided");
227  }
228 
229  CHECK_LOG(reports, "opening sources");
230 
231  for (size_t i = 0; i < total_sources; i++)
232  {
233  const char *path = vector_get(paths, i);
234  parse_source(broker, support, path);
235  }
236 
237  CHECK_LOG(reports, "parsing sources");
238 
239  for (size_t pass = 0; pass < ePassCount; pass++)
240  {
241  broker_run_pass(broker, pass);
242 
243  char *msg = str_format(arena, "running pass %s", broker_pass_name(pass));
244  CHECK_LOG(reports, msg);
245  }
246 
247  broker_resolve(broker);
248  CHECK_LOG(reports, "compiling sources");
249 
250  vector_t *mods = broker_get_modules(broker);
251  check_tree(reports, mods, arena);
252  CHECK_LOG(reports, "checking tree");
253 
254  ssa_result_t ssa = ssa_compile(mods, arena);
255  CHECK_LOG(reports, "compiling ssa");
256 
257  ssa_opt(reports, ssa, arena);
258  CHECK_LOG(reports, "optimizing ssa");
259 
260  // fs_t *fs = fs_virtual("out", arena);
261 
262  const char *target_output = cfg_string_value(tool.output_target);
263  if (str_equal(target_output, "auto"))
264  target_output = "cfamily";
265 
266  const char *output_dir = cfg_string_value(tool.output_dir);
267 
268  target_runtime_t *target = support_get_target(support, target_output);
269  if (target == NULL)
270  {
271  msg_notify(reports, &kEvent_InvalidTarget, node,
272  "could not identify target `%s`", target_output);
273  }
274  CHECK_LOG(reports, "querying target");
275 
276  fs_t *out = fs_physical(output_dir, arena);
277  if (out == NULL)
278  {
279  msg_notify(reports, &kEvent_FailedToCreateOutputDirectory, node,
280  "failed to create output directory `%s`", output_dir);
281  }
282 
283  CHECK_LOG(reports, "creating output directory");
284 
285  target_emit_t emit = {
287  .fs = out,
288  };
289 
290  target_emit_ssa(target, &ssa, &emit);
291  CHECK_LOG(reports, "emitting target ssa");
292 
293 #if 0
294  emit_options_t base_emit_options = {
295  .arena = arena,
296  .reports = reports,
297  .fs = fs,
298 
299  .modules = ssa.modules,
300  .deps = ssa.deps,
301  };
302 
303  if (cfg_bool_value(tool.emit_ssa))
304  {
305  ssa_emit_options_t emit_options = {.opts = base_emit_options};
306 
307  emit_ssa(&emit_options);
308  CHECK_LOG(reports, "emitting ssa");
309  }
310 
311  c89_emit_options_t c89_emit_options = {
312  .opts = base_emit_options,
313  };
314  c89_emit_result_t c89_emit_result = emit_c89(&c89_emit_options);
315  CT_UNUSED(c89_emit_result); // TODO: check for errors
316  CHECK_LOG(reports, "emitting c89");
317 #endif
318 
319  // sync_result_t result = fs_sync(out, fs);
320  // if (result.path != NULL)
321  // {
322  // msg_notify(reports, &kEvent_FailedToWriteOutputFile, node, "failed to sync %s",
323  // result.path);
324  // }
325 
326  // CHECK_LOG(reports, "writing output files");
327 }
tool_t make_tool(version_info_t version, arena_t *arena)
Definition: cmd.c:121
CT_NODISCARD STA_RET_STRING CT_OS_API char * os_error_string(os_error_t error, arena_t *arena)
convert an os error code to a string
Definition: os.c:56
int main(int argc, const char **argv)
Definition: main.c:168
#define CHECK_LOG(logger, fmt)
Definition: main.c:158
CT_ARGPARSE_API void ap_event(ap_t *self, const cfg_field_t *param, ap_event_t callback, void *data)
add a callback event to a parameter
Definition: argparse.c:139
CT_ARGPARSE_API vector_t * ap_get_posargs(ap_t *self)
get all positional arguments
Definition: argparse.c:158
CT_BACKTRACE_API void bt_update(void)
update the loaded module cache
Definition: dbghelp.c:147
CT_NODISCARD CT_PUREFN CT_BASE_API bool str_equal(const char *lhs, const char *rhs)
compare strings equality
Definition: util.c:76
CT_BROKER_API const node_t * broker_get_node(broker_t *broker)
Definition: broker.c:333
CT_BROKER_API emit_result_t target_emit_ssa(target_runtime_t *runtime, const ssa_result_t *ssa, target_emit_t *emit)
Definition: broker.c:579
CT_BROKER_API void broker_parse(language_runtime_t *runtime, io_t *io)
Definition: broker.c:376
CT_BROKER_API void broker_run_pass(broker_t *broker, broker_pass_t pass)
Definition: broker.c:419
CT_BROKER_API void broker_resolve(broker_t *broker)
Definition: broker.c:450
CT_BROKER_API broker_t * broker_new(const frontend_t *frontend, arena_t *arena)
Definition: broker.c:130
CT_CONSTFN CT_BROKER_API const char * broker_pass_name(broker_pass_t pass)
extra stuff
Definition: broker.c:597
CT_BROKER_API void broker_init(broker_t *broker)
Definition: broker.c:265
CT_BROKER_API vector_t * broker_get_modules(broker_t *broker)
get all the modules in the broker this does not include the root module
Definition: broker.c:362
CT_BROKER_API arena_t * broker_get_arena(broker_t *broker)
Definition: broker.c:341
CT_BROKER_API logger_t * broker_get_logger(broker_t *broker)
Definition: broker.c:325
@ ePassCount
Definition: broker.h:60
CT_BEGIN_API CT_CHECK_API void check_tree(logger_t *reports, vector_t *mods, arena_t *arena)
check the tree form IR all found errors are reported to the reports object
Definition: tree.c:1071
CT_FORMAT_API const colour_pallete_t kColourDefault
a colour pallete that applies ANSI VT100 colours
Definition: colour.c:24
CT_PUREFN CT_CONFIG_API const char * cfg_string_value(const cfg_field_t *field)
get the current string value of a configuration field
Definition: reflect.c:151
CT_PUREFN CT_CONFIG_API bool cfg_bool_value(const cfg_field_t *field)
get the current boolean value of a configuration field
Definition: reflect.c:143
CT_PUREFN CT_CONFIG_API int cfg_int_value(const cfg_field_t *field)
get the current integer value of a configuration field
Definition: reflect.c:135
CT_PUREFN CT_CONFIG_API size_t cfg_enum_value(const cfg_field_t *field)
get the current enum value of a configuration field
Definition: reflect.c:167
#define CT_NEW_VERSION(major, minor, patch)
creates a new ctu_version_t from major, minor and patch
Definition: version_def.h:20
#define CT_EXIT_OK
no user errors or internal errors
Definition: macros.h:91
CT_NODISCARD CT_FS_API fs_t * fs_physical(const char *root, arena_t *arena)
create a filesystem interface to a physical location on disk
Definition: physical.c:215
CT_NODISCARD CT_IO_API io_t * io_file(const char *path, os_access_t mode, arena_t *arena)
create an IO object from a file
Definition: file.c:125
CT_NODISCARD CT_IO_API os_error_t io_error(const io_t *io)
get the last error from the io object
Definition: io.c:144
CT_IO_API io_t * io_stdout(void)
get the global stdout IO object
Definition: console.c:60
CT_SUPPORT_API language_runtime_t * support_get_lang(support_t *support, const char *ext)
Definition: support.c:176
CT_SUPPORT_API void support_load_default_modules(support_t *support)
load all default modules
Definition: support.c:139
CT_SUPPORT_API bool support_load_module(support_t *support, module_type_t mask, const char *name, loaded_module_t *out)
Definition: support.c:153
CT_SUPPORT_API target_runtime_t * support_get_target(support_t *support, const char *name)
Definition: support.c:192
CT_SUPPORT_API support_t * support_new(broker_t *broker, loader_t *loader, arena_t *arena)
create a support instance from an existing loader and broker configures the broker with the modules i...
Definition: support.c:113
CT_BASE_API void ctu_log(STA_FORMAT_STRING const char *fmt,...)
log a message
#define CT_UNUSED(x)
mark a variable as unused
Definition: macros.h:46
CT_NOTIFY_API event_builder_t msg_notify(INOUT_NOTNULL logger_t *logs, const diagnostic_t *diagnostic, const node_t *node, STA_FORMAT_STRING const char *fmt,...)
notify the logger of a new message
CT_NOTIFY_API void logger_reset(logger_t *logs)
reset the loggers messages
Definition: notify.c:89
CT_NOTIFY_API void msg_note(event_builder_t builder, STA_FORMAT_STRING const char *fmt,...)
add a note to an existing message
CT_NODISCARD CT_NOTIFY_API typevec_t * logger_get_events(const logger_t *logs)
get the events from the logger
Definition: notify.c:36
CT_SETUP_API setup_init_t setup_parse(int argc, const char **argv, setup_options_t setup)
parse the command line
Definition: setup.c:366
CT_PUREFN CT_SETUP_API bool setup_should_exit(const setup_init_t *init)
accessor functions
Definition: setup.c:434
CT_SETUP_API void setup_default(arena_t *arena)
initialise the runtime with default options
Definition: setup.c:188
CT_BEGIN_API CT_SETUP_API arena_t * ctu_default_alloc(void)
get the default allocator
Definition: memory.c:43
CT_PUREFN CT_SETUP_API int setup_exit_code(const setup_init_t *init)
get the exit code
Definition: setup.c:442
CT_SSA_API void ssa_opt(logger_t *reports, ssa_result_t mod, arena_t *arena)
Optimize a given module.
Definition: opt.c:359
CT_SSA_API ssa_result_t ssa_compile(vector_t *mods, arena_t *arena)
compile a set of trees into their ssa form
Definition: ssa.c:911
CT_NODISCARD CT_STD_API char * str_filename(const char *path, arena_t *arena)
get the filename from a path
Definition: str.c:218
CT_NODISCARD CT_STD_API char * str_ext(const char *path, arena_t *arena)
get the last file extension from a path
Definition: str.c:173
CT_NODISCARD CT_PUREFN CT_STD_API void * vector_get(const vector_t *vector, size_t index)
get a value from a vector
Definition: vector.c:134
CT_NODISCARD CT_PUREFN CT_STD_API size_t vector_len(const vector_t *vector)
get the length of a vector
Definition: vector.c:152
CT_SUPPORT_API loader_t * loader_new(arena_t *arena)
Definition: loader.c:41
module_type_t
Definition: loader.h:32
STA_DECL char * str_format(arena_t *arena, const char *fmt,...)
Definition: str.c:97
argparse instance
Definition: common.h:26
an allocator object
Definition: arena.h:86
Definition: main.c:103
support_t * support
Definition: main.c:105
logger_t * logger
Definition: main.c:106
broker_t * broker
Definition: main.c:104
io_t * con
Definition: main.c:107
an event builder handles adding additional information to an event
Definition: notify.h:68
bool zeroth_line
the zeroth line of a file is the first line
Definition: notify.h:51
the frontend running the mediator
Definition: broker.h:251
module_info_t info
information about the frontend
Definition: broker.h:253
Definition: common.h:72
io object implementation
Definition: impl.h:122
Definition: common.h:8
a logging sink
Definition: notify.c:14
version_info_t version
the version of the module
Definition: broker.h:107
const char * id
unique id for the module
Definition: broker.h:101
a position in a source file
Definition: node.h:23
text_format_t report_format
Definition: notify.h:92
the result of parsing the command line
Definition: setup.h:69
ap_t * ap
Definition: setup.h:34
vector_t * modules
Definition: ssa.h:363
map_t * deps
Definition: ssa.h:364
file_layout_t layout
Definition: broker.h:309
file_config_t config
Definition: notify.h:75
Definition: cmd.h:14
cfg_field_t * add_language
Definition: cmd.h:17
cfg_field_t * output_layout
Definition: cmd.h:24
cfg_field_t * emit_ssa
Definition: cmd.h:22
cfg_field_t * report_limit
Definition: cmd.h:28
cfg_field_t * add_plugin
Definition: cmd.h:18
cfg_field_t * output_dir
Definition: cmd.h:23
cfg_field_t * add_target
Definition: cmd.h:19
setup_options_t options
Definition: cmd.h:31
cfg_field_t * report_style
Definition: cmd.h:29
cfg_field_t * output_target
Definition: cmd.h:25
a generic vector of pointers
Definition: vector.c:16
CT_FORMAT_API int text_report(typevec_t *events, report_config_t config, const char *title)
Definition: common_extra.c:416