Ao desenvolver complementos para aplicacións CAD (
Cando só tes un complemento ou aínda es un principiante autodidacta nesta materia, podes simplemente facer unha copia do proxecto, cambiar os lugares necesarios nel e montar unha nova versión do complemento. En consecuencia, as modificacións posteriores do código suporán un aumento múltiple dos custos laborais.
A medida que adquira experiencia e coñecemento, atopará varias formas de automatizar este proceso. Percorrín este camiño e quero contarvos o que acabei e o cómodo que é.
En primeiro lugar, vexamos un método que é obvio e que usei durante moito tempo.
Ligazóns a ficheiros do proxecto
E para que todo sexa sinxelo, visual e comprensible, describirei todo usando un exemplo abstracto de desenvolvemento de complementos.
Abramos Visual Studio (teño a versión Community 2019. E si, en ruso) e creemos unha nova solución. Imos chamalo MySuperPluginForRevit
Faremos un complemento para Revit para as versións 2015-2020. Polo tanto, imos crear un novo proxecto na solución (Net Framework Class Library) e chamalo MySuperPluginForRevit_2015
Necesitamos engadir ligazóns á API de Revit. Por suposto, podemos engadir ligazóns a ficheiros locais (teremos que instalar todos os SDK necesarios ou todas as versións de Revit), pero seguiremos inmediatamente o camiño correcto e conectaremos o paquete NuGet. Podes atopar bastantes paquetes, pero vou usar o meu.
Despois de conectar o paquete, fai clic co botón dereito sobre o elemento "referencias"e selecciona o elemento"Mover packages.config a PackageReference...»
Se de súpeto neste momento comezas a entrar en pánico, porque na xanela das propiedades do paquete non haberá ningún elemento importante "Copiar localmente", que definitivamente necesitamos establecer o valor teito, entón non te asustes: vai ao cartafol co proxecto, abre o ficheiro coa extensión .csproj nun editor conveniente para ti (uso Notepad++) e atopa alí unha entrada sobre o noso paquete. Ela agora ten este aspecto:
<PackageReference Include="ModPlus.Revit.API.2015">
<Version>1.0.0</Version>
</PackageReference>
Engade unha propiedade a ela tempo de execución. Resultará así:
<PackageReference Include="ModPlus.Revit.API.2015">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
Agora, ao construír un proxecto, os ficheiros do paquete non se copiarán no cartafol de saída.
Imos máis aló: imaxinemos inmediatamente que o noso complemento usará algo da API de Revit, que cambiou co paso do tempo cando se lanzaron novas versións. Ben, ou só necesitamos cambiar algo no código dependendo da versión de Revit para a que esteamos a facer o complemento. Para resolver tales diferenzas no código, utilizaremos símbolos de compilación condicional. Abra as propiedades do proxecto, vai á pestana "Asemblea"e no campo"Notación de compilación condicional"escribamos R2015.
Teña en conta que o símbolo debe engadirse tanto para a configuración de depuración como para a versión.
Ben, mentres estamos na xanela de propiedades, imos inmediatamente á pestana "App"e no campo"Espazo de nomes predeterminado» eliminar o sufixo _2015para que o noso espazo de nomes sexa universal e independente do nome da asemblea:
No meu caso, no produto final, os complementos de todas as versións colócanse nun cartafol, polo que os nomes dos meus conxuntos permanecen co sufixo do formulario _20хх. Pero tamén pode eliminar o sufixo do nome da montaxe se se supón que os ficheiros están situados en cartafoles diferentes.
Imos ao código do ficheiro Clase 1.cs e simula algún código alí, tendo en conta as diferentes versións de Revit:
namespace MySuperPluginForRevit
{
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
[Regeneration(RegenerationOption.Manual)]
[Transaction(TransactionMode.Manual)]
public class Class1 : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
#if R2015
TaskDialog.Show("ModPlus", "Hello Revit 2015");
#elif R2016
TaskDialog.Show("ModPlus", "Hello Revit 2016");
#elif R2017
TaskDialog.Show("ModPlus", "Hello Revit 2017");
#elif R2018
TaskDialog.Show("ModPlus", "Hello Revit 2018");
#elif R2019
TaskDialog.Show("ModPlus", "Hello Revit 2019");
#elif R2020
TaskDialog.Show("ModPlus", "Hello Revit 2020");
#endif
return Result.Succeeded;
}
}
}
Inmediatamente tiven en conta todas as versións de Revit anteriores á versión 2015 (que estaban dispoñibles no momento da escritura) e inmediatamente tiven en conta a presenza de símbolos de compilación condicional, que se crean usando o mesmo modelo.
Pasemos ao máis destacado. Creamos un novo proxecto na nosa solución, só para a versión do complemento para Revit 2016. Repetimos todos os pasos descritos anteriormente, respectivamente, substituíndo o número 2015 polo número 2016. Pero o ficheiro Clase 1.cs eliminar do novo proxecto.
Arquivo co código necesario - Clase 1.cs – xa o temos e só necesitamos inserir unha ligazón a el nun novo proxecto. Hai dúas formas de inserir ligazóns:
- Longo - Fai clic co botón dereito sobre o proxecto e selecciona "Agregar"->"Elemento existente", na xanela que se abre, busque o ficheiro necesario e en lugar da opción "Agregar"seleccione a opción"Engadir como conexión»
- Curto – directamente no explorador de solucións, seleccione o ficheiro desexado (ou mesmo ficheiros, ou mesmo cartafoles enteiros) e arrástreo a un novo proxecto mentres manteña premida a tecla Alt. Ao arrastrar, verá que ao premer a tecla Alt, o cursor do rato pasará dun signo máis a unha frecha.
ACTUALIZACIÓN: Fixen unha pequena confusión neste parágrafo: para transferir varios ficheiros, debes manter premido Maiús + Alt!
Despois de realizar o trámite, teremos un expediente no segundo proxecto Clase 1.cs coa icona correspondente (frecha azul):
Ao editar o código na xanela do editor, tamén podes escoller en que contexto do proxecto queres mostrar o código, o que che permitirá ver o código que se edita baixo diferentes símbolos de compilación condicional:
Creamos todos os demais proxectos (2017-2020) usando este esquema. Truco de vida: se arrastras ficheiros no Explorador de solucións non desde o proxecto base, senón desde o proxecto onde xa están inseridos como ligazón, non tes que manter premida a tecla Alt.
A opción descrita é bastante boa ata o momento de engadir unha nova versión do complemento ou ata o momento de engadir novos ficheiros ao proxecto; todo isto faise moi tedioso. E recentemente, de súpeto, decateime de como resolvelo todo cun proxecto e estamos pasando ao segundo método
A maxia das configuracións
Despois de terminar de ler aquí, podes exclamar: "Por que describiches o primeiro método, se o artigo trata inmediatamente sobre o segundo?!" E describín todo para que quede máis claro por que necesitamos símbolos de compilación condicional e en que lugares difieren os nosos proxectos. E agora queda máis claro para nós exactamente que diferenzas nos proxectos debemos implementar, quedando só un proxecto.
E para que todo sexa máis evidente, non imos crear un novo proxecto, senón que faremos cambios no noso proxecto actual creado de primeira maneira.
Entón, en primeiro lugar, eliminamos todos os proxectos da solución excepto o principal (que contén os ficheiros directamente). Eses. proxectos para as versións 2016-2020. Abre o cartafol coa solución e elimina alí os cartafoles destes proxectos.
Quédanos un proxecto na nosa decisión: MySuperPluginForRevit_2015. Abre as súas propiedades e:
- Na pestana "App"elimina o sufixo do nome da asemblea _2015 (máis tarde quedará claro por que)
- Na pestana "Asemblea» eliminar o símbolo de compilación condicional R2015 do campo correspondente
Nota: a última versión de Visual Studio ten un erro: os símbolos de compilación condicional non se amosan na xanela de propiedades do proxecto, aínda que están dispoñibles. Se experimentas este fallo, debes eliminalos manualmente do ficheiro .csproj. Non obstante, aínda temos que traballar nel, así que segue lendo.
Cambia o nome do proxecto na xanela do Explorador de solucións eliminando o sufixo _2015 e, a continuación, elimine o proxecto da solución. Isto é necesario para manter a orde e os sentimentos dos perfeccionistas! Abrimos o cartafol da nosa solución, renomeamos alí o cartafol do proxecto do mesmo xeito e cargamos o proxecto de novo na solución.
Abre o xestor de configuración. configuración dos EUA Solte en principio, non será necesario, polo que o borramos. Creamos novas configuracións con nomes que xa nos son familiares R2015, R2016, ..., R2020. Teña en conta que non precisa copiar a configuración doutras configuracións e non precisa crear configuracións do proxecto:
Vaia ao cartafol co proxecto e abra o ficheiro coa extensión .csproj nun editor conveniente para vostede. Por certo, tamén podes abrilo en Visual Studio: debes descargar o proxecto e, a continuación, o elemento desexado estará no menú contextual:
A edición en Visual Studio é incluso preferible, xa que o editor aliña e solicita.
No ficheiro veremos os elementos
Vaia ao elemento común (primeiro). PropertyGroup e mira a propiedade Nome da Asemblea – este é o nome da asemblea e deberiamos telo sen sufixo _2015. Se hai un sufixo, elimínao.
Buscar un elemento cunha condición
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
Non o necesitamos, borrámolo.
Elemento con condición
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
serán necesarios para traballar na fase de desenvolvemento e depuración do código. Podes cambiar as súas propiedades segundo as túas necesidades: establecer diferentes camiños de saída, cambiar os símbolos de compilación condicional, etc.
Agora imos crear novos elementos PropertyGroup para as nosas configuracións. Nestes elementos só necesitamos establecer catro propiedades:
- Ruta de saída - cartafol de saída. Definei o valor predeterminado binR20xx
- Definir constantes – Símbolos de compilación condicional. O valor debe ser especificado TRACE;R20хх
- TargetFrameworkVersion - Versión da plataforma. As diferentes versións da API de Revit requiren que se especifiquen diferentes plataformas.
- Nome da Asemblea – nome do conxunto (é dicir, o nome do ficheiro). Podes escribir o nome exacto da montaxe, pero para a súa versatilidade recomendo escribir o valor $(AssemblyName)_20хх. Para iso, eliminamos previamente o sufixo do nome da montaxe
A característica máis importante de todos estes elementos é que simplemente se poden copiar noutros proxectos sen cambialos en absoluto. Máis adiante no artigo anexarei todo o contido do ficheiro .csproj.
Está ben, descubrimos as propiedades do proxecto, non é difícil. Pero que facer coas bibliotecas de complementos (paquetes NuGet). Se miramos máis aló, veremos que as bibliotecas incluídas están especificadas nos elementos
O elemento vén na nosa axuda
Usando elemento Escoller, establecemos diferentes paquetes NuGet para diferentes configuracións:
Todos os contidos csproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5AD738D6-4122-4E76-B865-BE7CE0F6B3EB}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MySuperPluginForRevit</RootNamespace>
<AssemblyName>MySuperPluginForRevit</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>binDebug</OutputPath>
<DefineConstants>DEBUG;R2015</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'R2015|AnyCPU' ">
<OutputPath>binR2015</OutputPath>
<DefineConstants>TRACE;R2015</DefineConstants>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<AssemblyName>$(AssemblyName)_2015</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'R2016|AnyCPU' ">
<OutputPath>binR2016</OutputPath>
<DefineConstants>TRACE;R2016</DefineConstants>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<AssemblyName>$(AssemblyName)_2016</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'R2017|AnyCPU' ">
<OutputPath>binR2017</OutputPath>
<DefineConstants>TRACE;R2017</DefineConstants>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<AssemblyName>$(AssemblyName)_2017</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'R2018|AnyCPU' ">
<OutputPath>binR2018</OutputPath>
<DefineConstants>TRACE;R2018</DefineConstants>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<AssemblyName>$(AssemblyName)_2018</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'R2019|AnyCPU' ">
<OutputPath>binR2019</OutputPath>
<DefineConstants>TRACE;R2019</DefineConstants>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<AssemblyName>$(AssemblyName)_2019</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'R2020|AnyCPU' ">
<OutputPath>binR2020</OutputPath>
<DefineConstants>TRACE;R2020</DefineConstants>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<AssemblyName>$(AssemblyName)_2020</AssemblyName>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Class1.cs" />
<Compile Include="PropertiesAssemblyInfo.cs" />
</ItemGroup>
<Choose>
<When Condition=" '$(Configuration)'=='R2015' ">
<ItemGroup>
<PackageReference Include="ModPlus.Revit.API.2015">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
</When>
<When Condition=" '$(Configuration)'=='R2016' ">
<ItemGroup>
<PackageReference Include="ModPlus.Revit.API.2016">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
</When>
<When Condition=" '$(Configuration)'=='R2017' ">
<ItemGroup>
<PackageReference Include="ModPlus.Revit.API.2017">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
</When>
<When Condition=" '$(Configuration)'=='R2018' ">
<ItemGroup>
<PackageReference Include="ModPlus.Revit.API.2018">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
</When>
<When Condition=" '$(Configuration)'=='R2019' ">
<ItemGroup>
<PackageReference Include="ModPlus.Revit.API.2019">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
</When>
<When Condition=" '$(Configuration)'=='R2020' or '$(Configuration)'=='Debug'">
<ItemGroup>
<PackageReference Include="ModPlus.Revit.API.2020">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
</When>
</Choose>
<Import Project="$(MSBuildToolsPath)Microsoft.CSharp.targets" />
</Project>
Teña en conta que nunha das condicións especifiquei dúas configuracións mediante OU. Deste xeito, o paquete necesario conectarase durante a configuración Depurar.
E aquí temos case todo perfecto. Volvemos cargar o proxecto, activamos a configuración que necesitamos, chamamos ao elemento " no menú contextual da solución (non o proxecto)Restaura todos os paquetes NuGet"e vemos como cambian os nosos paquetes.
E nesta fase cheguei a un camiño sen saída: para recoller todas as configuracións á vez, poderiamos usar o conxunto de lotes (menú "Asemblea"->"Construción por lotes"), pero ao cambiar de configuración, os paquetes non se restauran automaticamente. E á hora de montar o proxecto, isto tampouco sucede, aínda que, en teoría, debería. Non atopei unha solución a este problema utilizando medios estándar. E o máis probable é que este sexa tamén un erro de Visual Studio.
Polo tanto, para a montaxe por lotes, decidiuse utilizar un sistema de montaxe automatizado especial
Entón, vai ao cartafol da nosa solución (non ao proxecto), manteña premida a tecla Desprazarse e fai clic co botón dereito nun espazo baleiro do cartafol; no menú contextual, selecciona o elemento "Abre aquí a xanela de PowerShell».
Se non o tes instalado nuclear, a continuación, escriba primeiro o comando
dotnet tool install Nuke.GlobalTool –global
Agora escribe o comando nuclear e pediráselle que configure nuclear para o proxecto actual. Non sei como escribir isto máis correctamente en ruso; en inglés escribirase. Non se puido atopar o ficheiro .nuke. Queres configurar unha compilación? [i/n]
Preme a tecla Y e despois haberá elementos de configuración directa. Necesitamos a opción máis sinxela de usar MSBuild, así que respondemos como na captura de pantalla:
Imos a Visual Studio, que nos pedirá que recarguemos a solución, xa que se lle engadiu un novo proxecto. Recargamos a solución e vemos que temos un proxecto construír no que só nos interesa un ficheiro - Construír.cs
Abre este ficheiro e escribe un script para construír o proxecto para todas as configuracións. Ben, ou usa o meu script, que podes editar segundo as túas necesidades:
using System.IO;
using Nuke.Common;
using Nuke.Common.Execution;
using Nuke.Common.ProjectModel;
using Nuke.Common.Tools.MSBuild;
using static Nuke.Common.Tools.MSBuild.MSBuildTasks;
[CheckBuildProjectConfigurations]
[UnsetVisualStudioEnvironmentVariables]
class Build : NukeBuild
{
public static int Main () => Execute<Build>(x => x.Compile);
[Solution] readonly Solution Solution;
// If the solution name and the project (plugin) name are different, then indicate the project (plugin) name here
string PluginName => Solution.Name;
Target Compile => _ => _
.Executes(() =>
{
var project = Solution.GetProject(PluginName);
if (project == null)
throw new FileNotFoundException("Not found!");
var build = new List<string>();
foreach (var (_, c) in project.Configurations)
{
var configuration = c.Split("|")[0];
if (configuration == "Debug" || build.Contains(configuration))
continue;
Logger.Normal($"Configuration: {configuration}");
build.Add(configuration);
MSBuild(_ => _
.SetProjectFile(project.Path)
.SetConfiguration(configuration)
.SetTargets("Restore"));
MSBuild(_ => _
.SetProjectFile(project.Path)
.SetConfiguration(configuration)
.SetTargets("Rebuild"));
}
});
}
Volvemos á xanela de PowerShell e escribimos o comando de novo nuclear (podes escribir o comando nuclear indicando o requirido Branco. Pero temos un Branco, que se executa por defecto). Despois de premer a tecla Intro, sentirémonos como auténticos hackers, xa que, como nunha película, o noso proxecto ensamblarase automaticamente para diferentes configuracións.
Por certo, pode usar PowerShell directamente desde Visual Studio (menú "Ver"->"Outras fiestras"->"Consola do xestor de paquetes"), pero todo será en branco e negro, o que non é moi cómodo.
Isto conclúe o meu artigo. Estou seguro de que pode descubrir a opción de AutoCAD vostede mesmo. Espero que o material aquí presentado atope os seus "clientes".
Спасибо за внимание!
Fonte: www.habr.com