# Vcpkg Internals: como o gerenciador de pacotes da M$ funciona por dentro (

Caloni, 2018-09-12 computer [up] [copy]

Depois de entender mais ou menos como funciona o vcpkg é hora de realmente entrar no código e entender qual a grande sacada dessa ferramenta da Microsoft.

Depurando o projeto

Uma das formas mais divertidas de entender o funcionamento de um fonte é compilar e sair depurando. E foi o que eu fiz. Através dos step ins e step outs foi possível ter as primeiras impressões de em qual pé está o projeto, além de pegar boas ideias para meu próprio código.

Por exemplo, no começo do programa encontrei uma saída simples e eficaz de como tratar entrada e saída (ou só saída) de dentro de um terminal:

SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);

Com tudo UTF-8 a vida fica mais fácil.

Outro ponto interessante é que o fonte é muito C++ moderno, com direito a inclusive usar headers ainda experimentais, como o filesystem (C++ 17). Ele usa também um conjunto de paths sobre onde estão as coisas (instalação, pacotes, etc). Há muito código no vcpkg que são módulos independentes que soam como retrabalho de coisas comuns, como parseamento de argumentos (e a já citada transformação em UTF-8), mas o objetivo do projeto é ser independente de tudo. Do contrário ele não seria um bom gerenciador de pacotes.

O arquivo vcpkg\installed\vcpkg\status contém em formato texto simples o status de todos os pacotes instalados (se foi instalado com sucesso ou não, etc). A pasta vcpkg\ports contém todos os pacotes, instalados ou não. O início de tudo é o executável na pasta-raiz após compilado, vcpkg.exe, feito em C++ e que realiza todas as bruxarias para montar a hierarquia de pastas e arquivos em texto. Tudo é tão simples e baseado em arquivos de texto que vejo que a M$ finalmente se rendeu ao jeito unix de fazer as coisas (mais conhecido como o jeito certo).

Triplets

No gerenciador de pacotes há um conceito chamado de triplet, que não é uma novidade; é uma forma de especificar um conjunto de elementos do ambiente para cross compiling utilizando um simples nome.

c:\Libs\vcpkg>vcpkg help triplet
Available architecture triplets:
  arm-uwp
  arm-windows
  arm64-uwp
  arm64-windows
  x64-linux
  x64-osx
  x64-uwp
  x64-windows
  x64-windows-static
  x86-uwp
  x86-windows
  x86-windows-static
  x86-windows-static-v140xp  <--- essa eu criei

O vcpkg já vem com alguns triplets de fábrica, mas você pode criar os seus próprios na pasta triplets, alterando várias variáveis de controle de compilação:

  • VCPKG_TARGET_ARCHITECTURE. A arquitetura alvo (x86, x64, arm, arm64).
  • VCPKG_CRT_LINKAGE. A linkagem do CRT (que é mais conhecida pelo pessoal do Zwindows; valores: dynamic, static).
  • VCPKG_LIBRARY_LINKAGE. O mesmo do CRT, mas para libs (as bibliotecas podem ignorar se elas não suportam isso).
  • VCPKG_CMAKE_SYSTEM_NAME. A plataforma alvo, que pode ser vazio (o Windows desktop padrão), WindowsStore, Darwin (Mac OSX) ou Linux.
  • VCPKG_PLATFORM_TOOLSET. O toolset do Visual Studio (mais uma coisa do Zwindows); v141, v140 são valores válidos (vazio também).
  • VCPKG_VISUAL_STUDIO_PATH. Onde está a instalação do Visual Studio (é, o vcpkg tem uma certa tendência pro Zwindows).
  • VCPKG_CHAINLOAD_TOOLCHAIN_FILE. Esse não é do Zwindows, mas do CMake; a possibilidade de escolher outro toolchain (diferente de scripts/toolchains) para o CMake.

VCPKG_CXX_FLAGS

Há diversas flags de compilação que podem ser especificadas direto no triplet:

  • VCPKG_CXX_FLAGS_DEBUG
  • VCPKG_CXX_FLAGS_RELEASE
  • VCPKG_C_FLAGS
  • VCPKG_C_FLAGS_DEBUG
  • VCPKG_C_FLAGS_RELEASE

Customização per-port

A macro do CMake PORT será interpretada pelo triplet. Isso é uma garantia de mudanças nos settings para portabilidade. Por exemplo:

set(VCPKG_LIBRARY_LINKAGE static)
if(PORT MATCHES "qt5-")
    set(VCPKG_LIBRARY_LINKAGE dynamic)
endif()

Que compila qualquer coisa que entre no match "qt5-\*" como dinâmico (DLLs), embora todo o resto possa ser estático.

Integração com Visual Studio

A integração com o Visual Studio ocorre com o uso daqueles pedaços de configuração de projetos que são as abas de propriedades. Você mesmo pode criar abas de propriedade como arquivos separados do seu vcxproj para configurações comuns a mais projetos.

Para realizar a integração o comando é **vcpkg integrate install":

c:\Libs\vcpkg>vcpkg.exe integrate install
Applied user-wide integration for this vcpkg root.
All MSBuild C++ projects can now #include any installed libraries.
Linking will be handled automatically.
Installing new libraries will make them instantly available.
CMake projects should use: "-DCMAKE_TOOLCHAIN_FILE=c:/Libs/vcpkg/scripts/buildsystems/vcpkg.cmake"

Note que as coisas para quem usa CMake são automáticas e fáceis de usar. Basta acrescentar o toolchain especificado. Já para Visual Studio...

Behind the scene

O mecanismo envolve uma pasta do msbuild:

**C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\ImportBefore\Default**

Dentro dessa pasta é colocado um desses pedaços de configuração (propriedades) chamado **vcpkg.system.props**.

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- version 1 -->
  <PropertyGroup>
    <VCLibPackagePath Condition="'$(VCLibPackagePath)' == ''">$(LOCALAPPDATA)\vcpkg\vcpkg.user</VCLibPackagePath>
  </PropertyGroup>
  <Import Condition="'$(VCLibPackagePath)' != '' and Exists('$(VCLibPackagePath).targets')" Project="$(VCLibPackagePath).targets" />
</Project>

Essa diretiva usa a pasta definida pela variável de ambiente **LOCALAPPDATA** (geralmente C:\Users\<seu-usuario>\AppData\Local) para localizar um outro arquivo, o **vcpkg.user.targets**.

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Condition="Exists('C:\Libs\vcpkg\scripts\buildsystems\msbuild\vcpkg.targets') and '$(VCPkgLocalAppDataDisabled)' == ''" Project="C:\Libs\vcpkg\scripts\buildsystems\msbuild\vcpkg.targets" />
</Project>

No exemplo estou usando um vcpkg disponível na pasta c:\libs (que é basicamente um clone do repositório GitHub do vcpkg). Note que ele inclui automaticamente nos projetos do Visual Studio um target dentro dele, o **vcpkg\scripts\buildsystems\msbuild\vcpkg.targets**. Vejamos o que tem nele:

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- ... -->
  <ItemDefinitionGroup Condition="'$(VcpkgEnabled)' == 'true'">
    <Link>
      <AdditionalDependencies Condition="'$(VcpkgNormalizedConfiguration)' == 'Debug' and '$(VcpkgAutoLink)' != 'false'">%(AdditionalDependencies);$(VcpkgRoot)debug\lib\*.lib</AdditionalDependencies>
      <AdditionalDependencies Condition="'$(VcpkgNormalizedConfiguration)' == 'Release' and '$(VcpkgAutoLink)' != 'false'">%(AdditionalDependencies);$(VcpkgRoot)lib\*.lib</AdditionalDependencies>
      <AdditionalLibraryDirectories Condition="'$(VcpkgNormalizedConfiguration)' == 'Release'">%(AdditionalLibraryDirectories);$(VcpkgRoot)lib;$(VcpkgRoot)lib\manual-link</AdditionalLibraryDirectories>
      <AdditionalLibraryDirectories Condition="'$(VcpkgNormalizedConfiguration)' == 'Debug'">%(AdditionalLibraryDirectories);$(VcpkgRoot)debug\lib;$(VcpkgRoot)debug\lib\manual-link</AdditionalLibraryDirectories>
    </Link>
    <ClCompile>
      <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(VcpkgRoot)include</AdditionalIncludeDirectories>
    </ClCompile>
    <ResourceCompile>
      <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(VcpkgRoot)include</AdditionalIncludeDirectories>
    </ResourceCompile>
  </ItemDefinitionGroup>
  <!-- ... -->
  </Target>
</Project>

Note como as pastas de instalação dos pacotes do triplet selecionado são incluídas na configuração de um projeto do Visual Studio. As libs ficam na subpasta installed/triplet/lib, os binários em installed/triplet/bin, os includes em installed/triplet/include e assim por diante. A ramificação dos pacotes está de acordo com o basename de cada um deles.

