.NET CLI

Depois de quase dois anos trabalhando com ele, que passou por diversos nomes diferentes (k, kre, xre, dnx), está na hora de nos despedirmos. O DNX está oficialmente morto. E com ele o DNVM, e o DNU.

Mas calma, pessoal, muita calma. O que aconteceu é que o .NET CLI, junto com outras tecnologias, será o substituto do DNX. Toda aquela tecnologia que estava sendo construída segue sendo construída. O que aconteceu foi que o time de ASP.NET, que liderava esse esforço, agora está trabalhando com o time do .NET, e o projeto ficou muito maior. Sim, eu havia cantado essa bola, lá atrás, quando a tecnologia foi anunciada e o Victor Cavalcante e eu gravamos o 15º episódio do Tecnoretórica (ele volta, ele volta…) em 12 de Maio de 2014, quase dois anos atrás, e depois no episódio 16, quatro dias depois. Havíamos dito que uma ideia grande como a que havia sido anunciada dificilmente ficaria restrita a somente o ASP.NET, que tinha tudo pra crescer, e se tornar o novo .NET. Foi exatamente o que aconteceu. E, apesar de termos, na época, alguma informação privilegiada (e sob NDA), essa não era uma delas, era somente uma aposta, que acertamos em cheio.

As mudanças afetam bastante a maneira com que executamos uma aplicação da nova stack do ASP.NET, que antes era chamado de ASP.NET 5 – até o RC1, e agora se chama ASP.NET Core, nome que passará a valer a partir do RC2. Mas o código que desenvolvemos terá poucas alterações. Quem vai ser mais afetado é quem trabalha com os códigos internos do ASP.NET, quem trabalha com uma aplicação ASP.NET MVC terá pouco impacto. E se você não fez nada sério ainda com o novo ASP.NET, nada muda.

Mudanças na linha de comando

A maneira com que desenvolvemos não mudará praticamente nada. Em vez de rodar uma aplicação com “dnx web”, passaremos a escrever “dotnet run”. Outras mudanças em comandos:

Antigo Novo
dnu restore dotnet restore
dnu pack dotnet pack
dnu build dotnet compile/build
dnu publish dotnet publish
dnu run dotnet run
dnx-watch dotnet watch
dnx ef dotnet ef
dnx test dotnet test
user-secrets dotnet user-secrets

Não está claro ainda como ficará a parte de comandos, que fazemos com dnx rodando “dnu commands”, podendo instalar ferramentas de desenvolvimento.

Outro ponto interessante é que rodar um comando significa rodar a aplicação “dotnet-comando”, ou seja, rodar “dotnet restore” vai rodar o executável “dotnet.exe” que fica, nesse momento, no Windows, instalado em “C:\Program Files\dotnet\bin\”, com o comando “restore”, que, por sua vez, rodará o executável “dotnet-restore” que fica no mesmo lugar. Eu imagino que será exatamente assim que as novas ferramentas de desenvolvimento funcionarão. Por exemplo, eu criei o runner do fluent-migrator para dnx. Imagino que ao instalá-lo, provavelmente teremos um “dotnet-fm” em algum lugar no PATH do usuário, e ao rodar “dotnet fm” o “dotnet.exe” vai encontrá-lo, e executá-lo. Não me parece que nenhum dos comandos existentes (build, restore, publish, etc) sejam especiais.

Não há nenhuma mudança prevista na maneira com que interagimos com o Visual Studio. A ideia de salvar um arquivo e automaticamente a aplicação reiniciar com as alterações, por exemplo, continua valendo.

Não há mais DNVM

Essa é uma alteração interessante: o DNVM (.NET Version Manager) será descontinuado. O motivo é que ele era a ferramenta que usávamos para determinar a versão do framework com a qual um executável rodava. Isso não é mais necessário porque agora o framework se tornou uma dependência da aplicação, e, no caso do .NET Core, é baixado via nuget.

dnvm

Isso é interessante porque retira uma dependência que era escondida da aplicação, e não podia ser substituída. Dê uma olhada em “$HOME\.dnx\runtimes\dnx-coreclr-win-x86.1.0.0-rc1-update1\bin”, por exemplo, e verá todo o runtime e a BCL lá. Agora, todas as dependências se tornam dependências diretas, e não há mais um runtime escondido: ele é parte da sua app.

Não há mais um executor único

O dnx.exe era o responsável por rodar todas as aplicações criadas com dnx. Isso mudou, e esse executável não existirá mais, também será descontinuado. Agora, toda aplicação será uma console application, e gerará um .exe (e outros arquivos), que será executado. Os outros arquivos serão arquivos com o mesmo nome do .exe, mas outras extensões: .pdb, .dll, e .deps. O pdb é o arquivo de símbolos de debug, exatamente como no .NET atual. Já o .dll e o .deps são especiais, e pra explicar o que fazem, precisamos entendê-los juntos e com o .exe.

