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