A mágica ocorre já na hora de dar include. E é mágica desde o autocomplete até o link. Por exemplo, digamos que vamos fazer um embedded de Python usando o exemplo do help:

int main(int argc, char* argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], NULL);
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }
    Py_SetProgramName(program);  /* optional but recommended */
    Py_Initialize();
    PyRun_SimpleString("from time import time,ctime\n"
        "print('Today is', ctime(time()))\n");
    if (Py_FinalizeEx() < 0) {
        exit(120);
    }
    PyMem_RawFree(program);
    return 0;
}

O programa compila e linka. Para provar que ele usa a lib instalada (versão debug):

c:\Libs\vcpkg>dir /s /b installed\x86-windows\*python*.lib
c:\Libs\vcpkg\installed\x86-windows\debug\lib\boost_python36-vc140-mt-gd.lib
c:\Libs\vcpkg\installed\x86-windows\debug\lib\python36_d.lib
c:\Libs\vcpkg\installed\x86-windows\lib\boost_python36-vc140-mt.lib
c:\Libs\vcpkg\installed\x86-windows\lib\python36.lib
c:\Libs\vcpkg>mv c:\Libs\vcpkg\installed\x86-windows\debug\lib\python36_d.lib \Temp
1>------ Rebuild All started: Project: ConsoleApplication1, Configuration: Debug Win32 ------
1>pch.cpp
1>ConsoleApplication1.cpp
[triplet]: https://github.com/Microsoft/vcpkg/blob/master/docs/users/triplets.md
[CMake]: https://cmake.org/cmake/help/v3.11/manual/cmake-toolchains.7.html
[exemplo do help]: https://docs.python.org/3/extending/embedding.html
1>LINK : fatal error LNK1104: cannot open file 'python36_d.lib'
1>Done building project "ConsoleApplication1.vcxproj" -- FAILED.
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========

Se você prestou atenção ao conteúdo de msbuild\vcpkg.targets lá em cima vai ter visto que há uma condição que adiciona toda e qualquer lib como dependência adicional ao projeto compilando:

<AdditionalDependencies Condition="'$(VcpkgNormalizedConfiguration)' == 'Debug' and '$(VcpkgAutoLink)' != 'false'">%(AdditionalDependencies);$(VcpkgRoot)debug\lib\*.lib</AdditionalDependencies>
<AdditionalDependencies Condition="'$(VcpkgNormalizedConfiguration)' == 'Release' and '$(VcpkgAutoLink)' != 'false'">%(AdditionalDependencies);$(VcpkgRoot)lib\*.lib</AdditionalDependencies>

É isso que resolve o problema de saber qual o nome da lib resultante de um pacote instalado. Porém, isso não é o ideal, principalmente por dois motivos:

1. Os nomes de configuração do projeto tem que ser Debug ou Release (maneiras de melhorar já está sendo discutido no GitHub).

2. O usuário final não tem qualquer controle do que adicionar como dependência; simplesmente vai todos os pacotes instalados (mais uma discussão no GitHub).

Porém, no momento é assim que funciona. Para o problema #1 a solução paliativa é o próprio usuário adicionar em seu msbuild as condições de sua configuração. A sugestão da thread é boa:

<AdditionalDependencies Condition="$(VcpkgConfiguration.StartsWith('Debug')) and '$(VcpkgAutoLink)' != 'false'">%(AdditionalDependencies);$(VcpkgRoot)debug\lib\*.lib</AdditionalDependencies>

Pelo menos tudo que começar com Debug (ou Release) já entraria no filtro.

**Update**: Essa sugestão já foi adicionada à última versão do vcpkg. É feita uma normalização do nome:

  <PropertyGroup Condition="'$(VcpkgEnabled)' == 'true'">
    <VcpkgConfiguration Condition="'$(VcpkgConfiguration)' == ''">$(Configuration)</VcpkgConfiguration>
    <VcpkgNormalizedConfiguration Condition="$(VcpkgConfiguration.StartsWith('Debug'))">Debug</VcpkgNormalizedConfiguration>
    <VcpkgNormalizedConfiguration Condition="$(VcpkgConfiguration.StartsWith('Release')) or '$(VcpkgConfiguration)' == 'RelWithDebInfo' or '$(VcpkgConfiguration)' == 'MinSizeRel'">Release</VcpkgNormalizedConfiguration>
    <VcpkgRoot Condition="'$(VcpkgRoot)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), .vcpkg-root))\installed\$(VcpkgTriplet)\</VcpkgRoot>
    <VcpkgApplocalDeps Condition="'$(VcpkgApplocalDeps)' == ''">true</VcpkgApplocalDeps>
  </PropertyGroup>

Assim o que seguir é Debug ou Release =).

F5 que funciona

Um outro potencial problema dos usuários de Visual Studio para compilar e rodar projetos C++ são as dependências de binários (DLLs). É possível que um pacote seja compilado de maneira dinâmica, ou seja, com DLLs de dependência. Essas DLLs na instalação do pacote devem constar na pasta bin, mas por conta dessa pasta não fazer parte dos diretórios de sistema o depurador do Visual Studio irá carregar um executável em sua pasta de geração em que não encontrará as eventuais DLLs que ele precisa para rodar.

Para "corrigir" isso, ou melhor dizendo, contornar a experiência, também foi adicionado um comando Post Build no vcpkg.targets com um comando Power Shell que copia esses binários para a pasta de geração do projeto atual. Dessa forma o projeto pode rodar sem problemas, o usuário fica feliz e consegue terminar sua programação antes de passar para o deploy (e facilita deploys de testes, pois basta copiar a pasta de geração do executável que todas suas dependências estarão lá).

  <Target Name="AppLocalFromInstalled" AfterTargets="CopyFilesToOutputDirectory" BeforeTargets="CopyLocalFilesOutputGroup;RegisterOutput" Condition="'$(VcpkgEnabled)' == 'true' and '$(VcpkgApplocalDeps)' == 'true'">
    <WriteLinesToFile
    File="$(TLogLocation)$(ProjectName).write.1u.tlog"
    Lines="^$(TargetPath);$([System.IO.Path]::Combine($(ProjectDir),$(IntDir)))vcpkg.applocal.log" Encoding="Unicode"/>
    <Exec Condition="$(VcpkgConfiguration.StartsWith('Debug'))"
      Command="$(SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -noprofile -File %22$(MSBuildThisFileDirectory)applocal.ps1%22 %22$(TargetPath)%22 %22$(VcpkgRoot)debug\bin%22 %22$(TLogLocation)$(ProjectName).write.1u.tlog%22 %22$(IntDir)vcpkg.applocal.log%22"
      StandardOutputImportance="Normal">
    </Exec>
    <Exec Condition="$(VcpkgConfiguration.StartsWith('Release'))"
      Command="$(SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -noprofile -File %22$(MSBuildThisFileDirectory)applocal.ps1%22 %22$(TargetPath)%22 %22$(VcpkgRoot)bin%22 %22$(TLogLocation)$(ProjectName).write.1u.tlog%22 %22$(IntDir)vcpkg.applocal.log%22"
      StandardOutputImportance="Normal">
    </Exec>
    <ReadLinesFromFile File="$(IntDir)vcpkg.applocal.log">
      <Output TaskParameter="Lines" ItemName="VcpkgAppLocalDLLs" />
    </ReadLinesFromFile>
    <Message Text="@(VcpkgAppLocalDLLs,'%0A')" Importance="Normal" />
    <ItemGroup>
      <ReferenceCopyLocalPaths Include="@(VcpkgAppLocalDLLs)" />
    </ItemGroup>
  </Target>

O script executado pelo PowerShell fica em **vcpkg\scripts\buildsystems\msbuild** e recebe o TargetPath (o binário-alvo) como parâmetro e onde estão os binários instalados pelo vcpkg, e com base na saída da ferramenta dumpbin extrai as dependências do executável e as busca no diretório bin:

$a = $(dumpbin /DEPENDENTS $targetBinary | ? { $_ -match "^    [^ ].*\.dll" } | % { $_ -replace "^    ","" })

Isso é o equivalente ao uso padrão de dumpbin com grep e sed:

c:\Libs\vcpkg>dumpbin /DEPENDENTS  c:\...\Debug\ConsoleApplication1.exe | grep "^    .*.dll" | sed "s/^    \(.*.dll\)/\1/"
python36_d.dll
VCRUNTIME140D.dll
ucrtbased.dll
KERNEL32.dll

A cópia dos binários é feito com um teste simples de "path existe" com deploy:

