Eu tenho um arquivo de solução c # grande (~ 100 projetos) e estou tentando melhorar os tempos de compilação. Eu acho que "Copiar local" é um desperdício em muitos casos para nós, mas estou pensando nas melhores práticas.
Em nosso .sln, temos o aplicativo A, dependendo do conjunto B, que depende do conjunto C. No nosso caso, existem dezenas de "B" e um punhado de "C". Como todos estão incluídos no .sln, estamos usando referências de projeto. Todos os assemblies atualmente compilam em $ (SolutionDir) / Debug (ou Release).
Por padrão, o Visual Studio marca essas referências de projeto como "Copiar Local", o que resulta em cada "C" sendo copiado em $ (SolutionDir) / Debug uma vez para cada "B" criado. Isso parece um desperdício. O que pode dar errado se eu desligar a opção "Copiar local"? O que outras pessoas com sistemas grandes fazem?
ACOMPANHAMENTO:
Muitas respostas sugerem dividir a compilação em arquivos .sln menores ... No exemplo acima, eu criaria as classes de fundação "C" primeiro, seguidas pela maior parte dos módulos "B" e depois alguns aplicativos " UMA". Nesse modelo, preciso ter referências que não sejam do projeto a C de B. O problema que encontro é que "Debug" ou "Release" são inseridos no caminho da dica e acabo criando minhas compilações de Release "B" contra compilações de depuração de "C".
Para aqueles de vocês que dividiram a compilação em vários arquivos .sln, como gerencia esse problema?
fonte
<Private>True</Private>
em um csproj?.sln
em menor quebra o cálculo da interdependência automagica de VS de<ProjectReference/>
s. Mudei de vários.sln
s menores para um grande.sln
só porque o VS causa menos problemas dessa maneira ... Então, talvez o acompanhamento esteja assumindo uma solução não necessariamente a melhor para a pergunta original? ;-)Respostas:
Em um projeto anterior, trabalhei com uma grande solução com referências de projeto e me deparei com um problema de desempenho. A solução foi três vezes:
Sempre defina a propriedade Copy Local como false e imponha isso por meio de uma etapa personalizada do msbuild
Defina o diretório de saída de cada projeto para o mesmo diretório (de preferência em relação a $ (SolutionDir)
Os destinos cs padrão que são enviados com a estrutura calculam o conjunto de referências a serem copiadas para o diretório de saída do projeto que está sendo construído atualmente. Como isso requer o cálculo de um fechamento transitivo sob a relação 'Referências', isso pode se tornar MUITO dispendioso. Minha solução para isso foi redefinir o
GetCopyToOutputDirectoryItems
destino em um arquivo de destinos comum (por exemplo,Common.targets
) importado em todos os projetos após a importação doMicrosoft.CSharp.targets
. Resultando em cada arquivo de projeto com a seguinte aparência:Isso reduziu nosso tempo de compilação em um determinado momento de algumas horas (principalmente devido a restrições de memória), para alguns minutos.
A redefinição
GetCopyToOutputDirectoryItems
pode ser criada copiando as linhas 2.438-2.450 e 2.474-2.524 deC:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets
paraCommon.targets
.Para completar, a definição de destino resultante se torna:
Com essa solução alternativa, achei viável ter até 120 projetos em uma solução, isso tem o principal benefício de que a ordem de criação dos projetos ainda pode ser determinada pelo VS em vez de fazê-lo manualmente dividindo sua solução .
fonte
Vou sugerir que você leia os artigos de Patric Smacchia sobre esse assunto:
Você também pode ler este artigo para ajudar a reduzir o número de projetos e melhorar o tempo de compilação.
fonte
Sugiro ter cópia local = false para quase todos os projetos, exceto o que está no topo da árvore de dependência. E para todas as referências no conjunto superior, copie local = true. Vejo muitas pessoas sugerindo o compartilhamento de um diretório de saída; Eu acho que essa é uma ideia horrível baseada na experiência. Se o seu projeto de inicialização contiver referências a uma DLL que qualquer outro projeto contenha uma referência a você, em algum momento, ocorrerá uma violação de acesso / compartilhamento, mesmo que a cópia local = false em tudo e sua compilação falhe. Esse problema é muito chato e difícil de rastrear. Sugiro completamente ficar longe de um diretório de saída shard e, em vez de ter o projeto no topo da cadeia de dependência, grave os assemblies necessários na pasta correspondente. Se você não tem um projeto na parte superior, então eu sugiro uma cópia pós-compilação para colocar tudo no lugar certo. Além disso, eu tentaria ter em mente a facilidade da depuração. Quaisquer projetos exe ainda deixo copy local = true para que a experiência de depuração F5 funcione.
fonte
Você está certo. CopyLocal absolutamente matará seus tempos de construção. Se você tiver uma grande árvore de origem, desative o CopyLocal. Infelizmente, não é tão fácil quanto deve ser desabilitá-lo corretamente. Respondi a essa pergunta exata sobre como desativar o CopyLocal em Como substituir a configuração de CopyLocal (particular) para referências no .NET do MSBUILD . Confira. Bem como práticas recomendadas para grandes soluções no Visual Studio (2008).
Aqui estão mais algumas informações sobre o CopyLocal como eu o vejo.
O CopyLocal foi implementado realmente para oferecer suporte à depuração local. Ao preparar seu aplicativo para empacotamento e implantação, você deve criar seus projetos na mesma pasta de saída e ter todas as referências necessárias lá.
Escrevi sobre como lidar com a criação de grandes árvores de origem no artigo MSBuild: Práticas recomendadas para criar construções confiáveis, Parte 2 .
fonte
Na minha opinião, ter uma solução com 100 projetos é um GRANDE erro. Você provavelmente poderia dividir sua solução em pequenas unidades lógicas válidas, simplificando a manutenção e as compilações.
fonte
Estou surpreso que ninguém tenha mencionado o uso de hardlinks. Em vez de copiar os arquivos, ele cria um link físico para o arquivo original. Isso economiza espaço em disco e acelera bastante a compilação. Isso pode ser ativado na linha de comando com as seguintes propriedades:
/ p: CreateHardLinksForAdditionalFilesIfPossible = true; CreateHardLinksForCopyAdditionalFilesIfPossible = true; CreateHardLinksForCopyFilesToOutputDirectoryIfPossible = true; CreateHardLinksForCopyLossIfPossible = true; CreateHardLinksForPublishFilesIf
Você também pode adicionar isso a um arquivo de importação central, para que todos os seus projetos também possam obter esse benefício.
fonte
Se você definiu a estrutura de dependência por meio de referências de projeto ou dependências em nível de solução, é seguro ativar "Copy Local", diria até que é uma prática recomendada, pois isso permitirá que você use o MSBuild 3.5 para executar sua compilação paralelamente ( via / maxcpucount) sem processos diferentes que tropeçam entre si ao tentar copiar montagens referenciadas.
fonte
nossa "melhor prática" é evitar soluções em muitos projetos. Temos um diretório chamado "matrix" com versões atuais de assemblies e todas as referências são desse diretório. Se você alterar algum projeto e puder dizer "agora a alteração está concluída", poderá copiar a montagem no diretório "matriz". Portanto, todos os projetos que dependem dessa montagem terão a versão atual (= mais recente).
Se você tem poucos projetos em solução, o processo de criação é muito mais rápido.
Você pode automatizar a etapa "copiar montagem no diretório da matriz" usando macros do visual studio ou com "menu -> ferramentas -> ferramentas externas ...".
fonte
Você não precisa alterar os valores CopyLocal. Tudo o que você precisa fazer é predefinir um $ (OutputPath) comum para todos os projetos na solução e predefinir $ (UseCommonOutputDirectory) como true. Veja isto: http://blogs.msdn.com/b/kirillosenkov/archive/2015/04/04/using-a-common-intermediate-and-output-directory-for-your-solution.aspx
fonte
Definir CopyLocal = false reduzirá o tempo de construção, mas poderá causar problemas diferentes durante a implantação.
Existem muitos cenários, quando você precisa que a opção Copiar local seja deixada como True, por exemplo,
Os possíveis problemas descritos nas perguntas do SO
" Quando local de cópia deve ser definido como verdadeiro e quando não deve? ",
" Mensagem de erro 'Não é possível carregar um ou mais dos tipos solicitados. Recupere a propriedade LoaderExceptions para obter mais informações.' "
e a resposta de aaron-stainback para esta pergunta.
Minha experiência com a configuração de CopyLocal = false NÃO foi bem-sucedida. Veja no meu blog "NÃO alterar" copiar local "as referências do projeto para false, a menos que compreenda subsequências."
O tempo para resolver os problemas excedem os benefícios da configuração de copyLocal = false.
fonte
CopyLocal=False
certamente causará alguns problemas, mas existem soluções para eles. Além disso, você deve corrigir a formatação do seu blog, dificilmente legível, e dizer lá que "fui avisado pelo <consultor aleatório> da <empresa aleatória> sobre possíveis erros durante as implantações" não é um argumento. Você precisa se desenvolver.Costumo construir em um diretório comum (por exemplo, .. \ bin), para poder criar pequenas soluções de teste.
fonte
Você pode tentar usar uma pasta na qual todos os assemblies compartilhados entre projetos serão copiados, criar uma variável de ambiente DEVPATH e definir
<developmentMode developerInstallation="true" />
no arquivo machine.config na estação de trabalho de cada desenvolvedor. A única coisa que você precisa fazer é copiar qualquer nova versão na pasta onde a variável DEVPATH aponte.
Divida também sua solução em poucas soluções menores, se possível.
fonte
Pode não ser a melhor prática, mas é assim que eu trabalho.
Notei que o C ++ gerenciado despeja todos os seus binários em $ (SolutionDir) / 'DebugOrRelease'. Então, eu joguei todos os meus projetos de C # lá também. Também desativei o "Copiar local" de todas as referências a projetos na solução. Eu tive uma melhora notável no tempo de construção em minha pequena solução de 10 projetos. Essa solução é uma mistura de projetos em C #, C ++ gerenciado, C ++ nativo, C # webservice e instalador.
Talvez algo esteja quebrado, mas como essa é a única maneira de trabalhar, não percebo.
Seria interessante descobrir o que estou quebrando.
fonte
Normalmente, você só precisa copiar o local se quiser que seu projeto use a DLL que está na sua lixeira versus o que está em outro lugar (o GAC, outros projetos etc.)
Eu tenderia a concordar com as outras pessoas que você também deveria tentar, se possível, interromper essa solução.
Você também pode usar o Gerenciador de Configurações para criar diferentes configurações de compilação nessa solução que criará apenas determinados conjuntos de projetos.
Seria estranho se todos os 100 projetos dependessem um do outro, portanto, você poderá dividi-lo ou usar o Gerenciador de Configurações para ajudar a si mesmo.
fonte
Você pode ter suas referências de projetos apontando para as versões de depuração das DLLs. Do que no seu script msbuild, você pode definir o
/p:Configuration=Release
, assim você terá uma versão de lançamento do seu aplicativo e de todos os assemblies de satélite.fonte
Se você deseja ter um local central para referenciar uma DLL usando copiar local false, falhará sem o GAC, a menos que você faça isso.
http://nbaked.wordpress.com/2010/03/28/gac-alternative/
fonte
Se a referência não estiver contida no GAC, devemos definir o Local da cópia como true para que o aplicativo funcione. Se tivermos certeza de que a referência será pré-instalada no GAC, ela poderá ser definida como false.
fonte
Bem, eu certamente não sei como os problemas funcionam, mas eu tive contato com uma solução de compilação que ajudou a si mesma, de modo que todos os arquivos criados foram colocados em um ramdisk com a ajuda de links simbólicos .
c: \ pasta de solução \ obj -> ramdisk r: \ pasta de solução \ obj \
Você também pode informar adicionalmente ao visual studio qual diretório temporário ele pode usar para a compilação.
Na verdade, não foi tudo o que fez. Mas realmente atingiu minha compreensão do desempenho.
100% de uso do processador e um grande projeto em menos de 3 minutos com todas as dependências.
fonte