Al desarrollar complementos para aplicaciones CAD (
Cuando solo tiene un complemento o aún es un principiante autodidacta en este tema, simplemente puede hacer una copia del proyecto, cambiar los lugares necesarios en él y ensamblar una nueva versión del complemento. En consecuencia, los cambios posteriores en el código implicarán un aumento múltiple de los costos laborales.
A medida que adquiera experiencia y conocimientos, encontrará varias formas de automatizar este proceso. Caminé por este camino y quiero contarles con qué terminé y lo conveniente que es.
Primero, veamos un método que es obvio y que he usado durante mucho tiempo.
Enlaces a archivos de proyecto
Y para que todo sea simple, visual y comprensible, lo describiré todo utilizando un ejemplo abstracto de desarrollo de complementos.
Abramos Visual Studio (tengo la versión Community 2019. Y sí, en ruso) y creemos una nueva solución. llamémoslo MiSuperPluginParaRevit
Crearemos un complemento para Revit para las versiones 2015-2020. Por lo tanto, creemos un nuevo proyecto en la solución (Biblioteca de clases Net Framework) y llamémoslo MiSuperPluginParaRevit_2015
Necesitamos agregar enlaces a la API de Revit. Por supuesto, podemos agregar enlaces a archivos locales (necesitaremos instalar todos los SDK necesarios o todas las versiones de Revit), pero inmediatamente seguiremos el camino correcto y conectaremos el paquete NuGet. Puedes encontrar bastantes paquetes, pero usaré el mío.
Después de conectar el paquete, haga clic derecho en el elemento "referencias" y seleccione el elemento "Mueva paquetes.config a PackageReference...»
Si de repente en este punto empiezas a entrar en pánico, porque en la ventana de propiedades del paquete no habrá ningún elemento importante "Copiar localmente", que definitivamente debemos establecer en el valor false, entonces no entre en pánico: vaya a la carpeta con el proyecto, abra el archivo con la extensión .csproj en un editor que le resulte conveniente (yo uso Notepad++) y busque allí una entrada sobre nuestro paquete. Ella se ve así ahora:
<PackageReference Include="ModPlus.Revit.API.2015">
<Version>1.0.0</Version>
</PackageReference>
Agregarle una propiedad tiempo de ejecución. Resultará así:
<PackageReference Include="ModPlus.Revit.API.2015">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
Ahora, al crear un proyecto, los archivos del paquete no se copiarán a la carpeta de salida.
Vayamos más allá: imaginemos inmediatamente que nuestro complemento utilizará algo de la API de Revit, que ha cambiado con el tiempo cuando se lanzaron nuevas versiones. Bueno, o simplemente necesitamos cambiar algo en el código dependiendo de la versión de Revit para la que estemos creando el complemento. Para resolver tales diferencias en el código, usaremos símbolos de compilación condicional. Abra las propiedades del proyecto, vaya a la pestaña "asamblea"y en el campo"Notación de compilación condicional"vamos a escribir R2015.
Tenga en cuenta que el símbolo debe agregarse para las configuraciones de depuración y versión.
Bueno, ya que estamos en la ventana de propiedades, inmediatamente nos dirigimos a la pestaña “solicitud"y en el campo"Espacio de nombres predeterminado» eliminar el sufijo _2015para que nuestro espacio de nombres sea universal e independiente del nombre del ensamblado:
En mi caso, en el producto final, los complementos de todas las versiones se colocan en una carpeta, por lo que los nombres de mis ensamblados permanecen con el sufijo del formulario. _20хх. Pero también puede eliminar el sufijo del nombre del ensamblaje si se supone que los archivos deben estar ubicados en carpetas diferentes.
Vayamos al código del archivo. Clase1.cs y simular algo de código allí, teniendo en cuenta diferentes versiones 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 tomé en cuenta todas las versiones de Revit superiores a la versión 2015 (que estaban disponibles en el momento de escribir este artículo) e inmediatamente tomé en cuenta la presencia de símbolos de compilación condicionales, que se crean utilizando la misma plantilla.
Pasemos al punto culminante principal. Creamos un nuevo proyecto en nuestra solución, solo para la versión del complemento para Revit 2016. Repetimos todos los pasos descritos anteriormente, respectivamente, reemplazando el número 2015 por el número 2016. Pero el archivo Clase1.cs eliminar del nuevo proyecto.
Archivo con el código requerido - Clase1.cs – ya lo tenemos y solo necesitamos insertar un enlace en un nuevo proyecto. Hay dos formas de insertar enlaces:
- Largo – haga clic derecho en el proyecto y seleccione “Añadir"->"Elemento existente", en la ventana que se abre, busque el archivo requerido y en lugar de la opción "Añadir"seleccione la opción"Agregar como conexión»
- corto – directamente en el explorador de soluciones, seleccione el archivo deseado (o incluso archivos, o incluso carpetas enteras) y arrástrelo a un nuevo proyecto mientras mantiene presionada la tecla Alt. Mientras arrastra, verá que cuando presione la tecla Alt, el cursor del mouse cambiará de un signo más a una flecha.
UPD: Cometí un poco de confusión en este párrafo: para transferir varios archivos debes mantener presionado Mayús + Alt!
Luego de realizar el trámite, ya tendremos un archivo en el segundo proyecto. Clase1.cs con el icono correspondiente (flecha azul):
Al editar código en la ventana del editor, también puede elegir en qué contexto del proyecto mostrar el código, lo que le permitirá ver el código que se está editando bajo diferentes símbolos de compilación condicional:
Creamos todos los demás proyectos (2017-2020) utilizando este esquema. Truco de vida: si arrastra archivos en el Explorador de soluciones no desde el proyecto base, sino desde el proyecto donde ya están insertados como un enlace, ¡no tendrá que mantener presionada la tecla Alt!
La opción descrita es bastante buena hasta el momento de agregar una nueva versión del complemento o hasta el momento de agregar nuevos archivos al proyecto; todo esto se vuelve muy tedioso. Y recientemente, de repente me di cuenta de cómo solucionarlo todo con un proyecto y pasamos al segundo método.
La magia de las configuraciones.
Habiendo terminado de leer aquí, puede exclamar: "¿Por qué describiste el primer método, si el artículo trata inmediatamente sobre el segundo?" Y describí todo para que quede más claro por qué necesitamos símbolos de compilación condicional y en qué lugares difieren nuestros proyectos. Y ahora nos queda más claro exactamente qué diferencias en los proyectos debemos implementar, dejando solo un proyecto.
Y para que todo sea más obvio, no crearemos un nuevo proyecto, sino que haremos cambios en nuestro proyecto actual creado de la primera forma.
Entonces, antes que nada, eliminamos todos los proyectos de la solución excepto el principal (que contiene los archivos directamente). Aquellos. proyectos para las versiones 2016-2020. Abra la carpeta con la solución y elimine las carpetas de estos proyectos allí.
Nos queda un proyecto en nuestra decisión: MiSuperPluginParaRevit_2015. Abra sus propiedades y:
- En la pestaña "solicitud"eliminar el sufijo del nombre del ensamblado _2015 (quedará claro por qué más adelante)
- En la pestaña "asamblea» eliminar el símbolo de compilación condicional R2015 del campo correspondiente
Nota: la última versión de Visual Studio tiene un error: los símbolos de compilación condicional no se muestran en la ventana de propiedades del proyecto, aunque están disponibles. Si experimenta este problema, deberá eliminarlo manualmente del archivo .csproj. Sin embargo, todavía tenemos que trabajar en ello, así que sigue leyendo.
Cambie el nombre del proyecto en la ventana del Explorador de soluciones eliminando el sufijo _2015 y luego elimine el proyecto de la solución. ¡Esto es necesario para mantener el orden y los sentimientos de los perfeccionistas! Abrimos la carpeta de nuestra solución, cambiamos el nombre de la carpeta del proyecto allí de la misma manera y cargamos el proyecto nuevamente en la solución.
Abra el administrador de configuración. configuración de EE. UU. tortugitas en principio no será necesario, por lo que lo eliminamos. Creamos nuevas configuraciones con nombres que ya nos resultan familiares R2015, R2016, ..., R2020. Tenga en cuenta que no necesita copiar configuraciones de otras configuraciones y no necesita crear configuraciones de proyecto:
Vaya a la carpeta con el proyecto y abra el archivo con la extensión .csproj en un editor que le resulte conveniente. Por cierto, también puede abrirlo en Visual Studio; debe descargar el proyecto y luego el elemento deseado estará en el menú contextual:
Incluso es preferible editar en Visual Studio, ya que el editor alinea y solicita.
En el archivo veremos los elementos.
Ir al elemento común (primer) grupo de propiedades y mira la propiedad Nombre de ensamblaje – este es el nombre del ensamblaje y deberíamos tenerlo sin sufijo _2015. Si hay un sufijo, elimínelo.
Encontrar un elemento con una condición.
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
No lo necesitamos, lo eliminamos.
Elemento con condición
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
Será necesario trabajar en la etapa de desarrollo y depuración del código. Puede cambiar sus propiedades para adaptarlas a sus necesidades: establecer diferentes rutas de salida, cambiar símbolos de compilación condicionales, etc.
Ahora creemos nuevos elementos. grupo de propiedades para nuestras configuraciones. En estos elementos sólo necesitamos establecer cuatro propiedades:
- Ruta de salida – carpeta de salida. Establecí el valor predeterminado binR20xx
- Definir constantes – símbolos de compilación condicional. Se debe especificar el valor TRAZA;R20хх
- Versión del marco de destino – versión de plataforma. Las diferentes versiones de la API de Revit requieren que se especifiquen diferentes plataformas.
- Nombre de ensamblaje – nombre del ensamblado (es decir, nombre del archivo). Puedes escribir el nombre exacto del ensamblado, pero por versatilidad recomiendo escribir el valor $(NombreEnsamblaje)_20хх. Para hacer esto, previamente eliminamos el sufijo del nombre del ensamblaje.
La característica más importante de todos estos elementos es que pueden simplemente copiarse en otros proyectos sin cambiarlos en absoluto. Más adelante en el artículo adjuntaré todo el contenido del archivo .csproj.
Bien, hemos descubierto las propiedades del proyecto; no es difícil. Pero qué hacer con las bibliotecas de complementos (paquetes NuGet). Si miramos más allá, veremos que las bibliotecas incluidas están especificadas en los elementos
El elemento viene en nuestra ayuda.
Usando elemento Elige, configuramos diferentes paquetes NuGet para diferentes configuraciones:
Todos los contenidos 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>
Tenga en cuenta que en una de las condiciones especifiqué dos configuraciones a través de O. De esta manera el paquete requerido se conectará durante la configuración. Depurar.
Y aquí lo tenemos casi todo perfecto. Volvemos a cargar el proyecto, habilitamos la configuración que necesitamos, llamamos al elemento “ en el menú contextual de la solución (no el proyecto)Restaurar todos los paquetes NuGet"Y vemos cómo cambian nuestros paquetes.
Y en esta etapa llegué a un callejón sin salida: para recopilar todas las configuraciones a la vez, podríamos usar el ensamblaje por lotes (menú "asamblea"->"Construcción por lotes"), pero al cambiar de configuración, los paquetes no se restauran automáticamente. Y a la hora de montar el proyecto esto tampoco sucede, aunque, en teoría, debería. No he encontrado una solución a este problema utilizando medios estándar. Y lo más probable es que esto también sea un error de Visual Studio.
Por lo tanto, para el ensamblaje por lotes, se decidió utilizar un sistema de ensamblaje automatizado especial.
Entonces, vaya a la carpeta de nuestra solución (no al proyecto), mantenga presionada la tecla Shift y haga clic derecho en un espacio vacío en la carpeta; en el menú contextual seleccione el elemento "Abra la ventana de PowerShell aquí".
Si no lo tienes instalado Nuke, luego primero escribe el comando
dotnet tool install Nuke.GlobalTool –global
Ahora escribe el comando Nuke y se le pedirá que configure Nuke para el proyecto actual. No sé cómo escribir esto más correctamente en ruso; en inglés se escribirá No se pudo encontrar el archivo .nuke. ¿Quieres configurar una compilación? [t/n]
Presione la tecla Y y luego habrá elementos de configuración directa. Necesitamos la opción más simple usando MSBuild, entonces respondemos como en la captura de pantalla:
Vayamos a Visual Studio, que nos pedirá que recarguemos la solución, ya que se le ha agregado un nuevo proyecto. Recargamos la solución y vemos que tenemos un proyecto. build en el que estamos interesados en un solo archivo - Construir.cs
Abra este archivo y escriba un script para construir el proyecto para todas las configuraciones. Bueno, o usa mi script, que puedes editar según tus 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 a la ventana de PowerShell y volvemos a escribir el comando Nuke (puedes escribir el comando Nuke indicando el requerido Target. pero tenemos uno Target, que se ejecuta de forma predeterminada). Después de presionar la tecla Enter, nos sentiremos como verdaderos hackers, porque, como en una película, nuestro proyecto se ensamblará automáticamente para diferentes configuraciones.
Por cierto, puedes usar PowerShell directamente desde Visual Studio (menú "Ver"->"Otras ventanas"->"Consola del administrador de paquetes"), pero todo será en blanco y negro, lo que no es muy conveniente.
Con esto concluye mi artículo. Estoy seguro de que usted mismo podrá encontrar la opción para AutoCAD. Espero que el material aquí presentado encuentre sus “clientes”.
Gracias por su atención!
Fuente: habr.com