O que realmente acontece quando você digita dotnet run?
Se você trabalha com .NET, provavelmente já digitou esse comando muitas e muitas vezes: dotnet run E alguns segundos depois sua aplicação está rodando. Simples assim. Mas o que realmente aconteceu entre você apertar Enter e a sua aplicação começar a executar? Quem encontrou o projeto? Quem compilou o código C#? O que é gerado durante essa compilação? O processador consegue executar C# diretamente? Onde entram o SDK, o Runtime e o CLR nessa história? Foi justamente tentando responder essa pergunta que escolhi o tema do primeiro dia dos meus 30 dias de .NET. Então vamos seguir o caminho de um dotnet run, do terminal até a execução da aplicação. Você digita dotnet run no terminal A primeira peça que encontramos é o .NET CLI (Command-Line Interface). Mas o que é um CLI? A CLI é uma interface que permite interagir com um programa através de comandos escritos. Então não, a CLI não é exatamente a “tela preta” do terminal. O terminal é o programa que usamos para digitar os comandos, enquanto a .NET CLI é a ferramenta que recebe e interpreta comandos como o dotnet run. No Windows, por exemplo, podemos usar a .NET CLI através do PowerShell, Prompt de Comando ou Windows Terminal. No Linux e macOS, podemos utilizá-la através dos seus respectivos terminais. Quando instalamos o .NET SDK, temos acesso ao comando dotnet, que funciona como o ponto de entrada para várias ferramentas da plataforma: dotnet new # cria um novo projeto a partir de um template dotnet restore # restaura as dependências do projeto dotnet build # compila o projeto dotnet test # executa os testes dotnet run # compila e executa a aplicação dotnet publish # prepara a aplicação para ser distribuída Quando executamos dotnet run estamos basicamente dizendo: encontre meu projeto, prepare-o para execução e execute-o. Mas a CLI não apareceu magicamente na nossa máquina. Ela veio junto com outra peça importante: o .NET SDK. O SDK - Software Development Kit O SDK reúne as ferramentas necessárias para nós, desenvolvedores, conseguirmos criar nossas aplicações .NET. Quase como uma caixinha de ferramentas que tem martelo e chave de fenda, o SDK tem a CLI, ferramentas de build, compiladores, bibliotecas e outros componentes necessários para essa construção. Por conta desse conjunto todo, quando vamos desenvolver uma aplicação precisamos verificar se o SDK está instalado. Podemos descobrir quais estão instalados usando o comando: dotnet —list-sdks Então, quando executamos o dotnet run, estamos utilizando as ferramentas fornecidas pelo SDK para preparar nossa aplicação. O código C# precisa ser compilado, certo? Nosso processador não entende absolutamente nada da frase solta: Console.WriteLine(“Help, não entendo”); Depois de escrito e antes de ser executado, o nosso código C# precisa passar por um processo de compilação, ou seja, ser compilado. Compilar significa, de forma simplificada, transformar o código que escrevemos em uma representação que possa seguir para as próximas etapas de execução. No caso do C#, o compilador transforma nosso código em IL (Intermediate Language) e adiciona junto metadados sobre o nosso código. O IL é uma linguagem intermediária. Ou seja, nesse momento ainda não estamos falando a “lingua das máquinas” que será executada pelo processador. É nessa parte que começamos a sair do território do SDK e entrar no processo de execução da aplicação de fato. .NET Runtime O .NET Runtime é o conjunto de componentes necessários para executar uma aplicação .NET depois que ela foi compilada. Ele inclui componentes como o CLR, bibliotecas fundamentais do .NET e outros arquivos necessários para que uma aplicação consiga rodar. É por isso que SDK e Runtime têm funções diferentes. Se estamos desenvolvendo, precisamos das ferramentas do SDK. Mas, para apenas executar uma aplicação, dependendo da forma como ela foi publicada, podemos precisar somente do Runtime. Podemos verificar quais runtimes estão instalados na máquina com: dotnet —list-runtimes Quando nossa aplicação começa a ser executada, o Runtime fornece o ambiente necessário para que aquela IL que geramos anteriormente possa finalmente rodar. E uma das peças mais importantes desse processo é o CLR. CLR - Common Language Runtime O CLR (Common Language Runtime) é o ambiente responsável pela execução do código gerenciado do .NET. Mas ele não simplesmente “roda o código”. Durante a execução, o CLR cuida de várias coisas a mais para nós. Quando criamos objetos no nosso código, por exemplo, precisamos de memória para armazenar todos eles. O CLR participa do gerenciamento dessa memória e o Garbage Collector identifica objetos que não estão mais sendo utilizados para liberar espaço. Ele também fornece outros serviços que usamos constantemente sem precisar implementar tudo manualmente, como: gerenciamento de memória; tratamento de exceções; gerenciamento de threads; segurança de tipos. Durante a execução tradicional, entra também o JIT — Just-In-Time compiler. Ele transforma o IL (Intermediate Language que vimos ali em cima) em código de máquina que o processador daquela máquina consegue executar. E aqui tem uma coisa muito importante do .NET: o fato dele ser Multiplataforma O IL gerado na compilação é intermediário e não está diretamente preso às instruções de uma CPU específica. Quando a aplicação é executada, o Runtime apropriado para aquela plataforma participa da execução e o JIT gera código de máquina adequado à arquitetura do processador. O mesmo código .NET pode rodar em Windows, Linux e macOS desde que exista suporte do .NET para aquele sistema operacional e arquitetura. Juntando tudo Agora conseguimos enxergar o caminho inteiro: Tudo isso começou com um único comando de duas palavras, talvez o mais utilizado: dotnet run Então qual é a diferença entre SDK, CLI, Runtime e CLR? Depois de acompanhar o caminho do comando, as definições ficam bem menos abstratas. Segue um resuminho pra guardar na memória (a sua, não do computador) Termo Em um tweet SDK Conjunto de ferramentas que usamos para desenvolver aplicações .NET. CLI Interface de linha de comando através da qual acessamos várias ferramentas do .NET. Runtime Conjunto de componentes necessários para executar uma aplicação .NET. CLR Ambiente de execução do código gerenciado. IL Linguagem intermediária gerada a partir da compilação do código C#. JIT Compilador que transforma o IL em código de máquina. Então, em vez de decorar quatro definições separadas, podemos pensar nelas como peças que participam de momentos diferentes da vida da nossa aplicação. E da próxima vez que eu digitar dotnet run, vou saber o tanto de coisa que está acontecendo por trás daquele comando. Referências .NET SDK overview — Microsoft Learn .NET CLI overview — Microsoft Learn dotnet command — Microsoft Learn Common Language Runtime (CLR) — Microsoft Learn Managed execution process — Microsoft Learn Garbage Collection — Microsoft Learn Fundamentals of Garbage Collection — Microsoft Learn .NET Project SDK overview — Microsoft Learn