rusi/main.c

875 lines
31 KiB
C
Raw Normal View History

#включить "руси.з"
// Типы входных файлов
типдеф переч {
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;
}