O arquivo .exe não é um executável .NET, mas nativo ao SO onde a compilação aconteceu. É ele que fará o boot do runtime, e ele é sempre o mesmo arquivo pra qualquer aplicação, apenas com o nome alterado. Ele olha para o arquivo .deps, que contém as dependências, e as carrega. Aqui um trecho deste arquivo:

"Package","Microsoft.Win32.Registry","4.0.0-rc2-23704","sha512-UJhGbtOqPA05NRiN51rkEHOQNJnemaYhn2BaZ72wGF5fWpLbpcmylMdV48VIqWhDtX79dPN+mGrDzrGyS02gQQ==","runtime","Microsoft.Win32.Registry","lib\\DNXCore50\\Microsoft.Win32.Registry.dll"
"Package","runtime.win.System.Runtime.InteropServices.RuntimeInformation","4.0.0-rc2-23704","sha512-2Sd6eyJQ+UY9IFVgbsMDAl/lsJzcUTvpJx5OsAygOXqfKXQ8cwbFA+ZS2GgNnjTWIGp6AMrUQEmoYkOHvxLghw==","runtime","System.Runtime.InteropServices.RuntimeInformation","runtimes\\win\\lib\\dotnet5.2\\System.Runtime.InteropServices.RuntimeInformation.dll"
"Package","runtime.win7.Microsoft.Win32.Primitives","4.0.1-rc2-23704","sha512-b0qyxoBKEhe97v6Z9aIqjQZMsiiLdtD4twofj2GdEInRncRs/CoR4aSJ8o/zCm7LDs4qSkrqzgFIYsWDeEIEOw==","runtime","Microsoft.Win32.Primitives","runtimes\\win7\\lib\\dotnet5.4\\Microsoft.Win32.Primitives.dll"
"Package","runtime.win7.System.Console","4.0.0-rc2-23704","sha512-qybaMKWmOayY4f5SfPbfcDwNCIXn10TNFnZsOYUvSPe6xkn5kvQKk+QBLkrTOM5uhzYj0CPG9/UFwWizAp1UbA==","runtime","System.Console","runtimes\\win7\\lib\\dotnet5.4\\System.Console.dll"

O da minha app de exemplo tinha 174 linhas.

Após carregar essas dependências, que estarão no diretório ~\.nuget\packages (e não mais no ~\.dnx\packages e ~\.dnx\runtimes como antes), ele carrega o arquivo .dll, que é um executável .NET normal, e é onde está de fato a sua aplicação.

Esse modelo, de usar um arquivo comum para fazer o bootstrap do runtime, e depois carregar de fato a app, é muito usado em outras arquiteturas. Tem vantagens e desvantagens, e não vou discutí-las aqui, mas é bom saber que isso mudou. E pode continuar mudando.

Toda aplicação será uma aplicação console

Seguindo o modelo descrito acima, toda aplicação gera um .exe, e um .dll. Isso quer dizer que toda aplicação terá um entry point, com um método void main, incluindo aplicações ASP.NET. Nesse caso, esse arquivo carregará o runtime do ASP.NET, e então a aplicação em si. Tudo fica mais transparente.

Não se preocupe com essa complexidade, por 2 motivos. Primeiro, porque esses arquivos serão gerados para você quando criar um novo projeto. Segundo, porque a API para montar isso tudo é extremamente simples, e tomará menos de 10 linhas de código. E ainda permitirá que tenhamos mais controle sobre o que acontece de fato com a aplicação.

Se você já está trabalhando com o RC1, já deve ter visto que há um void main no projeto web. Aquilo foi só o começo, a mudança no RC2 será ainda maior.

A criação da .NET Standard Library e declínio das PCLs

Quem trabalha com outras plataformas, como C++, ou Go, está acostumado com a ideia de uma standard library (stdlib). A ideia aqui é a mesma: um conjunto de APIs que uma aplicação toma como dependência. Essa ideia não existia no .NET, trabalhávamos antes com versões do .NET Framework, identificadas por um Target Framework Moniker (TFM). Por exemplo, .NET 4.5.2 (TFM net452), ou Windows Phone 8 (TFM wp8). Cada TFM definia, explicitamente, um conjunto de APIs aplicável a um framework, e um TFM não era necessariamente compatível com o outro.

Para que pudéssemos trabalhar com portabilidade, criamos a ideia de Portable Class Libraries (PCLs), que faziam o mínimo denominador comum entre TFMs. Isso criava perfils, ou profiles, com o cruzamento de diversos TFMs. Por exemplo, Profile7 significava suportar .NET 4.5, Windows 8, Xamarin iOS, Android e iOS unificado. Para ver isso, basta olhar o diretório “C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.5\Profile\Profile7\SupportedFrameworks” na sua máquina. O problema é que, se amanhã alguém lançasse uma nova plataforma, por exemplo, o Lambda3Phone, com a exata mesma API que o Android Xamarin usa, ainda assim uma PCL criada com o Profile7 não poderia ser usada, pois não endereçava específicamente essa plataforma.

