Novidades do MSBuild no .NET 11 Preview 1: performance, novas APIs e correções
O MSBuild no .NET 11 Preview 1 chega na versão 18.4 e resolve problemas que afetam o dia a dia de quem compila projetos .NET. Um exemplo clássico é o resumo dos testes que, em alguns pipelines de CI, simplesmente sumia do terminal, sem erro e sem aviso.
Essa release, porém, vai muito além de correções pontuais. Ela reúne otimizações de performance, novas APIs para integração programática e mais de 20 bugs eliminados, todos organizados neste artigo para você avaliar o impacto no seu projeto.
O que mudou no MSBuild no .NET 11
Segundo as notas de lançamento oficiais do MSBuild no .NET 11 Preview 1, a versão 18.4 consolida melhorias acumuladas das versões 18.0 a 18.4. O foco está em três pilares: performance, estabilidade e novas APIs para integração programática.
Para quem usa dotnet build no dia a dia, vale lembrar que o MSBuild é o motor por trás desse comando. De acordo com a documentação do comando, executar dotnet build equivale a executar dotnet msbuild -restore, com diferença apenas na verbosidade padrão da saída.
Em outras palavras, cada otimização interna do MSBuild se traduz diretamente em builds mais rápidos e confiáveis. Por isso, mesmo quem nunca chama o msbuild diretamente sente o efeito dessas mudanças.
A lista de mudanças é extensa, então as novidades aparecem a seguir por categoria. Primeiro, vamos ao Terminal Logger, que concentra as correções mais visíveis.
Terminal Logger no MSBuild do .NET 11
O Terminal Logger é a interface visual que exibe o progresso do build no terminal. Ele mostra quais projetos estão compilando, o resultado dos testes e o resumo final.
No MSBuild do .NET 11, a equipe resolveu três problemas relevantes do Terminal Logger. Todos eles afetam quem depende do terminal como principal fonte de feedback.
Resumo de testes sempre reportado
Existia um bug em que o resumo dos testes podia desaparecer da saída do Terminal Logger. Segundo as notas, isso acontecia quando o nó do terminal ficava nulo antes de receber a mensagem de conclusão.
Na prática, o build terminava e o desenvolvedor ficava sem saber se os testes tinham passado ou falhado. A única alternativa era abrir o arquivo de log manualmente.
Agora, o Terminal Logger sempre contabiliza os resultados dos testes no resumo final. Parece uma correção simples, mas faz toda a diferença para quem acompanha o build pelo terminal.
Restauração do status do nó após yield
Quando uma tarefa do MSBuild cedia a execução, operação chamada de yield, o Terminal Logger não restaurava a exibição corretamente. Como resultado, o terminal mostrava informações desatualizadas durante builds complexos.
Imagine um build com vários projetos, em que uma tarefa cede a execução para que outra dependência compile primeiro. Antes da correção, o terminal podia exibir o status anterior do nó, o que confundia quem acompanhava o processo.
Agora, o status de cada nó volta a ficar consistente após o yield. Assim, o que aparece na tela corresponde ao que realmente acontece no build.
Forwarding logger para builds via API
Quem usa as APIs do MSBuild com o Terminal Logger agora tem acesso aos benefícios do forwarding logger em builds com vários nós. Para isso, novos métodos permitem configurar o Terminal Logger como forwarding logger.
Segundo as notas, essa mudança resolve uma lacuna de performance em ferramentas como dotnet run e dotnet watch. Na prática, rodar dotnet watch em projetos grandes tende a gerar uma saída de log mais consistente e com menos overhead.
Correções na linguagem do MSBuild no .NET 11
O MSBuild tem uma linguagem baseada em XML, com funções, propriedades e avaliação de expressões. É nesse motor que ele interpreta seus arquivos .csproj, .targets e .props.
O MSBuild no .NET 11 Preview 1 traz quatro correções importantes nesse motor de avaliação. Cada uma delas resolve um comportamento que parecia correto no arquivo, mas falhava na execução.
Encadeamento de funções de item com espaços
Havia um bug no parser em que o encadeamento de funções de item falhava quando existia espaço em branco antes do segundo operador -> ou dos seguintes. Esse tipo de problema frustra, porque o código parece correto visualmente.
Agora, o parser trata corretamente os espaços antes dos operadores. Por exemplo, a expressão abaixo, citada nas notas oficiais, passa a funcionar:
<ItemGroup>
<!-- encadeamento com espaços antes de cada operador -> -->
<Filtered Include="@(I -> WithMetadataValue('M', 'T') -> WithMetadataValue('M', 'T'))" />
</ItemGroup>
Se você já perdeu tempo depurando um encadeamento que deveria funcionar, essa pode ter sido a causa.
NoWarn junto com MSBuildWarningsAsMessages
Um bug fazia o MSBuild ignorar a propriedade NoWarn quando MSBuildWarningsAsMessages estava definida. Com isso, o desenvolvedor precisava escolher uma das duas configurações, embora o esperado fosse o funcionamento conjunto.
A correção restaura o comportamento correto. Agora, NoWarn suprime os avisos especificados, enquanto MSBuildWarningsAsMessages converte outros avisos em mensagens informativas.
<PropertyGroup>
<!-- suprime completamente estes avisos -->
<NoWarn>$(NoWarn);CS1591</NoWarn>
<!-- rebaixa estes avisos para mensagens -->
<MSBuildWarningsAsMessages>MSB3277</MSBuildWarningsAsMessages>
</PropertyGroup>
Elementos com apenas espaços e Regex.Replace
Quando um elemento XML continha apenas espaços em branco, a avaliação do projeto apresentava erros. Esse cenário parece raro, mas surge em arquivos gerados automaticamente ou editados por ferramentas de merge.
Além disso, a invocação de Regex.Replace(...) dentro de property functions produzia resultados errados. Para quem usa expressões regulares em .csproj ou .targets, essa correção evita substituições inesperadas durante o build.
Novas APIs do MSBuild no .NET 11
Essa é a parte mais interessante do MSBuild no .NET 11. O MSBuild no .NET 11 traz APIs que facilitam a integração programática e adicionam recursos que antes exigiam soluções improvisadas.
API de parsing de parâmetros do BinaryLogger
O BinaryLogger é o sistema de log binário do MSBuild, que gera arquivos .binlog. Segundo a documentação, esses arquivos guardam propriedades, itens, entradas e saídas de tarefas e o conteúdo dos arquivos importados, o que os torna essenciais para diagnóstico.
O novo método estático BinaryLogger.ParseParameters(string) converte uma string de parâmetros em um objeto tipado BinaryLoggerParameters. Antes dessa API, ferramentas como o dotnet watch precisavam duplicar essa lógica internamente.
// converte a string de parâmetros em um objeto tipado
var parsed = BinaryLogger.ParseParameters("LogFile=build.binlog;ProjectImports=None");
Console.WriteLine(parsed.LogFilePath); // "build.binlog"
Console.WriteLine(parsed.ProjectImportsCollectionMode); // None
O método recebe a string no mesmo formato da linha de comando, como em -bl:build.binlog;ProjectImports=None. Dessa forma, todas as ferramentas que consomem logs binários passam a usar a mesma lógica, sem divergências de comportamento.
Se você trabalha com diagnóstico de builds, o artigo sobre o Binlog MCP Server mostra como analisar esses arquivos com ajuda de IA.
EscapeDataString em property functions
Com o MSBuild no .NET 11, é possível usar System.Uri::EscapeDataString e System.Uri::UnescapeDataString diretamente como property functions. Isso ajuda quando você precisa codificar valores para URLs dentro de arquivos de projeto.
Antes dessa adição, a alternativa era criar uma tarefa customizada ou chamar um script externo. Agora, basta usar a sintaxe nativa:
<PropertyGroup>
<QueryParam>hello world & test=123</QueryParam>
<Encoded>$([System.Uri]::EscapeDataString($(QueryParam)))</Encoded>
<!-- Resultado: hello%20world%20%26%20test%3D123 -->
</PropertyGroup>
A propriedade Encoded passa a conter a string codificada corretamente. Ou seja, espaços, o "e comercial" e o sinal de igual viram suas representações percentuais, prontas para uso em URLs.
Modo transacional no WriteLinesToFile
No MSBuild do .NET 11, a tarefa WriteLinesToFile escreve e acrescenta conteúdo de forma transacional. Essa mudança resolve um problema real de builds paralelos, em que várias instâncias do MSBuild tentam escrever no mesmo arquivo ao mesmo tempo.
Antes, o resultado era imprevisível: às vezes o conteúdo corrompia, às vezes um processo sobrescrevia o outro. Em alguns casos, o build falhava com erro de acesso ao arquivo.
Segundo as notas, a tarefa agora cria arquivos novos em um local temporário e depois os move de forma atômica. Já nas operações de acréscimo, ela usa handles com FileShare.Read, evitando conflitos de escrita concorrente.
MSBUILDDEBUGONSTART=3 para task hosts
A variável de ambiente MSBUILDDEBUGONSTART controla o comportamento de depuração do MSBuild. O novo valor 3 pula os prompts para anexar o depurador nos processos filhos do TaskHost.
Isso ajuda em cenários de depuração automatizada, em que você quer investigar o processo principal sem interrupções. Em builds que usam task hosts para isolar tarefas, essa opção economiza bastante tempo.
Performance do MSBuild no .NET 11
A lista de otimizações de performance do MSBuild no .NET 11 chama a atenção. São melhorias pontuais que, somadas, reduzem o tempo de build em projetos de médio e grande porte.
Estruturas de dados otimizadas
Várias estruturas de dados internas mudaram para aproveitar melhor o cache e alocar menos memória. Esse tipo de ajuste parece pequeno, mas o MSBuild executa essas estruturas milhares de vezes por build.
ItemDictionary trocou a LinkedList por uma estrutura mais amigável ao cache da CPU. Afinal, cada nó de uma lista ligada pode ficar em uma região diferente do heap, enquanto dados contíguos aproveitam melhor as linhas de cache do processador.
Lookup.Scope ganhou uma nova implementação das tabelas de escopo, com buscas mais rápidas. Como o MSBuild avalia escopos intensamente, essa otimização tem efeito em cascata em toda a avaliação de projetos.
WorkUnitResult deixou de ser classe e virou struct, o que elimina uma alocação no heap para cada unidade de trabalho. Em builds com milhares de tarefas, isso reduz a pressão sobre o garbage collector.
Menos alocações no MSBuild do .NET 11
No MSBuild do .NET 11, diversas operações também passaram a evitar alocações desnecessárias no heap:
- ProjectMetadataInstance eliminou alocações que o fluxo normal de avaliação não exigia.
- LazyItemEvaluator evita o boxing de enumeradores, que ocorre quando um value type vira reference type.
- GetEnumerator ganhou uma verificação de
ICollection<T>que evita alocações em cenários comuns. - A otimização de captura única em regex trata de forma direta o caso mais comum, com apenas uma captura.
Otimizações de I/O e logging
O buffer de log do nó RAR, sigla de ResolveAssemblyReference, armazena eventos antes de enviá-los. Como essa tarefa é uma das que mais geram log, a mudança beneficia builds com muitas referências de assembly.
Além disso, a abstração FileSystems passou a substituir chamadas diretas à BCL nas operações de arquivo. Com isso, o time ganha espaço para otimizações por plataforma e para testes unitários mais eficientes do motor de build.
Outras otimizações notáveis
- EndsWith ganhou um caminho rápido para chamadas com
StringComparison. - ImmutableDictionary.SetItems usa o padrão builder em atualizações em lote, sem dicionários intermediários.
- Cópias de metadados RAR mudaram de ordem para priorizar os caminhos mais frequentes.
- BuildGlobResultFromIncludeItem simplificou a construção dos resultados de glob.
- Lookup.ExplicitModifications passou a usar um tipo concreto de dicionário, sem o custo de dispatch virtual.
Bugs corrigidos no MSBuild do .NET 11
Além das melhorias, o MSBuild no .NET 11 Preview 1 corrige mais de 20 bugs. A seguir, estão os mais relevantes para o dia a dia, organizados por categoria.
Estabilidade do build
O DrainPacketQueue tinha um possível travamento de thread após a conclusão do build. O processo ficava pendurado, sem erro e sem timeout, o que desperdiça agentes em pipelines de CI/CD.
Já os loops infinitos de depuração surgiam com o redirecionamento da pasta temporária por variáveis de ambiente. Portanto, se você customiza TEMP ou TMP no ambiente de build, essa correção interessa diretamente.
Por fim, o crash no Preprocess acontecia com a opção /preprocess, ou /pp. Essa opção gera uma versão expandida do projeto com todas as importações resolvidas e agora funciona de forma estável.
Compatibilidade entre plataformas
Em sistemas Unix, como Linux e macOS, o sistema de arquivos diferencia maiúsculas de minúsculas, e o MSBuild no .NET 11 passa a respeitar isso. A tarefa Copy não respeitava essa diferença corretamente, e a correção garante o tratamento adequado dos nomes de arquivos.
Além disso, os arquivos de solution filter, com extensão .slnf, agora aceitam separadores Unix e Windows. Isso facilita o trabalho em equipes que usam sistemas operacionais diferentes.
Resolução e depuração
Os erros em app.config agora incluem o número da linha na mensagem. Antes, a mensagem apenas indicava um problema no arquivo, sem informar a localização exata.
A resolução de assemblies também recebeu correção para arquivos .exe e tarefas legadas do .NET Framework. Assim, projetos que ainda dependem desses componentes continuam funcionando na nova versão.
Já a resolução de arquivos em inline tasks agora parte do diretório do projeto, e não do diretório de trabalho atual. Antes, o mesmo build podia funcionar ou falhar dependendo da pasta em que você executava o comando.
Outras correções do MSBuild no .NET 11
- DistributedFileLogger: corrigido o erro MSB1025 ao usar a flag
-dfl. - Determinismo do deps.json: a seção de recursos agora tem ordenação determinística, o que favorece builds reproduzíveis.
- /getItem e /getTargetResult: deixaram de lançar exceções não tratadas com caracteres ilegais no caminho.
- SDK resolver: ganhou um mecanismo de fallback para quando a API externa do resolver não está disponível.
- Variáveis de ambiente do SDK: o log registra apenas valores diferentes do padrão, com menos ruído.
O que o MSBuild no .NET 11 significa na prática
O MSBuild no .NET 11 Preview 1 não traz funcionalidades revolucionárias, e isso é positivo. Trata-se de uma release focada em maturidade, em que dezenas de ajustes tornam o motor de build mais robusto.
Se você mantém projetos de médio ou grande porte, as otimizações de performance são o destaque. A troca de listas ligadas por estruturas amigáveis ao cache e a redução de alocações devem resultar em builds mais rápidos, sobretudo em soluções grandes.
Para quem desenvolve ferramentas integradas ao MSBuild, as novas APIs eliminam código duplicado. Tanto o BinaryLogger.ParseParameters quanto o suporte a EscapeDataString simplificam integrações reais.
E se você já enfrentou algum dos bugs corrigidos, como o travamento do DrainPacketQueue no CI, vale testar a nova versão o quanto antes. Para conhecer as demais novidades do mesmo preview, confira os artigos sobre o SDK e o Hot Reload no .NET 11 Preview 1 e sobre o runtime do .NET 11 Preview 1.
Como testar o MSBuild no .NET 11
Para experimentar o MSBuild no .NET 11, instale o SDK do .NET 11 Preview 1 pelo site oficial do .NET. O SDK roda lado a lado com as versões estáveis, e você pode fixar a versão usada por cada projeto com um arquivo global.json.
Depois da instalação, confira a versão do MSBuild que acompanha o SDK com o comando abaixo:
# exibe a versão do MSBuild usada pelo SDK ativo
dotnet msbuild -version
Em seguida, compile um projeto existente gerando um log binário. Assim, você consegue comparar tempos e diagnosticar qualquer diferença com o Structured Log Viewer.
dotnet build -bl:build.binlog
O .NET 11 tem lançamento final previsto para novembro de 2026. Até lá, prefira usar os previews em ambientes de desenvolvimento, e acompanhe as versões candidatas, como a RC 1 do .NET 11, para planejar a migração.
FAQ
O MSBuild 18.4 é compatível com projetos .NET existentes?
Sim. As mudanças são internas e não exigem alterações nos arquivos de projeto, então um projeto que compila nas versões anteriores continua compilando.
Preciso alterar meus arquivos .csproj para ganhar performance?
Não. As otimizações são internas ao MSBuild, e basta compilar com o SDK do .NET 11 para aproveitá-las.
Qual a diferença entre dotnet build e MSBuild?
O dotnet build é a interface simplificada que chama o MSBuild internamente e equivale a dotnet msbuild -restore. O uso direto do MSBuild faz sentido quando você precisa de controle fino sobre parâmetros do build.
As correções entre plataformas afetam quem usa só Windows?
Pouco. Elas importam principalmente para equipes com ambientes mistos ou que compilam em containers Linux no CI/CD.
Quando o .NET 11 terá versão estável?
O lançamento final do .NET 11 está previsto para novembro de 2026. Para produção, aguarde ao menos uma versão candidata com licença go-live.