if (Test-Path "$installedDir\$_") {
    deployBinary $baseTargetBinaryDir $installedDir "$_"

**Fato curioso**: no script do PowerShell existem alguns hacks para alguns pacotes, incluindo Qt.

CMake for the win!

O uso do CMake permite aos usuários do vcpkg ter boas ideias apenas lendo os scripts do projeto. Se você abrir o solution vcpkg.sln dentro de toolsrc vai descobrir todos os scripts listados por lá. Há funções espertinhas como o download e extração de pacotes 7zip do Source Forge.

Essa parte fica em **vcpkg/scripts/cmake**. Olhe, por exemplo, como retornar a versão do Windows SDK (vcpkg_get_windows_sdk.cmake):

# Returns Windows SDK number via out variable "ret"
function(vcpkg_get_windows_sdk ret)
    set(WINDOWS_SDK $ENV{WindowsSDKVersion})
    string(REPLACE "\\" "" WINDOWS_SDK "${WINDOWS_SDK}")
    set(${ret} ${WINDOWS_SDK} PARENT_SCOPE)
endfunction()

Assim como o esquema de triplets, tudo pode ser atualizado conforme o gosto do freguês, adicionando funções e configurações úteis em seu clone do repositório, e feitas atualizações com a versão oficial.

Exportando instalações

O vcpkg não é apenas um ecossistema de libs compiladas e instaladas em uma pasta para serem usadas localmente. Pode ser um caminho simples e rápido para você conseguir compilar libs conhecidas e entregar para um terceiro um zip com todos os includes, libs e dependências do seu projeto.

c:\Libs\vcpkg>vcpkg.exe export python3 --7zip
The following packages are already built and will be exported:
    python3:x86-windows
Exporting package python3:x86-windows...
Exporting package python3:x86-windows... done
Creating 7zip archive...
Creating 7zip archive... done
7zip archive exported at: c:/Libs/vcpkg/vcpkg-export-20180912-172712.7z
To use the exported libraries in CMake projects use:
    "-DCMAKE_TOOLCHAIN_FILE=[...]/scripts/buildsystems/vcpkg.cmake"

Montando seu próprio pacote

Para trabalhar em equipe é vital que todos falem a mesma língua. Uma das formas disso acontecer é usar um gerenciamento de pacotes que inclua todos os ambientes que a equipe usa. Como geralmente esses ambiente não são os mesmos, o uso de pacotes próprios do vcpkg é um plus da ferramenta que vem para somar em padronização de fontes e compilação.

Primeiro de tudo é interessante existir um local público de download dos fontes (caso o projeto seja opensource; se bem que é possível que o endereço seja apenas visível para usuários logados ou outro mecanismo de proteção).

Uma estrutura simples de lib que compila com CMake, por exemplo, deverá conter alguns arquivos mínimos:

c:\Libs\vcpkg>dir /s /b \Libs\bitforge
c:\Libs\bitforge\bitforge.cpp
c:\Libs\bitforge\bitforge.h
c:\Libs\bitforge\CMakeLists.txt
c:\Libs\bitforge\LICENSE

Um .cpp com a implementação, um .h público para o usuário acessar, uma licença de uso (LICENSE) e um arquivo CMakeLists.txt são o suficiente para demonstar o uso. Dentro do CMakeLists.txt temos as seguintes diretivas:

cmake_minimum_required (VERSION 3.11.2)
project(bitforge VERSION 18.9.12 LANGUAGES CXX)
add_library(bitforge STATIC bitforge.cpp)
install(TARGETS bitforge DESTINATION lib)
install(FILES bitforge.h DESTINATION include)

A partir de um zip na internet da pasta bitforge já é possível começar a montar seu próprio pacote:

c:\Libs\vcpkg>vcpkg.exe create bitforge http://caloni.com.br/release/bitforge-18.9.12.zip bitforge-18.9.12.zip
-- Downloading http://caloni.com.br/release/bitforge-18.9.12.zip...
-- Generated portfile: C:\Libs\vcpkg\ports\bitforge\portfile.cmake
-- Generated CONTROL: C:\Libs\vcpkg\ports\bitforge\CONTROL
-- To launch an editor for these new files, run
--     .\vcpkg edit bitforge

_Dica: você pode também testar ou implantar isso localmente usando Python:_

python3 -m http.server
python -m SimpleHTTPServer

O arquivo portfile.cmake já possui teoricamente tudo o que precisa para falhar. Há alguns caveats que podem te dar bastante dor de cabeça no começo. Por isso mesmo eu vou economizar algum tempo para você.

Em primeiro lugar, preste atenção no diretório onde estarão os fontes. É costume do template usar o mesmo nome do zip, o que nem sempre é verdade (aqui não é, não existe versão no nome da pasta zipada):

Então em vez de:

set(SOURCE_PATH ${CURRENT_BUILDTREES_DIR}/src/bitforge-18.9.12)

Isso:

set(SOURCE_PATH ${CURRENT_BUILDTREES_DIR}/src/bitforge)

O erro que deve acontecer na falta dessa mudança é o seguinte:

c:\Libs\vcpkg>vcpkg.exe build bitforge
-- Using cached C:/Libs/vcpkg/downloads/bitforge-18.9.12.zip
-- Configuring x86-windows
CMake Error at scripts/cmake/vcpkg_execute_required_process.cmake:56 (message):
    Command failed: ninja;-v
    Working Directory: C:/Libs/vcpkg/buildtrees/bitforge/x86-windows-rel/vcpkg-parallel-configure
    See logs for more information:
      C:\Libs\vcpkg\buildtrees\bitforge\config-x86-windows-out.log
Call Stack (most recent call first):
  scripts/cmake/vcpkg_configure_cmake.cmake:246 (vcpkg_execute_required_process)
  ports/bitforge/portfile.cmake:22 (vcpkg_configure_cmake)
  scripts/ports.cmake:71 (include)
Elapsed time for package bitforge:x86-windows: 983.4 ms
Error: Building package bitforge:x86-windows failed with: BUILD_FAILED
Please ensure you're using the latest portfiles with `.\vcpkg update`, then
submit an issue at https://github.com/Microsoft/vcpkg/issues including:
  Package: bitforge:x86-windows
  Vcpkg version: 0.0.113-nohash
Additionally, attach any relevant sections from the log files above.
c:\Libs\vcpkg>cat C:\Libs\vcpkg\buildtrees\bitforge\config-x86-windows-out.log
[1/2] cmd /c "cd .. && "C:/Libs/vcpkg/downloads/tools/cmake-3.11.4-windows/cmake-3.11.4-win32-x86/bin/cmake.exe" "C:/Libs/vcpkg/buildtrees/bitforge/src/bitforge-18.9.12" "-DCMAKE_MAKE_PROGRAM=C:/Program Files (x86)/Microsoft Visual Studio/Preview/Enterprise/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja/ninja.exe" "-DBUILD_SHARED_LIBS=ON" "-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=C:/Libs/vcpkg/scripts/toolchains/windows.cmake" "-DVCPKG_TARGET_TRIPLET=x86-windows" "-DVCPKG_PLATFORM_TOOLSET=v141" "-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON" "-DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON" "-DCMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY=ON" "-DCMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP=TRUE" "-DCMAKE_VERBOSE_MAKEFILE=ON" "-DVCPKG_APPLOCAL_DEPS=OFF" "-DCMAKE_TOOLCHAIN_FILE=C:/Libs/vcpkg/scripts/buildsystems/vcpkg.cmake" "-DCMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION=ON" "-DVCPKG_CXX_FLAGS=" "-DVCPKG_CXX_FLAGS_RELEASE=" "-DVCPKG_CXX_FLAGS_DEBUG=" "-DVCPKG_C_FLAGS=" "-DVCPKG_C_FLAGS_RELEASE=" "-DVCPKG_C_FLAGS_DEBUG=" "-DVCPKG_CRT_LINKAGE=dynamic" "-DVCPKG_LINKER_FLAGS=" "-DCMAKE_INSTALL_LIBDIR:STRING=lib" "-DCMAKE_INSTALL_BINDIR:STRING=bin" "-G" "Ninja" "-DCMAKE_BUILD_TYPE=Release" "-DCMAKE_INSTALL_PREFIX=C:/Libs/vcpkg/packages/bitforge_x86-windows""
FAILED: ../CMakeCache.txt
cmd /c "cd .. && "C:/Libs/vcpkg/downloads/tools/cmake-3.11.4-windows/cmake-3.11.4-win32-x86/bin/cmake.exe" "C:/Libs/vcpkg/buildtrees/bitforge/src/bitforge-18.9.12" "-DCMAKE_MAKE_PROGRAM=C:/Program Files (x86)/Microsoft Visual Studio/Preview/Enterprise/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja/ninja.exe" "-DBUILD_SHARED_LIBS=ON" "-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=C:/Libs/vcpkg/scripts/toolchains/windows.cmake" "-DVCPKG_TARGET_TRIPLET=x86-windows" "-DVCPKG_PLATFORM_TOOLSET=v141" "-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON" "-DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON" "-DCMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY=ON" "-DCMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP=TRUE" "-DCMAKE_VERBOSE_MAKEFILE=ON" "-DVCPKG_APPLOCAL_DEPS=OFF" "-DCMAKE_TOOLCHAIN_FILE=C:/Libs/vcpkg/scripts/buildsystems/vcpkg.cmake" "-DCMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION=ON" "-DVCPKG_CXX_FLAGS=" "-DVCPKG_CXX_FLAGS_RELEASE=" "-DVCPKG_CXX_FLAGS_DEBUG=" "-DVCPKG_C_FLAGS=" "-DVCPKG_C_FLAGS_RELEASE=" "-DVCPKG_C_FLAGS_DEBUG=" "-DVCPKG_CRT_LINKAGE=dynamic" "-DVCPKG_LINKER_FLAGS=" "-DCMAKE_INSTALL_LIBDIR:STRING=lib" "-DCMAKE_INSTALL_BINDIR:STRING=bin" "-G" "Ninja" "-DCMAKE_BUILD_TYPE=Release" "-DCMAKE_INSTALL_PREFIX=C:/Libs/vcpkg/packages/bitforge_x86-windows""
--- IMPORTANTE ---
CMake Error: The source directory "C:/Libs/vcpkg/buildtrees/bitforge/src/bitforge-18.9.12" does not exist.
--- IMPORTANTE ---

Em segundo lugar, a cópia do header é feita tanto em release quanto em debug. A compilação via vcpkg irá te avisar que tem alguma coisa errada pois está duplicado, mas já há uma linha mágica que pode ser adicionada:

# Fix duplicated include
file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include)