.NET Platform Standard

O autor da biblioteca agora, em vez de especificar que suporta o Profile7 (algo que o Visual Studio fazia pra você, mas que você tinha que fazer na mão no seu arquivo do nuget, o .nuspec), passará a suportar uma versão da stdlib. Assim, atendendo a uma especificação de um conjunto de APIs em vez de um TFM, qualquer plataforma que suportar a especificação, suporta, automaticamente, todas as bibliotecas que endereçam esse conjunto de APIs. Portanto, um autor de um componente passa a depender, por exemplo, do pacote “NETStandard.Library”, versão “1.5” e qualquer plataforma que suporte essa versão da stblib (esse conjunto de APIs) poderá rodar tal componente. Esse autor poderá continuar suportando as PCLs, que, conforme o tempo passa, tendem a cair em desuso. As versões do .NET Platform Standard possuem seus próprios TFMs, sempre prefixados por “netstandard”.

JSON.NET no Nuget Package Explorer

Para mais informações sobre a .NET Plataform Standard, veja esse documento que está no Github. É o documento mais importante disponível sobre o assunto.

Veja a galeria do Myget do .NET Core para ver quais pacotes existem nesse momento suportando o padrão.

O DNU também já era

O .NET Utilities (DNU) também foi descontinuado. Boa parte das suas atribuições serão feitas pelo “dotnet.exe”, como, por exemplo, o restore, e o publish. O Nuget 3.3+ também poderá ser utilizado, e todo o modelo está sendo unificado.

Usando .NET CLI e Full .NET juntos

Uma novidade muito interessante é que você pode usar esse novo modelo de desenvolvimento com o .NET Full, sem depender do .NET Core, e, no final, produzir uma biblioteca ou executável como qualquer outro produzido anteriormente. O resultado da compilação, nesse caso, é um binário que dependerá da versão do .NET para o qual você o endereçou. Tudo que você fez foi usar o novo .NET CLI, e as ferramentas que vem com ele. É possível, nesse caso, desenvolver aplicação ASP.NET 4, e até WPF, nesse modelo. E tudo fica mais fácil pelo fato de que agora poderemos referenciar projetos baseados em xproj a partir de projetos csproj.

Mas vá com calma, já que esse não é o use case padrão. Eu imagino que logo depois do lançamento teremos gente tornando isso mais simples e interessante de trabalhar, dadas as inúmeras vantagens.

Suporte a F# e VB

Essa era inesperada, mas, ao que tudo indica, VB e F# estarão disponíveis e suportados na primeira versão do .NET Core 1.0 e .NET CLI. É algo que os amantes dessas linguagens esperavam e pediam, e agora chegou, junto com o C#. Bastará informar qual compilador você quer usar no seu project.json, no atributo compilationOptions/compiler, especificando “vbc”, por exemplo, para o compilador do VB.

Essa mudança também trouxe diversas outras vantagens, como, por exemplo, permitir informar opções antes indisponíveis do processo de compilação. Foi ela que trouxe, por exemplo, o suporte a analisadores Roslyn, como o CodeCracker, ao novo ASP.NET, que era algo que ainda não tínhamos.

Como começar

Vá para dotnet.github.io e siga as instruções para instalar o .NET CLI no Windows, Linux ou Mac. Mas já adianto que muito pouco está funcionando da forma esperada. A release ainda está sem data marcada (veja também esse post do time do ASP.NET), mas foi comentado em alguns locais mencionando metade de fevereiro, mas eu acredito que só teremos o RC2 no final de Março. De qualquer forma, volte a esse endereço de vez em quando e baixe o instalador novamente, pode ter sido atualizado.

E aí, você já experimentou o novo .NET CLI? O que achou? Conte aqui nos comentários.

So long, DNX, long live .NET CLI.

Giovanni Bassi

Arquiteto e desenvolvedor, agilista, escalador, provocador. É fundador e CSA da Lambda3. Programa porque gosta. Acredita que pessoas autogerenciadas funcionam melhor e por acreditar que heterarquia é mais eficiente que hierarquia. Foi reconhecido Microsoft MVP há mais de dez anos, dos mais de vinte que atua no mercado. Já palestrou sobre .NET, Rust, microsserviços, JavaScript, TypeScript, Ruby, Node.js, Frontend e Backend, Agile, etc, no Brasil, e no exterior. Liderou grupos de usuários em assuntos como arquitetura de software, Docker, e .NET.