diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 4960fb17..83c368db 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -358,7 +358,7 @@ lnk_manifest_from_inputs(Arena *arena, // write linker manifest to temp file String8 linker_manifest_path = push_str8f(scratch.arena, "%S.manifest.temp", manifest_name); - lnk_write_data_to_file_path(linker_manifest_path, linker_manifest); + lnk_write_data_to_file_path(linker_manifest_path, str8_zero(), linker_manifest); // push linker manifest str8_list_push(scratch.arena, &input_manifest_path_list, linker_manifest_path); @@ -3079,7 +3079,7 @@ lnk_write_thread(void *raw_ctx) { ProfBeginFunction(); LNK_WriteThreadContext *ctx = raw_ctx; - lnk_write_data_to_file_path(ctx->path, ctx->data); + lnk_write_data_to_file_path(ctx->path, ctx->temp_path, ctx->data); ProfEnd(); } @@ -3254,7 +3254,7 @@ lnk_run(int argc, char **argv) LNK_ObjList obj_list = {0}; LNK_LibList lib_index[LNK_InputSource_Count] = {0}; String8 image_data = str8_zero(); - OS_Handle image_write_thread = {0}; + OS_Handle image_write_thread = {0}; // init state machine struct StateList state_list = {0}; @@ -3763,7 +3763,7 @@ lnk_run(int argc, char **argv) ProfBeginDynamic("Write Manifest To: %.*s", str8_varg(config->manifest_name)); Temp temp = temp_begin(scratch.arena); String8 manifest_data = lnk_manifest_from_inputs(temp.arena, config->mt_path, config->manifest_name, config->manifest_uac, config->manifest_level, config->manifest_ui_access, input_manifest_path_list, manifest_dep_list); - lnk_write_data_to_file_path(config->manifest_name, manifest_data); + lnk_write_data_to_file_path(config->manifest_name, str8_zero(), manifest_data); temp_end(temp); ProfEnd(); } break; @@ -4080,10 +4080,14 @@ lnk_run(int argc, char **argv) } } - LNK_WriteThreadContext *ctx = push_array(scratch.arena, LNK_WriteThreadContext, 1); - ctx->path = config->image_name; - ctx->data = image_data; - image_write_thread = os_thread_launch(lnk_write_thread, ctx, 0); + // write image file in background + { + LNK_WriteThreadContext *ctx = push_array(scratch.arena, LNK_WriteThreadContext, 1); + ctx->path = config->image_name; + ctx->temp_path = config->temp_image_name; + ctx->data = image_data; + image_write_thread = os_thread_launch(lnk_write_thread, ctx, 0); + } if (lnk_get_log_status(LNK_Log_InputObj)) { U64 total_input_size = 0; @@ -4110,7 +4114,7 @@ lnk_run(int argc, char **argv) ProfBegin("Build Imp Lib"); lnk_timer_begin(LNK_Timer_Lib); String8List lib_list = lnk_build_import_lib(tp, tp_arena, config->machine, config->time_stamp, config->imp_lib_name, config->image_name, exptab); - lnk_write_data_list_to_file_path(config->imp_lib_name, lib_list); + lnk_write_data_list_to_file_path(config->imp_lib_name, str8_zero(), lib_list); lnk_timer_end(LNK_Timer_Lib); ProfEnd(); } break; @@ -4143,7 +4147,8 @@ lnk_run(int argc, char **argv) input.parsed_symbols, types); - lnk_write_data_list_to_file_path(config->rad_debug_name, rdi_data); + lnk_write_data_list_to_file_path(config->rad_debug_name, config->temp_rad_debug_name, rdi_data); + lnk_timer_end(LNK_Timer_Rdi); } @@ -4177,7 +4182,7 @@ lnk_run(int argc, char **argv) input.parsed_symbols, types); - lnk_write_data_list_to_file_path(config->pdb_name, pdb_data); + lnk_write_data_list_to_file_path(config->pdb_name, config->temp_pdb_name, pdb_data); lnk_timer_end(LNK_Timer_Pdb); } diff --git a/src/linker/lnk.h b/src/linker/lnk.h index 9f2b1b35..d6732174 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -224,6 +224,7 @@ typedef struct typedef struct { String8 path; + String8 temp_path; String8 data; } LNK_WriteThreadContext; diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index 7d123f23..0f07977f 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -152,17 +152,17 @@ global read_only struct { LNK_CmdSwitch_Rad_PdbHashTypeNames, "RAD_PDB_HASH_TYPE_NAMES", ":{NONE|LENIENT|FULL}", "Replace type names in LF_STRUCTURE and LF_CLASS with hashes." }, { LNK_CmdSwitch_Rad_SectVirtOff, "RAD_SECT_VIRT_OFF", ":#", "Set RVA where section data is placed in memory. For internal use only." }, { LNK_CmdSwitch_Rad_SharedThreadPool, "RAD_SHARED_THREAD_POOL", "[:STRING]", "Default value \"" LNK_DEFAULT_THREAD_POOL_NAME "\"" }, - { LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, "RAD_SHARED_THREAD_POOL_MAX_WORKERS", ":#", "Sets maximum number of workers in a thread pool." }, + { LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, "RAD_SHARED_THREAD_POOL_MAX_WORKERS", ":#", "Sets maximum number of workers in a thread pool." }, { LNK_CmdSwitch_Rad_SuppressError, "RAD_SUPPRESS_ERROR", ":#", "" }, { LNK_CmdSwitch_Rad_SymbolTableCapDefined, "RAD_SYMBOL_TABLE_CAP_DEFINED", ":#", "Number of buckets allocated in the symbol table for defined symbols." }, { LNK_CmdSwitch_Rad_SymbolTableCapInternal, "RAD_SYMBOL_TABLE_CAP_INTERNAL", ":#", "Number of buckets allocated in the symbol table for internal symbols." }, { LNK_CmdSwitch_Rad_SymbolTableCapLib, "RAD_SYMBOL_TABLE_CAP_LIB", ":#", "Number of buckets allocated in the symbol table for library symbols." }, { LNK_CmdSwitch_Rad_SymbolTableCapWeak, "RAD_SYMBOL_TABLE_CAP_WEAK", ":#", "Number of buckets allocated in the symbol table for weak symbols." }, { LNK_CmdSwitch_Rad_TargetOs, "RAD_TARGET_OS", ":{WINDOWS,LINUX,MAC}" }, + { LNK_CmdSwitch_Rad_WriteTempFiles, "RAD_WRITE_TEMP_FILES", "[:NO]", "When speicifed linker writes image and debug info to temporary files and renames after link is done." }, { LNK_CmdSwitch_Rad_TimeStamp, "RAD_TIME_STAMP", ":#", "Time stamp embeded in EXE and PDB." }, { LNK_CmdSwitch_Rad_Version, "RAD_VERSION", "", "Print version and exit." }, { LNK_CmdSwitch_Rad_Workers, "RAD_WORKERS", ":#", "Sets number of workers created in the pool. Number is capped at 1024. When /RAD_SHARED_THREAD_POOL is specified this number cant exceed /RAD_SHARED_THREAD_POOL_MAX_WORKERS." }, - { LNK_CmdSwitch_Help, "HELP", "", "" }, { LNK_CmdSwitch_Help, "?", "", "" }, }; @@ -1754,6 +1754,10 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } } break; + case LNK_CmdSwitch_Rad_WriteTempFiles: { + lnk_cmd_switch_parse_flag(obj_path, lib_path, cmd_switch, value_strings, &config->write_temp_files); + } break; + case LNK_CmdSwitch_Rad_TimeStamp: { lnk_cmd_switch_parse_u32(obj_path, lib_path, cmd_switch, value_strings, &config->time_stamp, 0); } break; @@ -2054,6 +2058,13 @@ lnk_config_from_cmd_line(Arena *arena, String8List raw_cmd_line) // :Rad_DebugAltPath config->rad_debug_alt_path = lnk_expand_env_vars_windows(arena, env_vars, config->rad_debug_alt_path); + // create temporary files names + if (config->write_temp_files == LNK_SwitchState_Yes) { + config->temp_image_name = push_str8f(arena, "%S.tmp%x", config->image_name, config->time_stamp); + config->temp_pdb_name = push_str8f(arena, "%S.tmp%x", config->pdb_name, config->time_stamp); + config->temp_rad_debug_name = push_str8f(arena, "%S.tmp%x", config->rad_debug_name, config->time_stamp); + } + if (lnk_get_log_status(LNK_Log_Debug)) { String8 full_cmd_line = str8_list_join(scratch.arena, &raw_cmd_line, &(StringJoin){ .sep = str8_lit_comp(" ") }); fprintf(stderr, "--------------------------------------------------------------------------------\n"); diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index 871e9662..ea9d3edc 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -155,6 +155,7 @@ typedef enum LNK_CmdSwitch_Rad_SymbolTableCapInternal, LNK_CmdSwitch_Rad_SymbolTableCapWeak, LNK_CmdSwitch_Rad_SymbolTableCapLib, + LNK_CmdSwitch_Rad_WriteTempFiles, LNK_CmdSwitch_Rad_TargetOs, LNK_CmdSwitch_Rad_TimeStamp, LNK_CmdSwitch_Rad_Version, @@ -364,6 +365,10 @@ typedef struct LNK_Config U64 symbol_table_cap_lib; B32 build_imp_lib; B32 build_exp; + LNK_SwitchState write_temp_files; + String8 temp_image_name; + String8 temp_pdb_name; + String8 temp_rad_debug_name; } LNK_Config; typedef enum diff --git a/src/linker/lnk_debug_info.c b/src/linker/lnk_debug_info.c index b3418b83..7e857d20 100644 --- a/src/linker/lnk_debug_info.c +++ b/src/linker/lnk_debug_info.c @@ -2676,7 +2676,7 @@ lnk_replace_type_names_with_hashes(TP_Context *tp, TP_Arena *arena, CV_DebugT de if (task.make_map) { String8List map = {0}; str8_list_concat_in_place_array(&map, task.maps, tp->worker_count); - lnk_write_data_list_to_file_path(map_name, map); + lnk_write_data_list_to_file_path(map_name, str8_zero(), map); tp_arena_release(&task.map_arena); } diff --git a/src/linker/lnk_io.c b/src/linker/lnk_io.c index 8969cd50..e781b1e8 100644 --- a/src/linker/lnk_io.c +++ b/src/linker/lnk_io.c @@ -10,6 +10,15 @@ lnk_open_file_read(char *path, uint64_t path_size, void *handle_buffer, uint64_t return !os_handle_match(handle, os_handle_zero()); } +shared_function int +lnk_open_file_write_rename(char *path, uint64_t path_size, void *handle_buffer, uint64_t handle_buffer_max) +{ + OS_Handle handle = os_file_open(OS_AccessFlag_Write|OS_AccessFlag_ShareRead|OS_AccessFlag_ShareWrite|OS_AccessFlag_ShareDelete, str8((U8*)path, path_size)); + Assert(sizeof(handle) <= handle_buffer_max); + MemoryCopy(handle_buffer, &handle, sizeof(handle)); + return !os_handle_match(handle, os_handle_zero()); +} + shared_function int lnk_open_file_write(char *path, uint64_t path_size, void *handle_buffer, uint64_t handle_buffer_max) { @@ -26,6 +35,14 @@ lnk_close_file(void *raw_handle) os_file_close(handle); } +shared_function int +lnk_rename_file(void *raw_handle, char *new_file_path, uint64_t new_file_path_size) +{ + OS_Handle handle = *(OS_Handle *)raw_handle; + B32 is_renamed = os_rename_file_by_handle(handle, str8((U8*)new_file_path, new_file_path_size)); + return (int)is_renamed; +} + shared_function uint64_t lnk_size_from_file(void *raw_handle) { @@ -163,34 +180,56 @@ lnk_read_data_from_file_path_parallel(TP_Context *tp, Arena *arena, String8Array } internal void -lnk_write_data_list_to_file_path(String8 path, String8List data) +lnk_write_data_list_to_file_path(String8 path, String8 temp_path, String8List data) { ProfBeginV("Write %M to %S", data.total_size, path); - B32 is_written = 0; + U64 bytes_written = 0; - OS_Handle handle; - if (lnk_open_file_write((char*)path.str, path.size, &handle, sizeof(handle))) { - U64 offset = 0; - for (String8Node *data_n = data.first; data_n != 0; data_n = data_n->next) { - U64 write_size = lnk_write_file(&handle, offset, data_n->string.str, data_n->string.size); - if (write_size != data_n->string.size) { - break; + B32 open_with_rename = (temp_path.size > 0); + OS_Handle file_handle = {0}; + if (open_with_rename) { + lnk_open_file_write_rename((char*)temp_path.str, temp_path.size, &file_handle, sizeof(file_handle)); + } else { + lnk_open_file_write((char*)path.str, path.size, &file_handle, sizeof(file_handle)); + } + + if (!os_handle_match(file_handle, os_handle_zero())) { + // write data nodes + { + for (String8Node *data_n = data.first; data_n != 0; data_n = data_n->next) { + U64 write_size = lnk_write_file(&file_handle, bytes_written, data_n->string.str, data_n->string.size); + if (write_size != data_n->string.size) { + break; + } + bytes_written += data_n->string.size; } - offset += data_n->string.size; } - lnk_close_file(&handle); + B32 is_write_complete = (bytes_written == data.total_size); - is_written = (offset == data.total_size); - if (is_written) { + if (is_write_complete) { + // rename file to original name + if (open_with_rename) { + if (lnk_rename_file(&file_handle, (char*)path.str, path.size)) { + lnk_log(LNK_Log_IO_Write, "Renamed %S -> %S", temp_path, path); + } else { + lnk_error(LNK_Error_IO, "failed to rename %S -> %S", temp_path, path); + } + } + } + + // log write + if (is_write_complete) { if (lnk_get_log_status(LNK_Log_IO_Write)) { lnk_log(LNK_Log_IO_Write, "File \"%S\" %M written", path, data.total_size); } } else { - lnk_error(LNK_Error_IO, "incomplete write occurred, %M written, expected %M, file %S", - offset, data.total_size, path); + lnk_error(LNK_Error_IO, "incomplete write, %M written, expected %M, file %S", bytes_written, data.total_size, path); } + + // clean up handle + lnk_close_file(&file_handle); } else { lnk_error(LNK_Error_NoAccess, "don't have access to write to %S", path); } @@ -199,12 +238,12 @@ lnk_write_data_list_to_file_path(String8 path, String8List data) } internal void -lnk_write_data_to_file_path(String8 path, String8 data) +lnk_write_data_to_file_path(String8 path, String8 temp_path, String8 data) { Temp scratch = scratch_begin(0,0); String8List data_list = {0}; str8_list_push(scratch.arena, &data_list, data); - lnk_write_data_list_to_file_path(path, data_list); + lnk_write_data_list_to_file_path(path, temp_path, data_list); scratch_end(scratch); } diff --git a/src/linker/lnk_io.h b/src/linker/lnk_io.h index 9bd504bb..7771caf0 100644 --- a/src/linker/lnk_io.h +++ b/src/linker/lnk_io.h @@ -17,7 +17,9 @@ typedef struct shared_function int lnk_open_file_read(char *path, uint64_t path_size, void *handle_buffer, uint64_t handle_buffer_max); shared_function int lnk_open_file_write(char *path, uint64_t path_size, void *handle_buffer, uint64_t handle_buffer_max); +shared_function int lnk_open_file_write_rename(char *path, uint64_t path_size, void *handle_buffer, uint64_t handle_buffer_max); shared_function void lnk_close_file(void *raw_handle); +shared_function int lnk_rename_file(void *raw_handle, char *new_file_path, uint64_t new_file_path_size); shared_function uint64_t lnk_size_from_file(void *raw_handle); shared_function uint64_t lnk_read_file(void *raw_handle, void *buffer, uint64_t buffer_max); shared_function uint64_t lnk_write_file(void *raw_handle, uint64_t offset, void *buffer, uint64_t buffer_size); @@ -27,8 +29,8 @@ shared_function uint64_t lnk_write_file(void *raw_handle, uint64_t offset, void internal String8 lnk_read_data_from_file_path(Arena *arena, String8 path); internal String8Array lnk_read_data_from_file_path_parallel(TP_Context *tp, Arena *arena, String8Array path_arr); -internal void lnk_write_data_list_to_file_path(String8 path, String8List list); -internal void lnk_write_data_to_file_path(String8 path, String8 data); +internal void lnk_write_data_list_to_file_path(String8 path, String8 temp_path, String8List list); +internal void lnk_write_data_to_file_path(String8 path, String8 temp_path, String8 data); internal String8List lnk_file_search(Arena *arena, String8List dir_list, String8 file_path); diff --git a/src/os/core/os_core.c b/src/os/core/os_core.c index 21a0a413..f44d5cb8 100644 --- a/src/os/core/os_core.c +++ b/src/os/core/os_core.c @@ -151,6 +151,16 @@ os_string_from_file_range(Arena *arena, OS_Handle file, Rng1U64 range) return result; } +internal B32 +os_rename_file(String8 orig_name, String8 new_name) +{ + Temp scratch = scratch_begin(0,0); + OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareWrite|OS_AccessFlag_ShareDelete, orig_name); + B32 is_renamed = os_rename_file_by_handle(file, new_name); + os_file_close(file); + return is_renamed; +} + //////////////////////////////// //~ rjf: Process Launcher Helpers diff --git a/src/os/core/os_core.h b/src/os/core/os_core.h index 5f09ca8b..3f4075b0 100644 --- a/src/os/core/os_core.h +++ b/src/os/core/os_core.h @@ -38,13 +38,14 @@ struct OS_ProcessInfo typedef U32 OS_AccessFlags; enum { - OS_AccessFlag_Read = (1<<0), - OS_AccessFlag_Write = (1<<1), - OS_AccessFlag_Execute = (1<<2), - OS_AccessFlag_Append = (1<<3), - OS_AccessFlag_ShareRead = (1<<4), - OS_AccessFlag_ShareWrite = (1<<5), - OS_AccessFlag_Inherited = (1<<6), + OS_AccessFlag_Read = (1<<0), + OS_AccessFlag_Write = (1<<1), + OS_AccessFlag_Execute = (1<<2), + OS_AccessFlag_Append = (1<<3), + OS_AccessFlag_ShareRead = (1<<4), + OS_AccessFlag_ShareWrite = (1<<5), + OS_AccessFlag_ShareDelete = (1<<6), + OS_AccessFlag_Inherited = (1<<7), }; //////////////////////////////// @@ -206,6 +207,8 @@ internal U64 os_file_write(OS_Handle file, Rng1U64 rng, void *data); internal B32 os_file_set_times(OS_Handle file, DateTime time); internal FileProperties os_properties_from_file(OS_Handle file); internal OS_FileID os_id_from_file(OS_Handle file); +internal B32 os_rename_file_by_handle(OS_Handle file, String8 new_name); +internal B32 os_rename_file(String8 orig_name, String8 new_name); internal B32 os_delete_file_at_path(String8 path); internal B32 os_copy_file_path(String8 dst, String8 src); internal String8 os_full_path_from_path(Arena *arena, String8 path); diff --git a/src/os/core/win32/os_core_win32.c b/src/os/core/win32/os_core_win32.c index 6a0e0281..d2b01c69 100644 --- a/src/os/core/win32/os_core_win32.c +++ b/src/os/core/win32/os_core_win32.c @@ -321,13 +321,14 @@ os_file_open(OS_AccessFlags flags, String8 path) DWORD share_mode = 0; DWORD creation_disposition = OPEN_EXISTING; SECURITY_ATTRIBUTES security_attributes = {sizeof(security_attributes), 0, 0}; - if(flags & OS_AccessFlag_Read) {access_flags |= GENERIC_READ;} - if(flags & OS_AccessFlag_Write) {access_flags |= GENERIC_WRITE;} - if(flags & OS_AccessFlag_Execute) {access_flags |= GENERIC_EXECUTE;} - if(flags & OS_AccessFlag_ShareRead) {share_mode |= FILE_SHARE_READ;} - if(flags & OS_AccessFlag_ShareWrite) {share_mode |= FILE_SHARE_WRITE|FILE_SHARE_DELETE;} - if(flags & OS_AccessFlag_Write) {creation_disposition = CREATE_ALWAYS;} - if(flags & OS_AccessFlag_Append) {creation_disposition = OPEN_ALWAYS; access_flags |= FILE_APPEND_DATA; } + if(flags & OS_AccessFlag_Read) {access_flags |= GENERIC_READ;} + if(flags & OS_AccessFlag_Write) {access_flags |= GENERIC_WRITE;} + if(flags & OS_AccessFlag_Execute) {access_flags |= GENERIC_EXECUTE;} + if(flags & OS_AccessFlag_ShareRead) {share_mode |= FILE_SHARE_READ;} + if(flags & OS_AccessFlag_ShareWrite) {share_mode |= FILE_SHARE_WRITE|FILE_SHARE_DELETE;} + if(flags & OS_AccessFlag_ShareDelete) {share_mode |= FILE_SHARE_DELETE; access_flags |= DELETE;} + if(flags & OS_AccessFlag_Write) {creation_disposition = CREATE_ALWAYS;} + if(flags & OS_AccessFlag_Append) {creation_disposition = OPEN_ALWAYS; access_flags |= FILE_APPEND_DATA; } if(flags & OS_AccessFlag_Inherited) { security_attributes.bInheritHandle = 1; @@ -469,6 +470,30 @@ os_id_from_file(OS_Handle file) return result; } +internal B32 +os_rename_file_by_handle(OS_Handle file, String8 new_name) +{ + Temp scratch = scratch_begin(0,0); + + HANDLE handle = (HANDLE)file.u64[0]; + + String16 new_name16 = str16_from_8(scratch.arena, new_name); + + U64 file_rename_info_size = sizeof(FILE_RENAME_INFO); + U64 buffer_size = file_rename_info_size + sizeof(WCHAR)*new_name16.size; + U8 *buffer = push_array(scratch.arena, U8, buffer_size); + + FILE_RENAME_INFO *rename_info = (FILE_RENAME_INFO *)buffer; + rename_info->ReplaceIfExists = 1; + rename_info->FileNameLength = new_name16.size * sizeof(new_name16.str[0]); + MemoryCopy(rename_info->FileName, new_name16.str, new_name16.size * sizeof(new_name16.str[0])); + + BOOL is_renamed = SetFileInformationByHandle(handle, FileRenameInfo, buffer, buffer_size); + + scratch_end(scratch); + return is_renamed; +} + internal B32 os_delete_file_at_path(String8 path) {