/*
Uma solução de recarga ao vivo somente de cabeçalho de arquivo para C, escrita em C++:
NOTA: O único arquivo que importa neste repositório é cr.h
.
Este arquivo contém a documentação em markdown, a licença, a implementação e a API pública. Todos os outros arquivos neste repositório são arquivos de suporte e podem ser ignorados com segurança.
Você pode baixar e instalar o cr usando o gerenciador de dependências vcpkg:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install cr
A porta cr no vcpkg é mantida atualizada pelos membros da equipe da Microsoft e colaboradores da comunidade. Se a versão estiver desatualizada, crie um problema ou solicitação pull no repositório vcpkg.
Um executável de aplicativo host (fino) usará cr
para gerenciar o recarregamento ao vivo do aplicativo real na forma de binário carregável dinâmico, um host seria algo como:
#define CR_HOST // required in the host only and before including cr.h
#include "../cr.h"
int main ( int argc , char * argv []) {
// the host application should initalize a plugin with a context, a plugin
cr_plugin ctx ;
// the full path to the live-reloadable application
cr_plugin_open ( ctx , "c:/path/to/build/game.dll" );
// call the update function at any frequency matters to you, this will give
// the real application a chance to run
while (! cr_plugin_update ( ctx )) {
// do anything you need to do on host side (ie. windowing and input stuff?)
}
// at the end do not forget to cleanup the plugin context
cr_plugin_close ( ctx );
return 0 ;
}
Enquanto o convidado (aplicativo real), seria assim:
CR_EXPORT int cr_main ( struct cr_plugin * ctx , enum cr_op operation ) {
assert ( ctx );
switch ( operation ) {
case CR_LOAD : return on_load (...); // loading back from a reload
case CR_UNLOAD : return on_unload (...); // preparing to a new reload
case CR_CLOSE : ...; // the plugin will close and not reload anymore
}
// CR_STEP
return on_update (...);
}
CR_INITIAL_FAILURE
. Se o plugin inicial falhar, o host deverá determinar o próximo caminho e não recarregaremos o plugin quebrado. cr_plugin_load
obsoleto em favor de cr_plugin_open
para consistência com cr_plugin_close
. Veja a edição nº 49.CR_BAD_IMAGE
caso o arquivo binário ainda não esteja pronto, mesmo que seu carimbo de data e hora tenha sido alterado. Isso poderia acontecer se a geração do arquivo (compilador ou cópia) fosse lenta.CR_BAD_IMAGE
). Agora a versão é decrementada uma vez no manipulador de falhas e depois outra vez durante a reversão e, em seguida, é alterada novamente. Uma reversão devido a uma imagem incompleta não reverterá incorretamente duas versões, ela continuará na mesma versão, tentando novamente o carregamento até que a imagem seja válida (a cópia ou o compilador terminaram de gravá-la). Isso pode afetar o uso atual de cr
se as informações version
forem usadas durante CR_UNLOAD
, pois agora terá um valor diferente. Duas amostras simples podem ser encontradas no diretório de samples
.
O primeiro é um aplicativo de console simples que demonstra alguns estados estáticos básicos funcionando entre instâncias e testes básicos de tratamento de falhas. Imprimir na saída é usado para mostrar o que está acontecendo.
O segundo demonstra como recarregar ao vivo um aplicativo opengl usando Dear ImGui. Alguns estados residem no lado do host, enquanto a maior parte do código está no lado do convidado.
As amostras e testes usam o sistema de compilação fips. Requer Python e CMake.
$ ./fips build # will generate and build all artifacts
$ ./fips run crTest # To run tests
$ ./fips run imgui_host # To run imgui sample
# open a new console, then modify imgui_guest.cpp
$ ./fips make imgui_guest # to build and force imgui sample live reload
int (*cr_main)(struct cr_plugin *ctx, enum cr_op operation)
Este é o ponteiro de função para a função de ponto de entrada binário carregável dinâmico.
Argumentos
ctx
para um contexto que será passado do host
para o guest
contendo informações valiosas sobre a versão atualmente carregada, motivo da falha e dados do usuário. Para obter mais informações, consulte cr_plugin
.operation
qual operação está sendo executada, consulte cr_op
.Retornar
CR_USER
. 0 ou um valor positivo que será passado ao processo host
. bool cr_plugin_open(cr_plugin &ctx, const char *fullpath)
Carrega e inicializa o plugin.
Argumentos
ctx
um contexto que irá gerenciar os dados internos do plugin e os dados do usuário.fullpath
caminho completo com nome de arquivo para o binário carregável do plugin ou NULL
.Retornar
true
em caso de sucesso, false
caso contrário. void cr_set_temporary_path(cr_plugin& ctx, const std::string &path)
Define o caminho temporário no qual as cópias temporárias do plugin serão colocadas. Deve ser chamado imediatamente após cr_plugin_open()
. Se o caminho temporary
não for definido, as cópias temporárias do arquivo serão copiadas para o mesmo diretório onde o arquivo original está localizado.
Argumentos
ctx
um contexto que irá gerenciar os dados internos do plugin e os dados do usuário.path
um caminho completo para um diretório existente que será usado para armazenar cópias temporárias do plugin. int cr_plugin_update(cr_plugin &ctx, bool reloadCheck = true)
Esta função chamará a função do plugin cr_main
. Ele deve ser chamado com a freqüência necessária à lógica/aplicativo principal.
Argumentos
ctx
os dados de contexto do plugin atual.reloadCheck
opcional: faça uma verificação de disco (stat()) para ver se a biblioteca dinâmica precisa ser recarregada.Retornar
cr_main
. void cr_plugin_close(cr_plugin &ctx)
Limpe os estados internos quando o plugin não for mais necessário.
Argumentos
ctx
os dados de contexto do plugin atual. cr_op
Enum indicando o tipo de etapa que está sendo executada pelo host
:
CR_LOAD
Uma carga causada por recarregamento está sendo executada, pode ser usada para restaurar qualquer estado interno salvo.CR_STEP
Uma atualização de aplicativo, esta é a operação normal e mais frequente;CR_UNLOAD
Um descarregamento para recarregar o plugin será executado, dando à aplicação uma chance de armazenar quaisquer dados necessários;CR_CLOSE
Usado ao fechar o plugin. Funciona como CR_UNLOAD
, mas nenhum CR_LOAD
deve ser esperado depois; cr_plugin
A estrutura do contexto da instância do plugin.
p
ponteiro opaco para dados cr internos;userdata
pode ser usado pelo usuário para passar informações entre recargas;version
para cada recarga bem-sucedida, começando em 1 para a primeira carga. A versão mudará durante um processo de tratamento de falhas ;failure
usada pelo sistema de proteção contra colisões, conterá o último código de erro de falha que causou uma reversão. Veja cr_failure
para mais informações sobre valores possíveis; cr_failure
Se ocorrer uma falha no binário carregável, o manipulador de falhas indicará o motivo da falha com um destes:
CR_NONE
Nenhum erro;CR_SEGFAULT
Falha de segmentação. SIGSEGV
em Linux/OSX ou EXCEPTION_ACCESS_VIOLATION
em Windows;CR_ILLEGAL
Em caso de instrução ilegal. SIGILL
em Linux/OSX ou EXCEPTION_ILLEGAL_INSTRUCTION
em Windows;CR_ABORT
Abort, SIGBRT
em Linux/OSX, não usado em Windows;CR_MISALIGN
, SIGBUS
em Linux/OSX ou EXCEPTION_DATATYPE_MISALIGNMENT
em Windows;CR_BOUNDS
é EXCEPTION_ARRAY_BOUNDS_EXCEEDED
, somente Windows;CR_STACKOVERFLOW
É EXCEPTION_STACK_OVERFLOW
, somente Windows;CR_STATE_INVALIDATED
Falha de segurança de gerenciamento CR_STATE
estático;CR_BAD_IMAGE
O plugin não é uma imagem válida (ou seja, o compilador ainda pode escrevê-lo);CR_OTHER
Outro sinal, somente Linux;CR_USER
Erro do usuário (para valores negativos retornados de cr_main
); CR_HOST
definir Esta definição deve ser usada antes de incluir cr.h
no host
, se CR_HOST
não estiver definido, cr.h
funcionará como um arquivo de cabeçalho de API público para ser usado na implementação guest
.
Opcionalmente, CR_HOST
também pode ser definido para um dos seguintes valores como forma de configurar o modo de operação safety
para gerenciamento automático de estado estático ( CR_STATE
):
CR_SAFEST
Validará o endereço e o tamanho das seções de dados de estado durante as recargas; se alguma coisa mudar, a carga será revertida;CR_SAFE
Validará apenas o tamanho da seção de estado, isso significa que o endereço da estática pode mudar (e é melhor evitar manter qualquer ponteiro para coisas estáticas);CR_UNSAFE
Não validará nada além de que o tamanho da seção se ajusta, pode não ser necessariamente exato (crescer é aceitável, mas diminuir não), este é o comportamento padrão;CR_DISABLE
Desativa completamente o gerenciamento automático de estado estático; CR_STATE
Usado para marcar uma variável estática global ou local a ser salva e restaurada durante uma recarga.
Uso
static bool CR_STATE bInitialized = false;
Você pode definir essas macros antes de incluir cr.h no host (CR_HOST) para personalizar as alocações de memória cr.h e outros comportamentos:
CR_MAIN_FUNC
: altera o símbolo 'cr_main' para o nome da função definida pelo usuário. padrão: #define CR_MAIN_FUNC "cr_main"CR_ASSERT
: substituir afirmação. padrão: #define CA_ASSERT(e) assert(e)CR_REALLOC
: substitui realloc da libc. padrão: #define CR_REALLOC(ptr, tamanho) ::realloc(ptr, tamanho)CR_MALLOC
: substitui o malloc da libc. padrão: #define CR_MALLOC(tamanho) ::malloc(tamanho)CR_FREE
: substitui o free da libc. padrão: #define CR_FREE(ptr) ::free(ptr)CR_DEBUG
: gera mensagens de depuração em CR_ERROR, CR_LOG e CR_TRACECR_ERROR
: registra mensagens de depuração em stderr. padrão (somente CR_DEBUG): #define CR_ERROR(...) fprintf(stderr, VA_ARGS )CR_LOG
: registra mensagens de depuração. padrão (somente CR_DEBUG): #define CR_LOG(...) fprintf(stdout, VA_ARGS )CR_TRACE
: imprime chamadas de função. padrão (somente CR_DEBUG): #define CR_TRACE(...) fprintf(stdout, "CR_TRACE: %sn", FUNCTION )R: Leia sobre por que fiz isso aqui.
R: Certifique-se de que o host do aplicativo e a dll estejam usando o tempo de execução dinâmico (/MD ou /MDd), pois todos os dados alocados no heap devem ser liberados com a mesma instância do alocador, compartilhando o tempo de execução entre o convidado e o host você garantirá que o mesmo alocador está sendo usado.
R: Sim. Isso deve funcionar sem problemas no Windows. No Linux e no OSX, pode haver problemas com o tratamento de falhas
Se você teve que carregar a dll antes de cr
por qualquer motivo, o Visual Studio ainda poderá manter um bloqueio no PDB. Você pode estar tendo esse problema e a solução está aqui.
Primeiro, certifique-se de que seu sistema de compilação não esteja interferindo ao ainda vincular à sua biblioteca compartilhada. Há tantas coisas que podem dar errado e você precisa ter certeza de que apenas cr
lidará com sua biblioteca compartilhada. No Linux, para obter mais informações sobre como descobrir o que está acontecendo, verifique este problema.
cr
é um recarregador C
e, ao lidar com C, assume que coisas simples funcionarão principalmente.
O problema é como o vinculador decidirá reorganizar as coisas de acordo com a quantidade de alterações feitas no código. Para alterações incrementais e localizadas, nunca tive problemas; em geral, quase não tive problemas ao escrever código C normal. Agora, quando as coisas começam a ficar mais complexas e beirando o C++, fica mais arriscado. Se você precisar fazer coisas complexas, sugiro verificar o RCCPP e ler este PDF e minha postagem original no blog sobre cr
aqui.
Com todas essas informações você poderá decidir qual é melhor para o seu caso de uso.
cr
Por patrocinar a portabilidade do cr
para o MacOSX.
Danny Grein
Rokas Kupstys
Noah Rinhart
Niklas Lundberg
Sepehr Taghdisian
Roberto Gabriel Jakabosky
@pixelherodev
Alexandre
Aceitamos TODAS as contribuições, não há pequenas coisas com que contribuir, até mesmo correções de erros de digitação de uma letra são bem-vindas.
A única coisa que precisamos é testar minuciosamente, manter o estilo do código e manter a documentação atualizada.
Além disso, aceitar e concordar em liberar qualquer contribuição sob a mesma licença.
A Licença MIT (MIT)
Copyright (c) 2017 Danny Angelo Carminati Grein
É concedida permissão, gratuitamente, a qualquer pessoa que obtenha uma cópia deste software e dos arquivos de documentação associados (o "Software"), para negociar o Software sem restrições, incluindo, sem limitação, os direitos de usar, copiar, modificar, mesclar , publicar, distribuir, sublicenciar e/ou vender cópias do Software e permitir que as pessoas a quem o Software seja fornecido o façam, sujeito às seguintes condições:
O aviso de direitos autorais acima e este aviso de permissão serão incluídos em todas as cópias ou partes substanciais do Software.
O SOFTWARE É FORNECIDO "COMO ESTÁ", SEM GARANTIA DE QUALQUER TIPO, EXPRESSA OU IMPLÍCITA, INCLUINDO, MAS NÃO SE LIMITANDO ÀS GARANTIAS DE COMERCIALIZAÇÃO, ADEQUAÇÃO A UM DETERMINADO FIM E NÃO VIOLAÇÃO. EM HIPÓTESE ALGUMA OS AUTORES OU DETENTORES DE DIREITOS AUTORAIS SERÃO RESPONSÁVEIS POR QUALQUER RECLAMAÇÃO, DANOS OU OUTRA RESPONSABILIDADE, SEJA EM UMA AÇÃO DE CONTRATO, ATO ILÍCITO OU DE OUTRA FORMA, DECORRENTE DE, OU EM CONEXÃO COM O SOFTWARE OU O USO OU OUTRAS NEGOCIAÇÕES NO SOFTWARE.
* /
#ifndef __CR_H__
#define __CR_H__
//
// Global OS specific defines/customizations
//
#if defined( _WIN32 )
#define CR_WINDOWS
#define CR_PLUGIN ( name ) "" name ".dll"
#elif defined( __linux__ )
#define CR_LINUX
#define CR_PLUGIN ( name ) "lib" name ".so"
#elif defined( __APPLE__ )
#define CR_OSX
#define CR_PLUGIN ( name ) "lib" name ".dylib"
#else
#error "Unknown/unsupported platform, please open an issue if you think this
platform should be supported."
#endif // CR_WINDOWS || CR_LINUX || CR_OSX
//
// Global compiler specific defines/customizations
//
#if defined( _MSC_VER )
#if defined( __cplusplus )
#define CR_EXPORT extern "C" __declspec(dllexport)
#define CR_IMPORT extern "C" __declspec(dllimport)
#else
#define CR_EXPORT __declspec(dllexport)
#define CR_IMPORT __declspec(dllimport)
#endif
#endif // defined(_MSC_VER)
#if defined( __GNUC__ ) // clang & gcc
#if defined( __cplusplus )
#define CR_EXPORT extern "C" __attribute__((visibility("default")))
#else
#define CR_EXPORT __attribute__((visibility("default")))
#endif
#define CR_IMPORT
#endif // defined(__GNUC__)
#if defined( __MINGW32__ )
#undef CR_EXPORT
#if defined( __cplusplus )
#define CR_EXPORT extern "C" __declspec(dllexport)
#else
#define CR_EXPORT __declspec(dllexport)
#endif
#endif
// cr_mode defines how much we validate global state transfer between
// instances. The default is CR_UNSAFE, you can choose another mode by
// defining CR_HOST, ie.: #define CR_HOST CR_SAFEST
enum cr_mode {
CR_SAFEST = 0 , // validate address and size of the state section, if
// anything changes the load will rollback
CR_SAFE = 1 , // validate only the size of the state section, this means
// that address is assumed to be safe if avoided keeping
// references to global/static states
CR_UNSAFE = 2 , // don't validate anything but that the size of the section
// fits, may not be identical though
CR_DISABLE = 3 // completely disable the auto state transfer
};
// cr_op is passed into the guest process to indicate the current operation
// happening so the process can manage its internal data if it needs.
enum cr_op {
CR_LOAD = 0 ,
CR_STEP = 1 ,
CR_UNLOAD = 2 ,
CR_CLOSE = 3 ,
};
enum cr_failure {
CR_NONE , // No error
CR_SEGFAULT , // SIGSEGV / EXCEPTION_ACCESS_VIOLATION
CR_ILLEGAL , // illegal instruction (SIGILL) / EXCEPTION_ILLEGAL_INSTRUCTION
CR_ABORT , // abort (SIGBRT)
CR_MISALIGN , // bus error (SIGBUS) / EXCEPTION_DATATYPE_MISALIGNMENT
CR_BOUNDS , // EXCEPTION_ARRAY_BOUNDS_EXCEEDED
CR_STACKOVERFLOW , // EXCEPTION_STACK_OVERFLOW
CR_STATE_INVALIDATED , // one or more global data section changed and does
// not safely match basically a failure of
// cr_plugin_validate_sections
CR_BAD_IMAGE , // The binary is not valid - compiler is still writing it
CR_INITIAL_FAILURE , // Plugin version 1 crashed, cannot rollback
CR_OTHER , // Unknown or other signal,
CR_USER = 0x100 ,
};
struct cr_plugin ;
typedef int ( * cr_plugin_main_func )( struct cr_plugin * ctx , enum cr_op operation );
// public interface for the plugin context, this has some user facing
// variables that may be used to manage reload feedback.
// - userdata may be used by the user to pass information between reloads
// - version is the reload counter (after loading the first instance it will
// be 1, not 0)
// - failure is the (platform specific) last error code for any crash that may
// happen to cause a rollback reload used by the crash protection system
struct cr_plugin {
void * p ;
void * userdata ;
unsigned int version ;
enum cr_failure failure ;
unsigned int next_version ;
unsigned int last_working_version ;
};
#ifndef CR_HOST
// Guest specific compiler defines/customizations
#if defined( _MSC_VER )
#pragma section(".state", read, write)
#define CR_STATE __declspec(allocate(".state"))
#endif // defined(_MSC_VER)
#if defined( CR_OSX )
#define CR_STATE __attribute__((used, section("__DATA,__state")))
#else
#if defined( __GNUC__ ) // clang & gcc
#define CR_STATE __attribute__((section(".state")))
#endif // defined(__GNUC__)
#endif
#else // #ifndef CR_HOST
// Overridable macros
#ifndef CR_LOG
# ifdef CR_DEBUG
# include
# define CR_LOG (...) fprintf(stdout, __VA_ARGS__)
# else
# define CR_LOG (...)
# endif
#endif
#ifndef CR_ERROR
# ifdef CR_DEBUG
# include
# define CR_ERROR (...) fprintf(stderr, __VA_ARGS__)
# else
# define CR_ERROR (...)
# endif
#endif
#ifndef CR_TRACE
# ifdef CR_DEBUG
# include
# define CR_TRACE fprintf(stdout, "CR_TRACE: %sn", __FUNCTION__);
# else
# define CR_TRACE
# endif
#endif
#ifndef CR_MAIN_FUNC
# define CR_MAIN_FUNC "cr_main"
#endif
#ifndef CR_ASSERT
# include
# define CR_ASSERT ( e ) assert(e)
#endif
#ifndef CR_REALLOC
# include
# define CR_REALLOC ( ptr , size ) ::realloc(ptr, size)
#endif
#ifndef CR_FREE
# include
# define CR_FREE ( ptr ) ::free(ptr)
#endif
#ifndef CR_MALLOC
# include
# define CR_MALLOC ( size ) ::malloc(size)
#endif
#if defined( _MSC_VER )
// we should probably push and pop this
# pragma warning(disable:4003) // not enough actual parameters for macro 'identifier'
#endif
#define CR_DO_EXPAND ( x ) x##1337
#define CR_EXPAND ( x ) CR_DO_EXPAND(x)
#if CR_EXPAND ( CR_HOST ) == 1337
#define CR_OP_MODE CR_UNSAFE
#else
#define CR_OP_MODE CR_HOST
#endif
#include
#include // duration for sleep
#include // memcpy
#include
#include // this_thread::sleep_for
#if defined( CR_WINDOWS )
#define CR_PATH_SEPARATOR '\'
#define CR_PATH_SEPARATOR_INVALID '/'
#else
#define CR_PATH_SEPARATOR '/'
#define CR_PATH_SEPARATOR_INVALID '\'
#endif
static void cr_split_path ( std :: string path , std :: string & parent_dir ,
std :: string & base_name , std :: string & ext ) {
std:: replace ( path . begin (), path . end (), CR_PATH_SEPARATOR_INVALID ,
CR_PATH_SEPARATOR );
auto sep_pos = path . rfind ( CR_PATH_SEPARATOR );
auto dot_pos = path . rfind ( '.' );
if ( sep_pos == std :: string :: npos ) {
parent_dir = "" ;
if ( dot_pos == std :: string :: npos ) {
ext = "" ;
base_name = path ;
} else {
ext = path . substr ( dot_pos );
base_name = path . substr ( 0 , dot_pos );
}
} else {
parent_dir = path . substr ( 0 , sep_pos + 1 );
if ( dot_pos == std :: string :: npos || sep_pos > dot_pos ) {
ext = "" ;
base_name = path . substr ( sep_pos + 1 );
} else {
ext = path . substr ( dot_pos );
base_name = path . substr ( sep_pos + 1 , dot_pos - sep_pos - 1 );
}
}
}
static std :: string cr_version_path ( const std :: string & basepath ,
unsigned version ,
const std :: string & temppath ) {
std:: string folder , fname , ext ;
cr_split_path ( basepath , folder , fname , ext );
std:: string ver = std :: to_string ( version );
#if defined( _MSC_VER )
// When patching PDB file path in library file we will drop path and leave only file name.
// Length of path is extra space for version number. Trim file name only if version number
// length exceeds pdb folder path length. This is not relevant on other platforms.
if ( ver . size () > folder . size ()) {
fname = fname . substr ( 0 , fname . size () - ( ver . size () - folder . size () - 1 ));
}
#endif
if (! temppath . empty ()) {
folder = temppath ;
}
return folder + fname + ver + ext ;
}
namespace cr_plugin_section_type {
enum e { state , bss , count };
}
namespace cr_plugin_section_version {
enum e { backup , current , count };
}
struct cr_plugin_section {
cr_plugin_section_type :: e type = {};
intptr_t base = 0 ;
char * ptr = 0 ;
int64_t size = 0 ;
void * data = nullptr ;
};
struct cr_plugin_segment {
char * ptr = 0 ;
int64_t size = 0 ;
};
// keep track of some internal state about the plugin, should not be messed
// with by user
struct cr_internal {
std :: string fullname = {};
std:: string temppath = {};
time_t timestamp = {};
void * handle = nullptr ;
cr_plugin_main_func main = nullptr ;
cr_plugin_segment seg = {};
cr_plugin_section data [ cr_plugin_section_type :: count ]
[ cr_plugin_section_version :: count ] = {};
cr_mode mode = CR_SAFEST ;
};
static bool cr_plugin_section_validate ( cr_plugin & ctx ,
cr_plugin_section_type :: e type ,
intptr_t vaddr , intptr_t ptr ,
int64_t size );
static void cr_plugin_sections_reload ( cr_plugin & ctx ,
cr_plugin_section_version :: e version );
static void cr_plugin_sections_store ( cr_plugin & ctx );
static void cr_plugin_sections_backup ( cr_plugin & ctx );
static void cr_plugin_reload ( cr_plugin & ctx );
static int cr_plugin_unload ( cr_plugin & ctx , bool rollback , bool close );
static bool cr_plugin_changed ( cr_plugin & ctx );
static bool cr_plugin_rollback ( cr_plugin & ctx );
static int cr_plugin_main ( cr_plugin & ctx , cr_op operation );
void cr_set_temporary_path ( cr_plugin & ctx , const std :: string & path ) {
auto pimpl = ( cr_internal * ) ctx . p ;
pimpl -> temppath = path ;
}
#if defined( CR_WINDOWS )
// clang-format off
#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
#include
#include
// clang-format on
#if defined( _MSC_VER )
#pragma comment(lib, "dbghelp.lib")
#endif
using so_handle = HMODULE ;
#ifdef UNICODE
# define CR_WINDOWS_ConvertPath ( _newpath , _path ) std::wstring _newpath(cr_utf8_to_wstring(_path))
static std :: wstring cr_utf8_to_wstring ( const std :: string & str ) {
int wlen = MultiByteToWideChar ( CP_UTF8 , 0 , str . c_str (), -1 , 0 , 0 );
wchar_t wpath_small [ MAX_PATH ];
std:: unique_ptr < wchar_t [] > wpath_big ;
wchar_t * wpath = wpath_small ;
if ( wlen > _countof ( wpath_small )) {
wpath_big = std :: unique_ptr < wchar_t [] > ( new wchar_t [ wlen ]);
wpath = wpath_big . get ();
}
if ( MultiByteToWideChar ( CP_UTF8 , 0 , str . c_str (), -1 , wpath , wlen ) != wlen ) {
return L"" ;
}
return wpath ;
}
#else
# define CR_WINDOWS_ConvertPath ( _newpath , _path ) const std::string &_newpath = _path
#endif // UNICODE
static time_t cr_last_write_time ( const std :: string & path ) {
CR_WINDOWS_ConvertPath ( _path , path );
WIN32_FILE_ATTRIBUTE_DATA fad ;
if (! GetFileAttributesEx ( _path . c_str (), GetFileExInfoStandard , & fad )) {
return -1 ;
}
if ( fad . nFileSizeHigh == 0 && fad . nFileSizeLow == 0 ) {
return -1 ;
}
LARGE_INTEGER time ;
time . HighPart = fad . ftLastWriteTime . dwHighDateTime ;
time . LowPart = fad . ftLastWriteTime . dwLowDateTime ;
return static_cast < time_t > ( time . QuadPart / 10000000 - 11644473600LL );
}
static bool cr_exists ( const std :: string & path ) {
CR_WINDOWS_ConvertPath ( _path , path );
return GetFileAttributes ( _path . c_str ()) != INVALID_FILE_ATTRIBUTES ;
}
static bool cr_copy ( const std :: string & from , const std :: string & to ) {
CR_WINDOWS_ConvertPath ( _from , from );
CR_WINDOWS_ConvertPath ( _to , to );
return CopyFile ( _from . c_str (), _to . c_str (), FALSE) ? true : false;
}
static void cr_del ( const std :: string & path ) {
CR_WINDOWS_ConvertPath ( _path , path );
DeleteFile ( _path . c_str ());
}
// If using Microsoft Visual C/C++ compiler we need to do some workaround the
// fact that the compiled binary has a fullpath to the PDB hardcoded inside
// it. This causes a lot of headaches when trying compile while debugging as
// the referenced PDB will be locked by the debugger.
// To solve this problem, we patch the binary to rename the PDB to something
// we know will be unique to our in-flight instance, so when debugging it will
// lock this unique PDB and the compiler will be able to overwrite the
// original one.
#if defined( _MSC_VER )
#include
#include
#include
#include
static std :: string cr_replace_extension ( const std :: string & filepath ,
const std :: string & ext ) {
std:: string folder , filename , old_ext ;
cr_split_path ( filepath , folder , filename , old_ext );
return folder + filename + ext ;
}
template < class T >
static T struct_cast ( void * ptr , LONG offset = 0 ) {
return reinterpret_cast < T > ( reinterpret_cast < intptr_t > ( ptr ) + offset );
}
// RSDS Debug Information for PDB files
using DebugInfoSignature = DWORD ;
#define CR_RSDS_SIGNATURE 'SDSR'
struct cr_rsds_hdr {
DebugInfoSignature signature ;
GUID guid ;
long version ;
char filename [ 1 ];
};
static bool cr_pe_debugdir_rva ( PIMAGE_OPTIONAL_HEADER optionalHeader ,
DWORD & debugDirRva , DWORD & debugDirSize ) {
if ( optionalHeader -> Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC ) {
auto optionalHeader64 =
struct_cast < PIMAGE_OPTIONAL_HEADER64 > ( optionalHeader );
debugDirRva =
optionalHeader64 -> DataDirectory [ IMAGE_DIRECTORY_ENTRY_DEBUG ]
. VirtualAddress ;
debugDirSize =
optionalHeader64 -> DataDirectory [ IMAGE_DIRECTORY_ENTRY_DEBUG ]. Size ;
} else {
auto optionalHeader32 =
struct_cast < PIMAGE_OPTIONAL_HEADER32 > ( optionalHeader );
debugDirRva =
optionalHeader32 -> DataDirectory [ IMAGE_DIRECTO