O erro que deve acontecer na falta dessa mudança é o seguinte:

c:\Libs\vcpkg>vcpkg.exe build bitforge
-- Using cached C:/Libs/vcpkg/downloads/bitforge-18.9.12.zip
-- Configuring x86-windows
-- Building x86-windows-dbg
-- Building x86-windows-rel
-- Performing post-build validation
--- IMPORTANTE ---
Include files should not be duplicated into the /debug/include directory. If this cannot be disabled in the project cmake, use
    file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include)
--- IMPORTANTE ---
The software license must be available at ${CURRENT_PACKAGES_DIR}/share/bitforge/copyright
    file(COPY ${CURRENT_BUILDTREES_DIR}/src/bitforge/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/bitforge)
    file(RENAME ${CURRENT_PACKAGES_DIR}/share/bitforge/LICENSE ${CURRENT_PACKAGES_DIR}/share/bitforge/copyright)
Found 2 error(s). Please correct the portfile:
    c:\Libs\vcpkg\ports\bitforge\portfile.cmake
-- Performing post-build validation done
Elapsed time for package bitforge:x86-windows: 4.139 s
Error: Building package bitforge:x86-windows failed with: POST_BUILD_CHECKS_FAILED
Please ensure you're using the latest portfiles with `.\vcpkg update`, then
submit an issue at https://github.com/Microsoft/vcpkg/issues including:
  Package: bitforge:x86-windows
  Vcpkg version: 0.0.113-nohash
Additionally, attach any relevant sections from the log files above.

E por último, é obrigatório ter um arquivo de copyright, no caso o nosso LICENSE do projeto. O portfile.cmake já tem o comando, mas está comentado:

# Handle copyright
file(INSTALL ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/bitforge RENAME copyright)

O erro que deve acontecer na falta dessa mudança é o seguinte:

c:\Libs\vcpkg>vcpkg.exe build bitforge
Your feedback is important to improve Vcpkg! Please take 3 minutes to complete our survey by running: vcpkg contact --survey
-- Using cached C:/Libs/vcpkg/downloads/bitforge-18.9.12.zip
-- Configuring x86-windows
-- Building x86-windows-dbg
-- Building x86-windows-rel
-- Performing post-build validation
--- IMPORTANTE ---
The software license must be available at ${CURRENT_PACKAGES_DIR}/share/bitforge/copyright
--- IMPORTANTE ---
    file(COPY ${CURRENT_BUILDTREES_DIR}/src/bitforge/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/bitforge)
    file(RENAME ${CURRENT_PACKAGES_DIR}/share/bitforge/LICENSE ${CURRENT_PACKAGES_DIR}/share/bitforge/copyright)
Found 1 error(s). Please correct the portfile:
    c:\Libs\vcpkg\ports\bitforge\portfile.cmake
-- Performing post-build validation done
Elapsed time for package bitforge:x86-windows: 4.188 s
Error: Building package bitforge:x86-windows failed with: POST_BUILD_CHECKS_FAILED
Please ensure you're using the latest portfiles with `.\vcpkg update`, then
submit an issue at https://github.com/Microsoft/vcpkg/issues including:
  Package: bitforge:x86-windows
  Vcpkg version: 0.0.113-nohash
Additionally, attach any relevant sections from the log files above.

Basicamente isso é o que você precisa para começar a construir seu pacote:

c:\Libs\vcpkg>vcpkg.exe build bitforge
-- Using cached C:/Libs/vcpkg/downloads/bitforge-18.9.12.zip
-- Configuring x86-windows
-- Building x86-windows-dbg
-- Building x86-windows-rel
-- Installing: C:/Libs/vcpkg/packages/bitforge_x86-windows/share/bitforge/copyright
-- Performing post-build validation
-- Performing post-build validation done
Elapsed time for package bitforge:x86-windows: 4.224 s

O próximo passo é instalar:

c:\Libs\vcpkg>vcpkg.exe install bitforge
The following packages will be built and installed:
    bitforge[core]:x86-windows
Starting package 1/1: bitforge:x86-windows
Building package bitforge[core]:x86-windows...
-- Using cached C:/Libs/vcpkg/downloads/bitforge-18.9.12.zip
-- Configuring x86-windows
-- Building x86-windows-dbg
-- Building x86-windows-rel
-- Installing: C:/Libs/vcpkg/packages/bitforge_x86-windows/share/bitforge/copyright
-- Performing post-build validation
-- Performing post-build validation done
Building package bitforge[core]:x86-windows... done
Installing package bitforge[core]:x86-windows...
Installing package bitforge[core]:x86-windows... done
Elapsed time for package bitforge:x86-windows: 4.239 s
Total elapsed time: 4.239 s
c:\Libs\vcpkg>dir /s /b installed\x86-windows\bitforge.*
c:\Libs\vcpkg\installed\x86-windows\debug\lib\bitforge.lib
c:\Libs\vcpkg\installed\x86-windows\include\bitforge.h
c:\Libs\vcpkg\installed\x86-windows\lib\bitforge.lib
c:\Libs\vcpkg\installed\x86-windows\share\bitforge

E voilá! Agora o include está disponível, as funções estão disponíveis, o link está funcionando e seu pacote pode ser compartilhado com toda a empresa. Basta copiar a pasta ports/bitforge ou adicioná-la no repositório por um commit.


# Receita de café no Reddit

Caloni, 2018-09-13 food coffee [up] [copy]

Acabei de fazer o café seguindo as dicas do Reddit (que li novamente na insônia). O Loretto ficou absurdamente melhor apenas seguindo algumas dicas: proporção 1:15 do pó, coagem mais fina para menos tempo em contato com a água, deixando a água mais próxima da fervura (pesquise por colar de pérolas). O sabor do café ficou mais presente (menos aguado) com dez segundos mexendo, um minuto e meio em contato com a água desde o começo e trinta segundos baixando o êmbolo.


# Final Space

Caloni, 2018-09-15 cinema series [up] [copy]

Esta é uma série animada da Netflix que apresenta uma espécie de anti-herói (embora bem infantil) e personagens que possuem a pretensão de estarem em uma super-aventura galáctica. Só que não. E por algum motivo, apesar de irreverente, lhe escapa esse sentido do humor: que quando você tira importância de alguns elementos da história, mas mantém outros, você não entendeu direito a ideia por trás do seu próprio projeto.

Este desenho possui traços simples com estilo vetorial e situações ligeiramente engraçadas. A partir do terceiro episódio já fica chato, pois é repetitivo e as melhores piadas já foram gastas na primeira hora. Agora a única coisa que seguraria alguém assistindo seria o desfecho, onde nosso herói está perdido no espaço contemplando seus últimos oito minutos de existência. É sério, isso? Ricky & Morty já decifraram todo esse suspense animatrônico com o uso de boas ideias. Alguns criadores poderiam tentar de vez em quando.


# Limites

Caloni, 2018-09-15 cinema movies [up] [copy]

Este é o típico filme fácil de classificar: road trip sobre família disfuncional com o peso de um drama intimista sobre relacionamentos e traumas do passado. Ele também é fácil de acompanhar, pois não há nada além que aconteça em sua história que nos fará olhá-lo de novo. Ele também é um dos piores filmes para se escrever a respeito, porque ele não é tão bom a ponto de ser digno de nota nem tão ruim a ponto de ser digno de pena.

