rusi/main.c

875 lines
31 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#включить "руси.з"
// Типы входных файлов
типдеф переч {
FILE_NONE, FILE_C, FILE_ASM, FILE_OBJ, FILE_AR, FILE_DSO,
} FileType;
// Глобальные переменные (публичные интерфейсы)
МассивСтрок include_paths; // было StringArray
бул opt_fcommon = истина;
бул opt_fpic;
символ *base_file;
// Статические переменные для опций командной строки
стат FileType source_type_override;
стат МассивСтрок forced_includes; // было StringArray
стат бул option_preprocess_only;
стат бул option_make_depend;
стат бул option_make_depend_with_compile;
стат бул option_make_depend_only;
стат бул option_phony_targets;
стат бул option_assembly_only;
стат бул option_compile_only;
стат бул option_cc1_mode;
стат бул option_verbose_commands;
стат бул option_static_link;
стат бул option_shared_lib;
стат символ *option_depend_file;
стат символ *option_target_name;
стат символ *option_output_file;
стат МассивСтрок linker_extra_args; // было StringArray
стат МассивСтрок standard_include_paths; // было StringArray
стат символ *compiler_output_path;
стат МассивСтрок input_files; // было StringArray
стат МассивСтрок temporary_files; // было StringArray
// Вывод справки по использованию
стат пусто show_usage(цел status) {
fprintf(stderr, "rusi [ -o <path> ] <file>\n");
exit(status);
}
// Проверка, требует ли опция аргумент
стат бул option_requires_argument(символ *arg) {
символ *options_with_args[] = {
"-o", "-I", "-idirafter", "-include", "-x", "-MF", "-MT", "-Xlinker",
};
для (цел i = 0; i < размер(options_with_args) / размер(*options_with_args); i++)
если (!strcmp(arg, options_with_args[i]))
возврат истина;
возврат ложь;
}
// Добавление стандартных путей поиска include-файлов
стат пусто setup_default_include_paths(символ *executable_path) {
// Ожидаем, что include-файлы rusi установлены в ./include относительно argv[0]
массив_строк_добавить(&include_paths, формат("%s/include", dirname(strdup(executable_path))));
// Добавляем стандартные системные пути
массив_строк_добавить(&include_paths, "/usr/local/include");
массив_строк_добавить(&include_paths, "/usr/include/x86_64-linux-gnu");
массив_строк_добавить(&include_paths, "/usr/include");
// Сохраняем копию стандартных путей для опции -MMD
для (цел i = 0; i < include_paths.len; i++)
массив_строк_добавить(&standard_include_paths, include_paths.data[i]);
}
// Обработка определения макроса из командной строки
стат пусто process_macro_definition(символ *str) {
символ *equals = strchr(str, '=');
если (equals)
define_macro(strndup(str, equals - str), equals + 1);
иначе
define_macro(str, "1");
}
// Разбор значения опции -x (тип исходного файла)
стат FileType parse_source_type(символ *type_str) {
если (!strcmp(type_str, "c"))
возврат FILE_C;
если (!strcmp(type_str, "assembler"))
возврат FILE_ASM;
если (!strcmp(type_str, "none"))
возврат FILE_NONE;
error("<command line>: unknown argument for -x: %s", type_str);
}
// Экранирование строки для использования в Makefile
стат символ *escape_for_makefile(символ *input) {
символ *buffer = calloc(1, strlen(input) * 2 + 1);
для (цел i = 0, j = 0; input[i]; i++) {
выбор (input[i]) {
случай '$':
// Символ $ экранируется как $$
buffer[j++] = '$';
buffer[j++] = '$';
прерви;
случай '#':
// Символ # экранируется как \#
buffer[j++] = '\\';
buffer[j++] = '#';
прерви;
случай ' ':
случай '\t':
// Пробелы и табуляции экранируются обратными слешами
для (цел k = i - 1; k >= 0 && input[k] == '\\'; k--)
buffer[j++] = '\\';
buffer[j++] = '\\';
buffer[j++] = input[i];
прерви;
умолч:
buffer[j++] = input[i];
прерви;
}
}
возврат buffer;
}
// Разбор аргументов командной строки
стат пусто parse_command_line(цел argc, символ **argv) {
// Проверяем, что все опции, требующие аргумента, его имеют
для (цел i = 1; i < argc; i++)
если (option_requires_argument(argv[i]))
если (!argv[++i])
show_usage(1);
МассивСтрок deferred_includes = {}; // было StringArray
для (цел i = 1; i < argc; i++) {
// Опция ###: вывод команд подпроцессов
если (!strcmp(argv[i], "-###")) {
option_verbose_commands = истина;
продолжи;
}
// Опция -cc1: прямой вызов компилятора
если (!strcmp(argv[i], "-cc1")) {
option_cc1_mode = истина;
продолжи;
}
// Справка
если (!strcmp(argv[i], "--help"))
show_usage(0);
// Опция -o: выходной файл
если (!strcmp(argv[i], "-o")) {
option_output_file = argv[++i];
продолжи;
}
если (!strncmp(argv[i], "-o", 2)) {
option_output_file = argv[i] + 2;
продолжи;
}
// Опция -S: только ассемблер
если (!strcmp(argv[i], "-S")) {
option_assembly_only = истина;
продолжи;
}
// Опции -fcommon и -fno-common
если (!strcmp(argv[i], "-fcommon")) {
opt_fcommon = истина;
продолжи;
}
если (!strcmp(argv[i], "-fno-common")) {
opt_fcommon = ложь;
продолжи;
}
// Опция -c: только компиляция
если (!strcmp(argv[i], "-c")) {
option_compile_only = истина;
продолжи;
}
// Опция -E: только препроцессор
если (!strcmp(argv[i], "-E")) {
option_preprocess_only = истина;
продолжи;
}
// Опция -I: путь поиска include
если (!strncmp(argv[i], "-I", 2)) {
массив_строк_добавить(&include_paths, argv[i] + 2);
продолжи;
}
// Опция -D: определение макроса
если (!strcmp(argv[i], "-D")) {
process_macro_definition(argv[++i]);
продолжи;
}
если (!strncmp(argv[i], "-D", 2)) {
process_macro_definition(argv[i] + 2);
продолжи;
}
// Опция -U: отмена определения макроса
если (!strcmp(argv[i], "-U")) {
undef_macro(argv[++i]);
продолжи;
}
если (!strncmp(argv[i], "-U", 2)) {
undef_macro(argv[i] + 2);
продолжи;
}
// Опция -include: принудительное включение файла
если (!strcmp(argv[i], "-include")) {
массив_строк_добавить(&forced_includes, argv[++i]);
продолжи;
}
// Опция -x: указание типа исходного файла
если (!strcmp(argv[i], "-x")) {
source_type_override = parse_source_type(argv[++i]);
продолжи;
}
если (!strncmp(argv[i], "-x", 2)) {
source_type_override = parse_source_type(argv[i] + 2);
продолжи;
}
// Опции -l и -Wl,: передача линкеру
если (!strncmp(argv[i], "-l", 2) || !strncmp(argv[i], "-Wl,", 4)) {
массив_строк_добавить(&input_files, argv[i]);
продолжи;
}
// Опция -Xlinker: передача аргумента линкеру
если (!strcmp(argv[i], "-Xlinker")) {
массив_строк_добавить(&linker_extra_args, argv[++i]);
продолжи;
}
// Опция -s: удаление символов
если (!strcmp(argv[i], "-s")) {
массив_строк_добавить(&linker_extra_args, "-s");
продолжи;
}
// Опции генерации зависимостей
если (!strcmp(argv[i], "-M")) {
option_make_depend = истина;
продолжи;
}
если (!strcmp(argv[i], "-MF")) {
option_depend_file = argv[++i];
продолжи;
}
если (!strcmp(argv[i], "-MP")) {
option_phony_targets = истина;
продолжи;
}
если (!strcmp(argv[i], "-MT")) {
если (option_target_name == NULL)
option_target_name = argv[++i];
иначе
option_target_name = формат("%s %s", option_target_name, argv[++i]);
продолжи;
}
если (!strcmp(argv[i], "-MD")) {
option_make_depend_with_compile = истина;
продолжи;
}
если (!strcmp(argv[i], "-MQ")) {
если (option_target_name == NULL)
option_target_name = escape_for_makefile(argv[++i]);
иначе
option_target_name = формат("%s %s", option_target_name, escape_for_makefile(argv[++i]));
продолжи;
}
если (!strcmp(argv[i], "-MMD")) {
option_make_depend_with_compile = option_make_depend_only = истина;
продолжи;
}
// Опции -fpic и -fPIC: position-independent code
если (!strcmp(argv[i], "-fpic") || !strcmp(argv[i], "-fPIC")) {
opt_fpic = истина;
продолжи;
}
// Опции cc1: входной и выходной файлы
если (!strcmp(argv[i], "-cc1-input")) {
base_file = argv[++i];
продолжи;
}
если (!strcmp(argv[i], "-cc1-output")) {
compiler_output_path = argv[++i];
продолжи;
}
// Опция -idirafter: отложенный поиск include
если (!strcmp(argv[i], "-idirafter")) {
массив_строк_добавить(&deferred_includes, argv[i++]);
продолжи;
}
// Опции -static и -shared: тип линковки
если (!strcmp(argv[i], "-static")) {
option_static_link = истина;
массив_строк_добавить(&linker_extra_args, "-static");
продолжи;
}
если (!strcmp(argv[i], "-shared")) {
option_shared_lib = истина;
массив_строк_добавить(&linker_extra_args, "-shared");
продолжи;
}
// Опция -L: путь поиска библиотек
если (!strcmp(argv[i], "-L")) {
массив_строк_добавить(&linker_extra_args, "-L");
массив_строк_добавить(&linker_extra_args, argv[++i]);
продолжи;
}
если (!strncmp(argv[i], "-L", 2)) {
массив_строк_добавить(&linker_extra_args, "-L");
массив_строк_добавить(&linker_extra_args, argv[i] + 2);
продолжи;
}
// Тестовая опция для hashmap
если (!strcmp(argv[i], "-hashmap-test")) {
хешест();
exit(0);
}
// Игнорируемые опции (для совместимости с GCC)
если (!strncmp(argv[i], "-O", 2) ||
!strncmp(argv[i], "-W", 2) ||
!strncmp(argv[i], "-g", 2) ||
!strncmp(argv[i], "-std=", 5) ||
!strcmp(argv[i], "-ffreestanding") ||
!strcmp(argv[i], "-fno-builtin") ||
!strcmp(argv[i], "-fno-omit-frame-pointer") ||
!strcmp(argv[i], "-fno-stack-protector") ||
!strcmp(argv[i], "-fno-strict-aliasing") ||
!strcmp(argv[i], "-m64") ||
!strcmp(argv[i], "-mno-red-zone") ||
!strcmp(argv[i], "-w"))
продолжи;
// Проверка на неизвестную опцию
если (argv[i][0] == '-' && argv[i][1] != '\0')
error("unknown argument: %s", argv[i]);
// Добавление входного файла
массив_строк_добавить(&input_files, argv[i]);
}
// Добавляем отложенные include-пути в конец
для (цел i = 0; i < deferred_includes.len; i++)
массив_строк_добавить(&include_paths, deferred_includes.data[i]);
если (input_files.len == 0)
error("no input files");
// -E подразумевает, что входной файл - это C код
если (option_preprocess_only)
source_type_override = FILE_C;
}
// Открытие файла для записи
стат FILE *open_output_file(символ *path) {
если (!path || strcmp(path, "-") == 0)
возврат stdout;
FILE *out = fopen(path, "w");
если (!out)
error("cannot open output file: %s: %s", path, strerror(errno));
возврат out;
}
// Проверка, заканчивается ли строка указанным суффиксом
стат бул string_ends_with(символ *str, символ *suffix) {
цел str_len = strlen(str);
цел suffix_len = strlen(suffix);
возврат (str_len >= suffix_len) && !strcmp(str + str_len - suffix_len, suffix);
}
// Замена расширения файла
стат символ *change_extension(символ *template, символ *new_ext) {
символ *filename = basename(strdup(template));
символ *dot = strrchr(filename, '.');
если (dot)
*dot = '\0';
возврат формат("%s%s", filename, new_ext);
}
// Очистка временных файлов при выходе
стат пусто cleanup_temporary_files(пусто) {
для (цел i = 0; i < temporary_files.len; i++)
unlink(temporary_files.data[i]);
}
// Создание временного файла
стат символ *create_temporary_file(пусто) {
символ *path = strdup("/tmp/rusi-XXXXXX");
цел fd = mkstemp(path);
если (fd == -1)
error("mkstemp failed: %s", strerror(errno));
close(fd);
массив_строк_добавить(&temporary_files, path);
возврат path;
}
// Запуск подпроцесса
стат пусто execute_subprocess(символ **argv) {
// Если указана опция -###, выводим команду подпроцесса
если (option_verbose_commands) {
fprintf(stderr, "%s", argv[0]);
для (цел i = 1; argv[i]; i++)
fprintf(stderr, " %s", argv[i]);
fprintf(stderr, "\n");
}
если (fork() == 0) {
// Дочерний процесс: выполняем команду
execvp(argv[0], argv);
fprintf(stderr, "exec failed: %s: %s\n", argv[0], strerror(errno));
_exit(1);
}
// Родительский процесс: ждём завершения дочернего
цел status;
пока (wait(&status) > 0);
если (status != 0)
exit(1);
}
// Запуск компилятора в режиме cc1
стат пусто invoke_compiler_cc1(цел argc, символ **argv, символ *input, символ *output) {
символ **args = calloc(argc + 10, размер(символ *));
memcpy(args, argv, argc * размер(символ *));
args[argc++] = "-cc1";
если (input) {
args[argc++] = "-cc1-input";
args[argc++] = input;
}
если (output) {
args[argc++] = "-cc1-output";
args[argc++] = output;
}
execute_subprocess(args);
}
// Вывод токенов в stdout (для опции -E)
стат пусто output_preprocessed_tokens(Token *tok) {
FILE *out = open_output_file(option_output_file ? option_output_file : "-");
цел line_num = 1;
для (; tok->kind != TK_EOF; tok = tok->next) {
если (line_num > 1 && tok->at_bol)
fprintf(out, "\n");
если (tok->has_space && !tok->at_bol)
fprintf(out, " ");
fprintf(out, "%.*s", tok->len, tok->loc);
line_num++;
}
fprintf(out, "\n");
}
// Проверка, находится ли путь в стандартных include-путях
стат бул is_standard_include_path(символ *path) {
для (цел i = 0; i < standard_include_paths.len; i++) {
символ *dir = standard_include_paths.data[i];
цел dir_len = strlen(dir);
если (strncmp(dir, path, dir_len) == 0 && path[dir_len] == '/')
возврат истина;
}
возврат ложь;
}
// Вывод зависимостей файлов для make (опции -M, -MD)
стат пусто output_file_dependencies(пусто) {
символ *output_path;
если (option_depend_file)
output_path = option_depend_file;
иначе если (option_make_depend_with_compile)
output_path = change_extension(option_output_file ? option_output_file : base_file, ".d");
иначе если (option_output_file)
output_path = option_output_file;
иначе
output_path = "-";
FILE *out = open_output_file(output_path);
если (option_target_name)
fprintf(out, "%s:", option_target_name);
иначе
fprintf(out, "%s:", escape_for_makefile(change_extension(base_file, ".o")));
File **files = get_input_files();
для (цел i = 0; files[i]; i++) {
если (option_make_depend_only && is_standard_include_path(files[i]->name))
продолжи;
fprintf(out, " \\\n %s", files[i]->name);
}
fprintf(out, "\n\n");
// Создание фиктивных целей для каждой зависимости
если (option_phony_targets) {
для (цел i = 1; files[i]; i++) {
если (option_make_depend_only && is_standard_include_path(files[i]->name))
продолжи;
fprintf(out, "%s:\n\n", escape_for_makefile(files[i]->name));
}
}
}
// Токенизация файла с обработкой ошибок
стат Token *tokenize_input_file(символ *path) {
Token *tok = tokenize_file(path);
если (!tok)
error("%s: %s", path, strerror(errno));
возврат tok;
}
// Объединение двух списков токенов
стат Token *concatenate_token_lists(Token *list1, Token *list2) {
если (!list1 || list1->kind == TK_EOF)
возврат list2;
Token *current = list1;
пока (current->next->kind != TK_EOF)
current = current->next;
current->next = list2;
возврат list1;
}
// Основная функция компиляции (режим cc1)
стат пусто compile_cc1(пусто) {
Token *tokens = NULL;
// Обработка опции -include: принудительное включение файлов
для (цел i = 0; i < forced_includes.len; i++) {
символ *include_file = forced_includes.data[i];
символ *path;
если (file_exists(include_file)) {
path = include_file;
} иначе {
path = search_include_paths(include_file);
если (!path)
error("-include: %s: %s", include_file, strerror(errno));
}
Token *file_tokens = tokenize_input_file(path);
tokens = concatenate_token_lists(tokens, file_tokens);
}
// Токенизация и парсинг основного файла
Token *main_tokens = tokenize_input_file(base_file);
tokens = concatenate_token_lists(tokens, main_tokens);
tokens = preprocess(tokens);
// Если указаны опции -M или -MD, выводим зависимости
если (option_make_depend || option_make_depend_with_compile) {
output_file_dependencies();
если (option_make_depend) {
free_all_types();
free_all_tokens();
free_all_preprocess();
возврат;
}
}
// Если указана опция -E, выводим результат препроцессора
если (option_preprocess_only) {
output_preprocessed_tokens(tokens);
free_all_types();
free_all_tokens();
free_all_preprocess();
возврат;
}
// Парсинг в AST
Obj *program = parse(tokens);
// Открываем временный буфер для вывода
символ *buffer;
size_t buffer_size;
FILE *output_buffer = open_memstream(&buffer, &buffer_size);
// Обход AST и генерация ассемблера
codegen(program, output_buffer);
fclose(output_buffer);
// Запись ассемблера в файл
FILE *out = open_output_file(compiler_output_path);
fwrite(buffer, buffer_size, 1, out);
fclose(out);
free(buffer);
free_all_types();
free_all_tokens();
free_all_preprocess();
}
// Запуск ассемблера
стат пусто run_assembler(символ *input, символ *output) {
символ *cmd[] = {"as", "-c", input, "-o", output, NULL};
execute_subprocess(cmd);
}
// Поиск файла по шаблону (glob)
стат символ *find_matching_file(символ *pattern) {
символ *path = NULL;
glob_t buf = {};
glob(pattern, 0, NULL, &buf);
если (buf.gl_pathc > 0)
path = strdup(buf.gl_pathv[buf.gl_pathc - 1]);
globfree(&buf);
возврат path;
}
// Проверка существования файла
бул file_exists(символ *path) {
структ stat st;
возврат !stat(path, &st);
}
// Поиск пути к системным библиотекам
стат символ *locate_library_path(пусто) {
если (file_exists("/usr/lib/x86_64-linux-gnu/crti.o"))
возврат "/usr/lib/x86_64-linux-gnu";
если (file_exists("/usr/lib64/crti.o"))
возврат "/usr/lib64";
error("library path is not found");
}
// Поиск пути к библиотекам GCC
стат символ *locate_gcc_library_path(пусто) {
символ *search_patterns[] = {
"/usr/lib/gcc/x86_64-linux-gnu/*/crtbegin.o",
"/usr/lib/gcc/x86_64-pc-linux-gnu/*/crtbegin.o", // Для Gentoo
"/usr/lib/gcc/x86_64-redhat-linux/*/crtbegin.o", // Для Fedora
};
для (цел i = 0; i < размер(search_patterns) / размер(*search_patterns); i++) {
символ *path = find_matching_file(search_patterns[i]);
если (path)
возврат dirname(path);
}
error("gcc library path is not found");
}
// Запуск линкера
стат пусто invoke_linker(МассивСтрок *inputs, символ *output) { // было StringArray
МассивСтрок args = {}; // было StringArray
массив_строк_добавить(&args, "ld");
массив_строк_добавить(&args, "-o");
массив_строк_добавить(&args, output);
массив_строк_добавить(&args, "-m");
массив_строк_добавить(&args, "elf_x86_64");
символ *lib_path = locate_library_path();
символ *gcc_lib_path = locate_gcc_library_path();
// Добавляем стартовые объекты в зависимости от типа линковки
если (option_shared_lib) {
массив_строк_добавить(&args, формат("%s/crti.o", lib_path));
массив_строк_добавить(&args, формат("%s/crtbeginS.o", gcc_lib_path));
} иначе {
массив_строк_добавить(&args, формат("%s/crt1.o", lib_path));
массив_строк_добавить(&args, формат("%s/crti.o", lib_path));
массив_строк_добавить(&args, формат("%s/crtbegin.o", gcc_lib_path));
}
// Добавляем пути поиска библиотек
массив_строк_добавить(&args, формат("-L%s", gcc_lib_path));
массив_строк_добавить(&args, "-L/usr/lib/x86_64-linux-gnu");
массив_строк_добавить(&args, "-L/usr/lib64");
массив_строк_добавить(&args, "-L/lib64");
массив_строк_добавить(&args, "-L/usr/lib/x86_64-linux-gnu");
массив_строк_добавить(&args, "-L/usr/lib/x86_64-pc-linux-gnu");
массив_строк_добавить(&args, "-L/usr/lib/x86_64-redhat-linux");
массив_строк_добавить(&args, "-L/usr/lib");
массив_строк_добавить(&args, "-L/lib");
// Добавляем динамический линкер для не-статической линковки
если (!option_static_link) {
массив_строк_добавить(&args, "-dynamic-linker");
массив_строк_добавить(&args, "/lib64/ld-linux-x86-64.so.2");
}
// Добавляем дополнительные аргументы линкера
для (цел i = 0; i < linker_extra_args.len; i++)
массив_строк_добавить(&args, linker_extra_args.data[i]);
// Добавляем входные файлы
для (цел i = 0; i < inputs->len; i++)
массив_строк_добавить(&args, inputs->data[i]);
// Добавляем стандартные библиотеки
если (option_static_link) {
массив_строк_добавить(&args, "--start-group");
массив_строк_добавить(&args, "-lgcc");
массив_строк_добавить(&args, "-lgcc_eh");
массив_строк_добавить(&args, "-lc");
массив_строк_добавить(&args, "--end-group");
} иначе {
массив_строк_добавить(&args, "-lc");
массив_строк_добавить(&args, "-lgcc");
массив_строк_добавить(&args, "--as-needed");
массив_строк_добавить(&args, "-lgcc_s");
массив_строк_добавить(&args, "--no-as-needed");
}
// Добавляем конечные объекты
если (option_shared_lib)
массив_строк_добавить(&args, формат("%s/crtendS.o", gcc_lib_path));
иначе
массив_строк_добавить(&args, формат("%s/crtend.o", gcc_lib_path));
массив_строк_добавить(&args, формат("%s/crtn.o", lib_path));
массив_строк_добавить(&args, NULL);
execute_subprocess(args.data);
}
// Определение типа файла по расширению
стат FileType determine_file_type(символ *filename) {
если (source_type_override != FILE_NONE)
возврат source_type_override;
если (string_ends_with(filename, ".a"))
возврат FILE_AR;
если (string_ends_with(filename, ".so"))
возврат FILE_DSO;
если (string_ends_with(filename, ".o"))
возврат FILE_OBJ;
если (string_ends_with(filename, ".c") || string_ends_with(filename, ".рс") || string_ends_with(filename, ".си"))
возврат FILE_C;
если (string_ends_with(filename, ".s"))
возврат FILE_ASM;
error("<command line>: unknown file extension: %s", filename);
}
// Главная функция программы
цел главн(цел argc, символ **argv) {
// Регистрируем функцию очистки временных файлов
atexit(cleanup_temporary_files);
init_macros();
parse_command_line(argc, argv);
// Режим прямого вызова компилятора (cc1)
если (option_cc1_mode) {
setup_default_include_paths(argv[0]);
compile_cc1();
free_all_types();
free_all_tokens();
free_all_preprocess();
возврат 0;
}
// Проверка несовместимых опций
если (input_files.len > 1 && option_output_file && (option_compile_only || option_assembly_only || option_preprocess_only))
error("cannot specify '-o' with '-c,' '-S' or '-E' with multiple files");
МассивСтрок linker_args = {}; // было StringArray
// Обработка всех входных файлов
для (цел i = 0; i < input_files.len; i++) {
символ *input = input_files.data[i];
// Обработка опций -l (библиотеки)
если (!strncmp(input, "-l", 2)) {
массив_строк_добавить(&linker_args, input);
продолжи;
}
// Обработка опций -Wl, (передача линкеру)
если (!strncmp(input, "-Wl,", 4)) {
символ *s = strdup(input + 4);
символ *arg = strtok(s, ",");
пока (arg) {
массив_строк_добавить(&linker_args, arg);
arg = strtok(NULL, ",");
}
продолжи;
}
// Определение выходного файла
символ *output;
если (option_output_file)
output = option_output_file;
иначе если (option_assembly_only)
output = change_extension(input, ".s");
иначе
output = change_extension(input, ".o");
FileType file_type = determine_file_type(input);
// Обработка объектных файлов и архивов
если (file_type == FILE_OBJ || file_type == FILE_AR || file_type == FILE_DSO) {
массив_строк_добавить(&linker_args, input);
продолжи;
}
// Обработка ассемблерных файлов
если (file_type == FILE_ASM) {
если (!option_assembly_only)
run_assembler(input, output);
продолжи;
}
// Дальше предполагаем, что это C файл
assert(file_type == FILE_C);
// Только препроцессор
если (option_preprocess_only || option_make_depend) {
invoke_compiler_cc1(argc, argv, input, NULL);
продолжи;
}
// Только компиляция в ассемблер
если (option_assembly_only) {
invoke_compiler_cc1(argc, argv, input, output);
продолжи;
}
// Компиляция и ассемблирование
если (option_compile_only) {
символ *temp = create_temporary_file();
invoke_compiler_cc1(argc, argv, input, temp);
run_assembler(temp, output);
продолжи;
}
// Полный цикл: компиляция, ассемблирование и линковка
символ *temp_asm = create_temporary_file();
символ *temp_obj = create_temporary_file();
invoke_compiler_cc1(argc, argv, input, temp_asm);
run_assembler(temp_asm, temp_obj);
массив_строк_добавить(&linker_args, temp_obj);
продолжи;
}
// Запуск линкера, если есть что линковать
если (linker_args.len > 0)
invoke_linker(&linker_args, option_output_file ? option_output_file : "a.out");
возврат 0;
}