O que há para se dizer é que Vera Farmiga e Christopher Plummer não seguram a onda. Farmiga se dá bem em papéis menos intimistas (ou mais superficiais), como a médium em Invocação do Mal, mas aqui ela desempenha um papel crucial para levar o espectador a entender seu problema, e o que acontece é que ela simplesmente não está lá para nós. Já Plummer, é divertido de vê-lo atuar, mas alguma coisa não está certa nas dezenas de diálogos supostamente espirituosos que o entregaram.

Talvez seja o roteiro de Shana Feste (do para mim ótimo Amor Sem Fim), que veste do começo ao fim a carapuça de filme medíocre e não se preocupa em criar nenhuma reviravolta que se preze, mantendo a história em fogo morno todo o tempo. Sabemos minutos antes de acontecer uma "surpresa", e nada disso acaba soando natural (exceto quando seus personagens não condizem com o que esperaríamos que eles fizessem, como o episódio envolvendo uma pá). Shana parece estar conduzindo o filme a 50/h enquanto o espectador já está na estrada principal. Quando eles chegam em São Francisco nós já estamos quase voltando.

Ao mesmo tempo temos a comédia, que não funciona: um traficante de drogas velhinho vendendo maconha para monges budistas não é engraçado. É apenas peculiar. Da mesma forma todos os episódios do filme são apenas episódios peculiares para deixar claro como o personagem de Plummer é tão problemático. E a cartilha de seu personagem é completa: ele visita seus amigos e diferente do que imaginaríamos são todos gente boa (inclusive há participações especiais curiosas, como Christopher Lloyd e Peter Fonda), e aos poucos cria uma amizade com seu neto, porque mesmo que isso não seja importante para a história é preciso cumprir a cartilha do gênero.

E ao mesmo tempo que o roteiro de Shana Feste não ajuda, sua direção é intervencionista demais. Talvez para tentar desesperadamente chamar atenção para uma história que não está funcionando com o elenco escolhido Shana resolve tremer mais a câmera e muitas vezes impedir o espectador de ver os atores atuando, o que é péssimo para um drama. Note como quando somos apresentados ao personagem de Christopher Lloyd mal conseguimos reparar em como ele envelheceu, pois além do enquadramento deixá-lo sempre no topo da tela a câmera não pára de tremer. E nada disso é necessário no filme (como ele se refrescar pelado em sua varanda e convidar suas visitas).

Mas não deveria estar surpreso com a incapacidade de Shana Feste trazer alguma alma ao projeto: em um filme literalmente infestado de pets não se explora em nenhum momento essa busca interminável da personagem de Farmiga pelo aconchego inexistente em sua infância com pai ausente através de seus animaizinhos acolhidos da rua. São apenas mais uma coleção de esquisitice ao rol do filme, ele próprio se tornando um pouco esquisito no final das contas. Sobre o que era a história, mesmo?


# Coroutine Internals

Caloni, 2018-09-18 computer [up] [copy]

Uma corrotinas é um mecanismo de troca de contexto onde apenas uma thread está envolvida. Ela me faz lembrar do Windows 3.0, não exatamente por não existirem threads (e não existiam mesmo), mas pelo caráter cooperativo dos diferentes códigos.

Só que no caso do Windows se a rotina de impressão travasse todo o sistema congelava.

A volta das corrotinas via C++ moderno ocorre, para variar, no Boost. E a arquitetura é simples: mantenha um histórico das stacks das diferentes tasks da thread. Vamos pegar o caso mais simples da Boost.Coroutine para analisar:

#define BOOST_COROUTINES_NO_DEPRECATION_WARNING // Já existe uma nova versão de Coroutine, a 2, e a 1 está sendo abandonada.
#include <boost/coroutine/all.hpp>
#include <iostream>
using namespace boost::coroutines;
void cooperative(coroutine<void>::push_type &sink)
{
    std::cout << "Hello";
    sink();
    std::cout << "world";
}
int main()
{
    coroutine<void>::pull_type source{ cooperative };
    std::cout << ", ";
    source();
    std::cout << "!\n";
}

Se você já é um programador esperto já deve ter percebido que na saída do prompt será impresso "Hello, world!", com a vírgula no meio sendo impressa pela função main e as duas palavras da ponta pela função cooperative, ainda que ela seja chamada apenas uma vez.

Note que falei chamada porque se a stack não retornou da função ela não terminou ainda seu trabalho. Não houve o "return". Outra forma de entender isso é que ela é chamada aos poucos. Enfim, deixo para você a discussão semântica. O fato é que a saída é "Hello, world":

Vamos depurar.

Oh, oh! A stack de cooperative nos indica que ela não partiu do main, apesar de ter sido chamada através da construção de `coroutine<void>::pull_type`. O método sink chamado logo após imprimir "Hello" deve colocar essa rotina para dormir, voltando o controle para main. Vamos ver como isso é feito.

https://www.youtube.com/embed/xoAxig6vdTM

Oh, não. O depurador do Visual Studio está fazendo caquinha, pois rodando passo-a-passo voltei para a mesma função cooperative sem passar pelo main. No entanto, a vírgula ", " foi impressa.

Para conseguirmos depurar diferentes rotinas dentro da mesma thread é imperativo entendermos como o mecanismo de troca de contexto funciona por baixo dos panos. Para isso nada como depurar as próprias trocas de contexto.

typedef void ( * coroutine_fn)( push_coroutine< void > &);
explicit pull_coroutine( coroutine_fn fn, attributes const& attrs = attributes() )
{
    // create a stack-context
    stack_context stack_ctx;
    stack_allocator stack_alloc;
    // allocate the coroutine-stack
    stack_alloc.allocate( stack_ctx, attrs.size);
    BOOST_ASSERT( 0 != stack_ctx.sp);
    // typedef of internal coroutine-type
    typedef detail::pull_coroutine_object<
        push_coroutine< void >, void, coroutine_fn, stack_allocator
    >                                       object_t;
    // reserve space on top of coroutine-stack for internal coroutine-type
    std::size_t size = stack_ctx.size - sizeof( object_t);
    BOOST_ASSERT( 0 != size);
    void * sp = static_cast< char * >( stack_ctx.sp) - sizeof( object_t);
    BOOST_ASSERT( 0 != sp);
    // placement new for internal coroutine
    impl_ = new ( sp) object_t(
            boost::forward< coroutine_fn >( fn), attrs, detail::preallocated( sp, size, stack_ctx), stack_alloc); 
    BOOST_ASSERT( impl_);
    impl_->pull();
}

O tamanho total da stack reservada no Windows é de 1 MB, mas a granuralidade padrão é de 64 KB ("que é suficiente para qualquer um" - Gates, Bill). Então é por isso que quando o Boost aloca uma stack com atributos padrões esse é o tamanho que vemos (65536).

The default size for the reserved and initially committed stack memory is specified in the executable file header. Thread or fiber creation fails if there is not enough memory to reserve or commit the number of bytes requested. The default stack reservation size used by the linker is 1 MB. To specify a different default stack reservation size for all threads and fibers, use the STACKSIZE statement in the module definition (.def) file. The operating system rounds up the specified size to the nearest multiple of the system's allocation granularity (typically 64 KB). To retrieve the allocation granularity of the current system, use the GetSystemInfo function.

_Detalhe curioso de arquitetura x86 (32 bits): na hora de alocar, o sp (stack pointer) aponta para o final da pilha. Isso porque no x86 a **pilha cresce "para baixo"**._

ctx.sp = static_cast< char * >( limit) + ctx.size;

Logo em seguida, no topo da pilha, é empilhado o objeto da corrotina:

typedef pull_coroutine_object<push_coroutine, coroutine_fn, stack_allocator> object_t;
void * sp = static_cast<char*>(stack_ctx.sp) - sizeof( object_t);
impl_ = new (sp) object_t(boost::forward<coroutine_fn>(fn), attrs, detail::preallocated(sp, size, stack_ctx), stack_alloc); 

Bom, entrando mais a fundo na implementação de corrotinas do Boost, temos o objeto **pull_coroutine_impl**, que possui flags, ponteiro para exceção e o contexto do chamador e do chamado para se localizar.

template<>
class pull_coroutine_impl< void > : private noncopyable
{
protected:
    int                     flags_;
    exception_ptr           except_;
    coroutine_context   *   caller_;
    coroutine_context   *   callee_;

O **coroutine_context** possui elementos já conhecidos de quem faz hook de função: trampolins. Ou seja, funções usadas para realizar saltos incondicionais de um ponto a outro do código independente de contexto. Na minha época de hooks isso se fazia alocando memória na heap e escrevendo o código assembly necessário para realizar o pulo, geralmente de uma colinha de uma função naked (funções naked não possuem prólogo e epílogo, que são partes do código que montam e desmontam contextos dentro da pilha, responsável pela montagem dos frames com ponto de retorno, variáveis locais, argumentos).

// class hold stack-context and coroutines execution-context
class BOOST_COROUTINES_DECL coroutine_context
{
private:
    template< typename Coro >
    friend void trampoline( context::detail::transfer_t);
    template< typename Coro >
    friend void trampoline_void( context::detail::transfer_t);
    template< typename Coro >
    friend void trampoline_pull( context::detail::transfer_t);
    template< typename Coro >
    friend void trampoline_push( context::detail::transfer_t);
    template< typename Coro >
    friend void trampoline_push_void( context::detail::transfer_t);
    preallocated            palloc_;
    context::detail::fcontext_t     ctx_;

A função que faz a mágica do pulo do gato é a pull, que muda o estado da rotina para running e realiza o salto de contexto. Vamos analisar essa parte com muita calma.

inline void pull()
{
    BOOST_ASSERT( ! is_running() );
    BOOST_ASSERT( ! is_complete() );
    flags_ |= flag_running;
    param_type to( this);
    param_type * from(
            static_cast< param_type * >(
                caller_->jump(
                    * callee_,
                    & to) ) );
    flags_ &= ~flag_running;
    if ( from->do_unwind) throw forced_unwind();
    if ( except_) rethrow_exception( except_);
}

Quem desfaz a mágica, "desempilhando" o contexto para voltar ao chamador da corrotina (através do contexto apenas, não da pilha) é a função push.

inline void push()
{
    BOOST_ASSERT( ! is_running() );
    BOOST_ASSERT( ! is_complete() );
    flags_ |= flag_running;
    param_type to( this);
    param_type * from(
            static_cast< param_type * >(
                caller_->jump(
                    * callee_,
                    & to) ) );
    flags_ &= ~flag_running;
    if ( from->do_unwind) throw forced_unwind();
    if ( except_) rethrow_exception( except_);
}

Com os dados disponíveis nos objetos de contexto (no exemplo do main, a variável source) é possível pelo Windbg analisar qualquer tipo de stack com o comando **k**.

A variável de uma coroutine contém o contexto do chamador e do chamado. Quando houver a necessidade de explorar uma pilha não-ativa é preciso obter o valor de **sp** através dessa variável. Ela fica um pouco escondida, mas está lá. Acredite.

Usando o comando `k = BasePtr StackPtr InstructionPtr` passando o conteúdo de sp como o stack pointer o Windbg deve mostrar a pilha de todas as formas possíveis (especificar se terá FPO, mostrar código-fonte, argumentos, etc). Para a demonstração live fica bom ter um loop "eterno" para poder repetir a análise quantas vezes forem necessárias:

void cooperative(coroutine<void>::push_type &sink)
{
    while( g_stopAll == false )
    {
        boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
        sink();
        std::cout << "Hello";
        boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
        sink();
        std::cout << "world";
    }
}
int main()
{
    coroutine<void>::pull_type source{ cooperative };
    while( g_stopAll == false )
    {
        source();
        std::cout << ", ";
        source();
        std::cout << "!\n";
    }
}
0:000> ~kvn
 # ChildEBP RetAddr  Args to Child              
00 00b707c4 00f39668 00b708c8 075d31e5 00b70a08 coroutines_cooperative!cooperative+0xa0 (FPO: [Non-Fpo]) (CONV: cdecl) [c:\projects\caloni\static\samples\coroutines_cooperative\coroutines_cooperative.cpp @ 19] 
01 00b70910 00f18b5b cdcdcdcd cdcdcdcd 00f01fd2 coroutines_cooperative!boost::coroutines::detail::pull_coroutine_object<boost::coroutines::push_coroutine<void>,void,void (__cdecl*)(boost::coroutines::push_coroutine<void> &),boost::coroutines::basic_standard_stack_allocator<boost::coroutines::stack_traits> >::run+0xf8 (FPO: [Non-Fpo]) (CONV: thiscall) [c:\libs\vcpkg\installed\x86-windows\include\boost\coroutine\detail\pull_coroutine_object.hpp @ 281] 
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Projects\caloni\static\samples\coroutines_cooperative\Debug\boost_context-vc141-mt-gd-x32-1_67.dll - 
02 00b70a08 54261075 0075f4f0 0075f528 ffffffff coroutines_cooperative!boost::coroutines::detail::trampoline_pull<boost::coroutines::detail::pull_coroutine_object<boost::coroutines::push_coroutine<void>,void,void (__cdecl*)(boost::coroutines::push_coroutine<void> &),boost::coroutines::basic_standard_stack_allocator<boost::coroutines::stack_traits> > >+0x9b (FPO: [Non-Fpo]) (CONV: cdecl) [c:\libs\vcpkg\installed\x86-windows\include\boost\coroutine\detail\trampoline_pull.hpp @ 42] 
WARNING: Stack unwind information not available. Following frames may be wrong.
03 00b70a14 ffffffff 77c49ec1 cdcdcdcd cdcdcdcd boost_context_vc141_mt_gd_x32_1_67!make_fcontext+0x75
...
0a 00b70a30 00000000 00000000 00000000 00b70a48 coroutines_cooperative!boost::coroutines::detail::pull_coroutine_object<boost::coroutines::push_coroutine<void>,void,void (__cdecl*)(boost::coroutines::push_coroutine<void> &),boost::coroutines::basic_standard_stack_allocator<boost::coroutines::stack_traits> >::`vftable'
.detach

_Dica: É importante detachar do processo, mesmo que estejamos analisando em modo não-invasivo, porque a porta de Debug pode ser ocupada e o Visual Studio vai ficar pra sempre esperando receber eventos de debug que ele não vai mais receber._

Após rodarmos novamente o programa ele pára no main. Podemos atachar com o WinDbg quantas vezes precisarmos:

0:000> ~*kvn
.  0  Id: 1b50.a58 Suspend: 1 Teb: 00b8c000 Unfrozen
 # ChildEBP RetAddr  Args to Child              
00 00cff9cc 00f3cc8e 00000001 030b6998 030b84d8 coroutines_cooperative!main+0x9a (FPO: [Non-Fpo]) (CONV: cdecl) [c:\projects\caloni\static\samples\coroutines_cooperative\coroutines_cooperative.cpp @ 32] 
01 00cff9e0 00f3cb27 3c5cdfac 00f02a9a 00f02a9a coroutines_cooperative!invoke_main+0x1e (FPO: [Non-Fpo]) (CONV: cdecl) [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78] 
02 00cffa3c 00f3c9bd 00cffa4c 00f3cd08 00cffa60 coroutines_cooperative!__scrt_common_main_seh+0x157 (FPO: [Non-Fpo]) (CONV: cdecl) [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 
03 00cffa44 00f3cd08 00cffa60 760f8654 00b89000 coroutines_cooperative!__scrt_common_main+0xd (FPO: [Non-Fpo]) (CONV: cdecl) [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 331] 
04 00cffa4c 760f8654 00b89000 760f8630 af8d0600 coroutines_cooperative!mainCRTStartup+0x8 (FPO: [Non-Fpo]) (CONV: cdecl) [f:\dd\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17] 
05 00cffa60 77c24a47 00b89000 7348514c 00000000 KERNEL32!BaseThreadInitThunk+0x24 (FPO: [Non-Fpo])
06 00cffaa8 77c24a17 ffffffff 77c49ee4 00000000 ntdll!__RtlUserThreadStart+0x2f (FPO: [SEH])
07 00cffab8 00000000 00f02a9a 00b89000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])

# De Volta ao Jogo (John Wick)

Caloni, 2018-09-28 cinema movies [up] [copy]

Um drama pessoal que escala em uma espiral de violência e poder em um universo que caminha confortavelmente entre realismo e absurdo. O drama de perder a esposa doente depois de abandonar o trabalho que dava significado à sua vida é um realismo pesado. Roubarem seu carro e matarem o cachorro que a esposa lhe dera como último presente é de um absurdo dilacerante que motiva qualquer um a sair em busca de vingança. E se você é John Wick isso significa uma sentença de morte para muita gente.

Dirigido com um cuidado especial pelos diretores Chad Stahelski e David Leitch, onde o primeiro trabalhou na equipe de dublês em filmes de ação e o segundo estava envolvido em trabalhos similares, as cores e enquadramentos harmonizam lado a lado a atmosfera de solidão; o uso de uma tela mais estreita permite que o filme coloque John Wick no começo do filme em isolamento completo. No funeral, na chuva e com toda a cidade ao fundo, ele tem um único amigo que o cumprimenta debaixo de uma árvore. Já sua casa é gigantesca e que se torna ainda mais com o uso minimalista da direção de arte que usa poucos enfeites, e onde as cores frias desempenham o papel de estado de humor do personagem, à beira da depressão.

Se você se surpreende com esses detalhes que descrevo em um filme de ação sanguinário é porque este é um trabalho que se destaca de todos os demais. Ele possui alma. Seu protagonista é uma força da natureza. Surpreende que que ele seja interpretado pelo inexpressivo Keanu Reeves, mas mais surpreendente é que funciona. John Wick é uma pessoa simples demais para um ator mais flexível. Ele só queria viver em paz. Já que não pode o jeito é partir para a ignorância.

Este é um filme longo e que consegue ter cenas de ação mais longas que o normal sem sentirmos cansaço. Mais uma vez acima da média, a coreografia das cenas mantêm o espectador sempre atento para descobrir como John irá passar por tantos capangas e esquemas de segurança que foram colocados entre ele e seu alvo.

Aliás, outro toque de classe na obra é seu próprio universo de assassinos. Há um valor em comum para transações que não são rastreáveis e que não perdem o valor nem para o dólar: moedas de ouro. Há um hotel onde é proibido assassinatos para que possam descansar em paz, e quebrar essa regra, pode apostar, é o que um filme desses deve fazer.

Este é um roteiro previsível assinado por Derek Kolstad, que já trabalhou em filmes e séries do gênero como A Última Bala e Hitman. Sua estrutura simples torna claro o desfecho da história, e nisso você pode apostar em previbilidade. Mas quando se nota os personagens secundários e suas sutis reviravoltas (como a ponta de Willem Dafoe), ou como quase nada precisa realmente ser dito -- o aspecto descritivo do roteiro é o que garante nosso prazer visual -- daí o filme mantém um pouco de caos para uma diversão eventual. O suficiente para nos preocuparmos com o enredo.

"John Wick" mantém uma capa de realismo para usar frases de efeito como "você roubou meu carro e matou meu cachorro" ou "uma vez ele matou três caras com apenas um lápis" e não soar fantástico demais; é mais um escapismo delicioso do gênero de ação. E fica ainda melhor quando temos um protagonista tão visceral quanto este. Keanu Reaves é a força da natureza sem expressão que quando se sente ameaçado é melhor você estar do outro lado da tela.


# Legalize Já! Amizade Nunca Morre

Caloni, 2018-09-28 cinemaqui cinema movies [up] [copy]

Quase trinta anos após iniciado um movimento artístico liderado pela banda Planet Hemp, "Legalize Já!" ainda soa provocador. Extraído de um momento na história do país onde política e alienação andavam de mãos juntas, a mini biografia de Marcelo D2 e Skunk é muito mais sobre uma amizade inusitada do que uma crítica social.

Mas claro que nos tempos atuais a roupagem do filme vai tentar de todas as formas ser uma crítica social. O que me dá muita preguiça, pois repete mecanicamente a mesma fórmula da dualidade opressão/oprimido que é alimentado pelo sistema, algo já utilizado em centenas de trabalhos semelhantes. Essa realmente não é a parte boa da história. A parte boa mesmo é a amizade natural entre esses dois jovens.

Skunk é um negro vindo do microcosmos artístico da contracultura, inspirado por bandas que cantam suas mágoas de crescer em um ambiente sem oportunidades de viver de sua arte. (O irônico aqui é que essas bandas, ao se tornarem influenciadoras, passam a literalmente viver de sua arte.) Skunk é uma força da natureza que dá seus últimos suspiros na Terra. Tendo AIDS na década de 90 e se recusando a tomar seus remédios, o espectador já sabe o que esperar: este será o mártir da história.

Justamente por isso sua caracterização no filme é iconográfica demais. Interpretado com uma naturalidade ímpar por Ícaro Silva, que sintetiza uma figura indignada com a sociedade e fazendo valer suas ideias em fitas K7 que distribui aleatoriamente entre a multidão, não sabemos muito sobre sua vida, nem seus pecados, nem seus desejos. Exceto um: depois de topar acidentalmente com Marcelo (uma cena em paralela empolgante), torná-lo o bastião das boas novas da música de revolta no cenário brazuca. É estiloso, ele se veste bem, mas algo soa falso nesse personagem da vida real que sempre é abordado pela polícia e tem suas fitas jogadas no chão. Importante lembrar que o DJ que vive andando por aí espalhando suas fitas é tão clichê que já nos anos 90 era batido. Nem por isso a atuacão de Ícaro se torna menos interessante.

Já Marcelo, como não poderia deixar de ser, partindo de um argumento do próprio Marcelo D2 que levou à produção do filme, é um herói anti-establishment. Vive na casa do pai, mas logo terá que seguir o próprio rumo, pois sua namorada está grávida e eles precisam do dinheiro para abortar, e é curioso notar como "Legalize Já!" passa longe dessa outra atividade considerada crime ainda hoje em solo nacional. Enquanto Ícaro convence com a aparição misteriosa de Skunk, Renato Góes leva a pior, pois precisa viver um personagem que é muito mais ele apenas quando está cantando. Na maioria do tempo ele é um cidadão anônimo em busca do seu lugar ao sol.

Se Skunk parte de uma fábula do negro injustiçado, Marcelo tem poucas chances de ter seu drama levado a sério. Seu maior sacrifício na vida é se sujeitar a trabalhar de vendedor em uma loja de eletrodomésticos. Bom, se considerarmos que na loja as músicas escolhidas para entreter o cliente são "na boquinha da garrafa" e similares, hits dos anos 90, talvez o filme tenha seus motivos para dramatizar a rotina sufocante de Marcelo, que já escrevia suas letras de revolta há um tempo.

De qualquer forma, o melhor no filme continua sendo a amizade desses dois. Não há tensão suficiente para construir outra narrativa mais poderosa enquanto os dois conseguem criar uma dinâmica que mantém, sob o som de suas obras, um ótimo panorama do que era viver de maneira crítica na primeira década de reabertura do país.

Ainda assim, o filme tenta traçar seu tom realista e dramático através das cores drenadas em uma fotografia pálida e chapada. Quase preto e branco. Mensagem subliminar ou uma forma elegante de criar enquadramentos granulados e evocativos de uma vida sem esperança de melhora?

Note como o uso do enquadramento, por exemplo, coloca Marcelo como um cara minúsculo em frente a uma paisagem urbana que intimida e aliena, ou como Skunk vira mais um artefato em meio a pôsteres da casa de shows underground Garage, para através do zoom virar personagem principal, como se ele próprio fosse o próximo pôster a ser pendurado na parede como uma lembrança saudosa.

Filmes de bandas sempre possuem a vantagem da trilha sonora e da dinâmica do vídeo clipe. Aqui não é diferente. Com o adendo da amizade dos dois. Isso é suficiente para assistir esse filme se você for fã, e um motivo razoável se aprecia biografias de bandas. Para o resto, quem não curte uma viagem temporal regada a maconha? Apenas as autoridades ainda não perceberam.


# Native Floripa 2018

Caloni, 2018-09-28 [up] [copy]

O Native Floripa desse ano foi um evento de nerds que adoro e também uma viagem e encontro de nerds (que também adoro). Isso quer dizer que este é um post duplo, onde analiso tanto a viagem quanto o evento.

Como viagem Floripa é uma cidade que se divide em ilha e continente. Na ilha há um emaranhado de rodovias que circulam pelos morros e que se cruzam onde percebemos que a prefeitura não tem o mínimo de cuidado e investimento em fazer conexões decentes. Há dois elevados em obras que facilitaram a transição entre rodovias. Vimos um no caminho do aeroporto para Barra da Lagoa, e o motorista do Uber comentou que está há dez anos em obras, já tendo gasto todo o dinheiro em um amontoado de madeira e concreto inúteis, o que resume o que tenho a dizer sobre a organização da cidade como um todo.

Felizmente o povo do sul é um povo decente, de respeito e que graças a Deus não parece ter dado muita atenção nos movimentos de "justiça social" que assolam o país. Há uma casa de coxinhas muito boa, a Maria Coxinha, onde um dos pratos se chama Kibexinha. Isso é tudo que precisa ser dito sobre a saúde do povo da cidade.

O evento teve lugar, como no ano passado, na Acate, uma incubadora de startups, nos dias 22 e 23 de setembro de 2018 (sábado e domingo). Houve em alguns momentos duas trilhas, que eu condeno por ser um evento pequeno, mas no salão principal houve a filmagem para publicação na internet, que eu invejo, pois logo teremos disponível para todos as palestras da trilha principal. Essa filmagem se torna ainda mais especial quando se percebe que houve muitos poucos participante no evento, girando em torno de 20 no sábado e 10 no domingo. As palestras do ano passado já se encontram publicadas, mas como houve uma demora de alguns meses talvez esse ano teremos a mesma espera.

Confesso que fazia um bom tempo que não participava de um evento como esses. Nossa tentativa de realizar o próximo encontro em sp miou por falta de público em um momento em que C++ está em obras e com discussões importantíssimas sobre a linguagem e bibliotecas necessárias. O Native Floripa atendeu essa necessidade em pelo menos algumas palestras.

O destaque do evento com certeza foram as corrotinas em C++. Verdadeiras máquinas de performance onde se economiza troca de contexto, houve três palestras sobre o assunto, podemos dizer. Duas delas ministradas por Vinicius, mantenedor da Boost.Http, onde ambas dialogam sobre a fascinante questão de como adequar o uso de corrotinas sem interferir no fluxo do programa. A terceira palestra é minha, onde discurso sobre a dificuldade atual de depurar corrotinas sem ferramentas atualizadas para este "novo" paradigma.

Outra palestra que me lembro com muita empolgação é a sobre WebAssenbly. Não torça o nariz antes de entender. O palestrante nos apresenta algo ainda em andamento sobre transpilar código C/C++ para uma máquina virtual criada a partir de JavaScript. A estrutura da palestra é muito boa e o palestrante melhor ainda. Ele chegou a alterar o código durante a palestra para nos demonstrar diversos usos dessa tecnologia. Ainda em testes, mas muito promissora.

Por fim, as conversas entre os palestrantes e os participantes foi muito frutífera. Assim como nosso grupo do Telegram, importa menos o tema do que as pessoas envolvidas. E todos concordam que não há nada melhor no mundo que conversar com pessoas inteligentes e beber chopes do Sul. E lá na Acate há a melhor praça de alimentação que já vi na vida. Comida e bebida (chopes e vinhos por taça) boa e barata. Há massas e carnes de muita qualidade. O Madero do lado, que já não é nada de mais, ficou ainda menor.

O chope Putz IPA têm lúpulos cheirosos e corpo leve, pouco alcoólico e bem agradável gelado. Ou seja, nem parece IPA.
Coruja Pilsen é uma... pilsen? Bom, tem um salgadinho próximo de uma witbier prestes a ser esquecida.
O chope da Strappa Gingerberry é um chá preto fermentado com gengibre e gradação alcoólica ridícula de 0.6%. Ele é ainda frutado (possui morango) e deixa a garganta quentinha. Ótima opção entre bebidas mais fortes. Dá uma pausa etílica e estilosa na balada.
Kairós Sol Poente é uma West Coast IPA de respeito. Equilibrada de corpo leve, seu chope é aromático e um pouco salgado com amargor presente sem exageros.

Ficamos hospedados em uma casa na Barra da Lagoa pelo AirBnB em alta concentração de temas filosóficos. Fizemos um churrasco imprestável, fomos em restaurantes medíocres da orla, mas a conversa foi sempre interessante. Era como se o grupo de filosofia do Telegram tivesse se mudado temporariamente para lá. Temas como metafísica, política, social justice, auto ajuda e imprint de traumas eram frequentes. A paisagem belíssima, a casa aconchegante e a companhia agradável.

Native Floripa virou já uma tradição. Ano que vem nos vemos de novo.

Minhas palestras


# Maniac

Caloni, 2018-09-30 cinema series [up] [copy]

Dois estranhos se encontram casualmente. Ambos possuem traumas no passado. Um deles é curável, e o outro tem esquizofrenia. Ambos parecem pensar na mesma frequência e frequentam os mesmos sonhos gerados por computador. Ambientado em um mundo ligeiramente futurista e esquisito, com um toque de retrô, a série Maniac tem um quê estético (e temático) que aos poucos nos revela ser um sci-fi pautado em algumas ideias muito mais psicológicas e filosóficas que poderia-se esperar.

Dirigido do começo ao fim por Cary Joji Fukunaga (do sensacional Beasts of No Nation e Jane Eyre), os episódios são pequenos momentos em um experimento que pretende curar o sofrimento humano (no sentido psicológico). Visto como máquinas defeituosas pelos pesquisadores Dr. James K. Mantleray (Justin Theroux) e a Dra. Azumi Fujita (a deliciosa andróide de Ex-Machina Sonoya Mizuno), o trabalho de uma vida de Mantleray se baseia na personalidade de sua mãe, autora de sucesso de livros de auto-ajuda (Sally Field, interessante). Além disso, esta é uma das inúmeras iterações que através de três drogas que geram fases distintas de tratamento, pretende criar a droga mais aguardada da humanidade: a que nos tira a própria humanidade e nos transforma em robôs.

A pegada da série é lúdica e cheia de detalhes divertidos para acompanhar, como as diferentes referências da vida real que viram elementos importantíssimos nas aventuras virtuais dos pacientes (como o livro de Miguel de Cervantes, Don Quixote, não por acaso sobre um maluco que vê gigantes onde há moinhos de vento). E ela nos entrega de bandeja uma análise não dos pacientes em si, mas apenas do casal principal, interpretados por Emma Stone (La La Land) e um Jonah Hill magrinho (O Homem que Mudou o Jogo). Porém, mais que isso, essa também é uma análise dos cientistas por trás da pesquisa, e embora essa seja a parte mais interessante do "longa" (são 10 episódios com menos de 1 hora cada) ela sempre se mantém abaixo da superfície, para que os espectadores mais atentos percebam os detalhes não tão sutis das interações entre a casa de cientistas já citados e os trejeitos e como cada um lida com os problemas que eventualmente começam a acontecer no centro de pesquisa. Não é apenas a pilha gigantesca de cigarros da Dra. Fujita; olhe mais de perto.

Emma Stone ganha o público com uma interpretação maleável, que a quase transforma em outra personagem nas diferentes aventuras dentro do tratamento que virtualiza mundos para que os pacientes interajam com seus problemas na vida real. Uma hora ela é uma cuidadora de idosos que resolve salvar um guaxinim de uma gangue de vendedores de peles, enquanto em outra hora ela é uma elfa que com sua irmã explora as profundezas de seu psiquê. Stone nunca se limita a criar outra persona, mas interage em diferentes níveis entre elas. Há lapsos de memória que conseguem manter uma ligação orgânica, ainda que não completa. E isso é o que nos entrega uma personagem multifacetada.

Já Jonah Hill mais uma vez prova que é ótimo em ser protagonista. Sua esquizofrenia é a parte menos interessante do que sua tentativa sempre frustrada de fugir da passividade esmagadora de sua vida real. Em seus sonhos ele é mais ativo, mas não deixa de ser apenas um elemento em cena que vai ligando pontos. A sua busca é mais enigmática, e assim como tudo na série, os detalhes do seu passado você irá ter que ir pescando em um diálogo ou outro. Porém, Hill consegue manter uma identidade própria através dos sonhos diferentes de Stone. Ele é alguém que precisa de cuidados, mesmo que sob controle. As interações entre os dois é o mais interessante de acompanhar por causa dessa dinâmica diferente dos papéis, e por isso nos últimos episódios isso falta um pouco.

Não apenas o design de produção, mas fotografia, figurino, cenários e direção transformam cada episódio em uma imersão quase completa na linguagem psicológica de exploração de quem somos nós. Dessa forma, quando algum problema ocorre em um determinado mundo, não é o problema em si que nos interessamos, mas como ele se manifesta e se relaciona com os personagens da vida real e o que estão enfrentando dentro de suas cabeças. Há um jogo de várias camadas acontecendo, ainda que a série a tente banalizar um pouco, ela é muito mais do que é visto na superfície. Observe o sonho sobre o alienígena e perceba como a fantasia por trás da história é absurda para que consigamos adentrar no verdadeiro problema: o personagem de Jonah Hill sente que estraga até as tarefas mais importantes de sua vida (como seu relacionamento), colocando tudo a perder meio que sem querer (esse sonho ainda tem um plano-sequência de tirar o fôlego).

Como não poderia deixar de ser há inúmeras referências, propositais ou não, das situações no Cinema que unem inteligência artificial com sonhos com psicose com traumas e com análise de personagens (ainda que esses sejam meio estereotipados). Podemos de imediato nos lembrar de 2001 - Uma Odisseia no Espaço e seu HAL-9000 (como não lembrar, já que há LEDs piscando em tudo quanto é lado, cores primárias como identificadores e até as letras dos monitores lembram uma IBM da vida, de onde HAL tirou seu nome). Também há um quê de Brilho Eterno de uma Mente Sem Lembranças, pois sonhos e realidade se misturam nos detalhes.

Maniac dá impressão de não conseguir explorar muito bem seus personagens secundários, que só são importantes para criar a atmosfera nos primeiros episódios. Os diversos assuntos que se abrem nesse tema também ficam um tanto inexplorados, e talvez essa seja uma estratégia para tornar o espectador mais ativo a respeito dos problemas inerentes da busca por significado dos humanos na vida. Há de se pensar se a série como um todo não é um grande experimento.


[2018-08] [2018-10]