Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Multi-Target 2021 and 2022 Using MSBuild

10 REPLIES 10
Reply
Message 1 of 11
JOfford_13
2204 Views, 10 Replies

Multi-Target 2021 and 2022 Using MSBuild

For those interested you can configure a .csproj to multi-target both 2021 and 2022 on .NET Framework 4.8 using MSBuild. I posted this on a comment in @jeremytammik's blog but figured it'd be useful here too.

 

The first task is to choose what .NET 4.8 add-in you want to actively program against. There can only be one active add-in per .NET framework as far as I know. In this example I'm setting my default to Revit 2022 using the custom 'RevitVersion' property if it hasn't been configured yet.

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop" InitialTargets="Test">
  ...
  <PropertyGroup>
    <TargetFrameworks>net461;net47;net472;net48</TargetFrameworks>
    <Configurations>Debug;Release</Configurations>
    <OutputPath>bin\$(Configuration)\</OutputPath>
    <UseWindowsForms>true</UseWindowsForms>
    <RevitVersion Condition=" '$(RevitVersion)' == '' ">2022</RevitVersion>
    ...
  </PropertyGroup>

 

 Next, define configurations changes per each version.

  <PropertyGroup Condition=" '$(TargetFramework)' == 'net461' ">
    <PlatformTarget>x64</PlatformTarget>
    <DefineConstants>DEBUG;REVIT2018</DefineConstants>
    <OutputPath>bin\$(Configuration)\2018\</OutputPath>
  </PropertyGroup>

  <PropertyGroup Condition=" '$(TargetFramework)' == 'net47' ">
    <PlatformTarget>x64</PlatformTarget>
    <DefineConstants>$(DefineConstants);REVIT2019</DefineConstants>
    <OutputPath>bin\$(Configuration)\2019</OutputPath>
  </PropertyGroup>

  <PropertyGroup Condition=" '$(TargetFramework)' == 'net472' ">
    <PlatformTarget>x64</PlatformTarget>
    <DefineConstants>$(DefineConstants);REVIT2020</DefineConstants>
    <OutputPath>bin\$(Configuration)\2020\</OutputPath>
  </PropertyGroup>

  <PropertyGroup Condition=" '$(TargetFramework)' == 'net48' And '$(RevitVersion)' == '2021' ">
    <PlatformTarget>x64</PlatformTarget>
    <DefineConstants>$(DefineConstants);REVIT2021</DefineConstants>
    <OutputPath>bin\$(Configuration)\2021</OutputPath>
  </PropertyGroup>

  <PropertyGroup Condition=" '$(TargetFramework)' == 'net48' And '$(RevitVersion)' == '2022' ">
    <PlatformTarget>x64</PlatformTarget>
    <DefineConstants>$(DefineConstants);REVIT2022</DefineConstants>
    <OutputPath>bin\$(Configuration)\2022</OutputPath>
  </PropertyGroup>

 

Next, load the proper dll references for each version.

  <ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
    <Reference Include="AdWindows">
      <HintPath>C:\Program Files\Autodesk\Revit 2018\AdWindows.dll</HintPath>
      <EmbedInteropTypes>false</EmbedInteropTypes>
      <Private>false</Private>
    </Reference>
    <Reference Include="RevitAPI">
      <HintPath>C:\Program Files\Autodesk\Revit 2018\RevitAPI.dll</HintPath>
      <EmbedInteropTypes>false</EmbedInteropTypes>
      <Private>false</Private>
    </Reference>
    <Reference Include="RevitAPIUI">
      <HintPath>C:\Program Files\Autodesk\Revit 2018\RevitAPIUI.dll</HintPath>
      <EmbedInteropTypes>false</EmbedInteropTypes>
      <Private>false</Private>
    </Reference>
  </ItemGroup>

  <ItemGroup Condition=" '$(TargetFramework)' == 'net47' ">
    <Reference Include="AdWindows">
      <HintPath>C:\Program Files\Autodesk\Revit 2019\AdWindows.dll</HintPath>
      <EmbedInteropTypes>false</EmbedInteropTypes>
      <Private>false</Private>
    </Reference>
    <Reference Include="RevitAPI">
      <HintPath>C:\Program Files\Autodesk\Revit 2019\RevitAPI.dll</HintPath>
      <Private>False</Private>
      <EmbedInteropTypes>false</EmbedInteropTypes>
    </Reference>
    <Reference Include="RevitAPIUI">
      <HintPath>C:\Program Files\Autodesk\Revit 2019\RevitAPIUI.dll</HintPath>
      <EmbedInteropTypes>false</EmbedInteropTypes>
      <Private>false</Private>
    </Reference>
  </ItemGroup>

  <ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
    <Reference Include="AdWindows">
      <HintPath>C:\Program Files\Autodesk\Revit 2020\AdWindows.dll</HintPath>
      <EmbedInteropTypes>false</EmbedInteropTypes>
      <Private>false</Private>
    </Reference>
    <Reference Include="RevitAPI">
      <HintPath>C:\Program Files\Autodesk\Revit 2020\RevitAPI.dll</HintPath>
      <EmbedInteropTypes>false</EmbedInteropTypes>
      <Private>false</Private>
    </Reference>
    <Reference Include="RevitAPIUI">
      <HintPath>C:\Program Files\Autodesk\Revit 2020\RevitAPIUI.dll</HintPath>
      <EmbedInteropTypes>false</EmbedInteropTypes>
      <Private>false</Private>
    </Reference>
  </ItemGroup>

  <ItemGroup Condition=" '$(TargetFramework)' == 'net48' And '$(RevitVersion)' == '2021' ">
    <Reference Include="AdWindows">
      <HintPath>C:\Program Files\Autodesk\Revit 2021\AdWindows.dll</HintPath>
      <EmbedInteropTypes>false</EmbedInteropTypes>
      <Private>false</Private>
    </Reference>
    <Reference Include="RevitAPI">
      <HintPath>C:\Program Files\Autodesk\Revit 2021\RevitAPI.dll</HintPath>
      <EmbedInteropTypes>false</EmbedInteropTypes>
      <Private>false</Private>
    </Reference>
    <Reference Include="RevitAPIUI">
      <HintPath>C:\Program Files\Autodesk\Revit 2021\RevitAPIUI.dll</HintPath>
      <EmbedInteropTypes>false</EmbedInteropTypes>
      <Private>false</Private>
    </Reference>
  </ItemGroup>

  <ItemGroup Condition=" '$(TargetFramework)' == 'net48' And '$(RevitVersion)' == '2022' ">
    <Reference Include="AdWindows">
      <HintPath>C:\Program Files\Autodesk\Revit 2022\AdWindows.dll</HintPath>
      <EmbedInteropTypes>false</EmbedInteropTypes>
      <Private>false</Private>
    </Reference>
    <Reference Include="RevitAPI">
      <HintPath>C:\Program Files\Autodesk\Revit 2022\RevitAPI.dll</HintPath>
      <EmbedInteropTypes>false</EmbedInteropTypes>
      <Private>false</Private>
    </Reference>
    <Reference Include="RevitAPIUI">
      <HintPath>C:\Program Files\Autodesk\Revit 2022\RevitAPIUI.dll</HintPath>
      <EmbedInteropTypes>false</EmbedInteropTypes>
      <Private>false</Private>
    </Reference>
  </ItemGroup>

 

At the end, you need to configure an additional build for whatever .NET 4.8 Revit add-in you didn't set as the default above. This is nice because it will catch build errors even though the active .NET 4.8 version is something else.

  <Target Name="Test">
    <Message Importance="high" Text="-- Building $(MSBuildProjectFile), TF = $(TargetFramework), Config = $(Configuration), Revit Version = $(RevitVersion) --" />
  </Target>
  <Target Name="Build2021" BeforeTargets="DispatchToInnerBuilds">
    <Message Importance="high" Text="*** running pre-dispatch builds ***" />
    <MSBuild Projects="myProject.csproj" Properties="Configuration=$(Configuration);TargetFramework=net48;RevitVersion=2021"></MSBuild>
  </Target>

 

Side note: Using a multi-target solution means you need to keep references to all the old versions of Revit. Be sure to copy out the needed dlls before removing that Revit version from your machine.  

 

Hope this helps.

10 REPLIES 10
Message 2 of 11
jeremy_tammik
in reply to: JOfford_13

Thank you very much indeed for posting here as well. This will certainly gain greater visibility than the comment on The Building Coder. I am planning to promote this to a blog post as well for yet greater visibility and preservation for posterity. Brilliant summary!

 

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 3 of 11
jeremy_tammik
in reply to: JOfford_13

Message 4 of 11
moj
Participant
in reply to: JOfford_13

Hi,

 

I have been making Revit add-ins for several years now and my tools are working just fine on Revit 2017 up to 2022.

But why do you want to target different .net versions for each Revit version when .net48 compiles just fine for Revit 2018 although it targets .net 461?

Message 5 of 11
CADdaddy.com
in reply to: JOfford_13

Hi All,

I'm eager to use this multi-targeting feature but I'm having a problem with it. I'm thinking the problem may be something very fundamental that I'm just not getting.

 

This is how my project is built:

  1. in the PROJ file there is a variable that I set for the version year. This version year sets the target framework and paths for Autodesk references similar to the OP's example above.
  2. after setting the version year, I press the build button in VS. The built DLL is saved to the folder specified in my PROJ file.
  3. after building the first version year, I change the version year variable, then build the next year version just like in the previous step.

All of this works perfectly well and I can generate DLLs for all the version years. The only problem is that the file versions are not imbedded correctly in the DLLs and always report "0.0.0.0". I've posted about this on Stackoverflow but had no responses to date. https://stackoverflow.com/questions/70074034/fileversion-in-sdk-project-is-correct-but-is-not-reflec....

 

Is it possible that the SDK projects are only meant to be built using MSBuild instead of using the build button in the VS UI?....or.....is there a "multitargeted SDK Projects for Dummies" book I can buy???

 

Any help would be greatly appreciated.

James

Message 6 of 11
ricaun
in reply to: CADdaddy.com

Use the <Version> tag instead of <FileVersion>.

 

Something like this:

 

<Version>$(RevitVersion).0.0</Version>

 

See yaa!

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

Message 7 of 11
CADdaddy.com
in reply to: ricaun

Yes, I've messed around with both of those options (and "GenerateAssemblyInfo") while burning up two whole weekends trying to figure out why nothing works.  See same result below with "Version" instead of "FileVersion".  No matter what I do the file version in the DLL is always 0.0.0.0

 

CADdaddycom_0-1643069171733.png

 

Message 8 of 11
ricaun
in reply to: CADdaddy.com

The Version is 0.0.0.0.

The Product Name is empty.

The File description is empty.

The Copyright is empty.

The DLL file is 244 KB. 😦

 

Definitely, something crazy is happening.

 

I suppose the issue is related to the compiler, not the configuration of the csproj. Try to update the IDE.

 

I don't know if the size is real, do you have a template inside the DLL? 😅

 

Make smaller maybe...

 

 

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

Message 9 of 11
CADdaddy.com
in reply to: ricaun

Thanks for the reply.

 

I think the size is high because I've embedded some bitmaps for the ribbon buttons.

 

I actually got a new computer and a fresh VS install because I had a certain lack of confidence with my old computer.  Same problem.  I'm dreading having to try to build this project from scratch.

Message 10 of 11
kraftwerk15
in reply to: JOfford_13

I looked in to the above solution for my Solution where I started adding in the ForgeTypeId, but needed some of the parameters to be able to be added in 2021 using ParameterType and wanted to start transitioning my Solution over to ForgeTypeId in Revit 2022 and in to the future.

 

I implemented the solution above, but was having a tremendous amount of issues of trying to get the 2021 build to actually build. And not knowing a tremendous amount of MSBUILD, this gave me an opportunity to learn a little more.

 

With the first line in the original post showing:

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop" InitialTargets="Test">

 


I thought the InitialTargets was important, neh I say critical, to be able to call the message for this whole process. It wasn't. You can easily remove it. My SDK also looks different for the .NET SDK. This also is not important for the build.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net472;net48</TargetFrameworks>
    <OutputPath>bin\$(Configuration)\</OutputPath>
    <UseWindowsForms>true</UseWindowsForms>
    ...
  </PropertyGroup>

 I've removed other Frameworks from the original post, because I'm only targeting Revit 2020, 2021, and 2022. I also removed the initializing <RevitVersion> as it also was not necessary.

 

Then I want to set the RevitVersion property based upon the .NET Framework version as shown below. This was not noted in the original post.

 

<PropertyGroup Condition=" '$(TargetFramework)' == 'net46' ">
    <RevitVersion>2018</RevitVersion>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(TargetFramework)' == 'net47' ">
    <RevitVersion>2019</RevitVersion>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(TargetFramework)' == 'net472' ">
    <RevitVersion>2020</RevitVersion>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(TargetFramework)' == 'net48' ">
    <RevitVersion>2022</RevitVersion>
  </PropertyGroup>

 

Notice that there is no Revit 2021 listed in this version. This is because the multi-framework versions are going to build normally, but we are going to call MSBUILD separately for the Revit 2021 version after all the builds for the versions have actually been built. 

 

In the next post for the Configurations, mine looks incredibly similar to the above in the original post. But, make sure to keep the net48 versions setup how they look above.

 

Then we get to the RevitAPI dlls. I personally use the Nuget package for Revit dlls. If you have everything locally, then use the above. But, if you don't there is the Nuget package available (not managed by me). So, we need to swap the PackageReference based upon the .NET Framework version and consequently the Revit Version. Here is what that code looks like below:

 

<Choose>
    <When Condition=" '$(RevitVersion)' == '2018'">
      <ItemGroup>
        <PackageReference Include="Autodesk.Revit.SDK" Version="2018.*" PrivateAssets="All">
          <ExcludeAssets>runtime</ExcludeAssets>
        </PackageReference>
      </ItemGroup>
    </When>
    <When Condition=" '$(RevitVersion)' == '2019'">
      <ItemGroup>
        <PackageReference Include="Autodesk.Revit.SDK" Version="2019.*" PrivateAssets="All">
          <ExcludeAssets>runtime</ExcludeAssets>
        </PackageReference>
      </ItemGroup>
    </When>
    <When Condition=" '$(RevitVersion)' == '2020'">
      <ItemGroup>
        <PackageReference Include="Autodesk.Revit.SDK" Version="2020.*" PrivateAssets="All">
          <ExcludeAssets>runtime</ExcludeAssets>
        </PackageReference>
      </ItemGroup>
    </When>
    <When Condition=" '$(RevitVersion)' == '2021'">
      <ItemGroup>
        <PackageReference Include="Autodesk.Revit.SDK" Version="2021.*" PrivateAssets="All">
          <ExcludeAssets>runtime</ExcludeAssets>
        </PackageReference>
      </ItemGroup>
    </When>
    <Otherwise>
      <ItemGroup>
        <PackageReference Include="Autodesk.Revit.SDK" Version="2022.*" PrivateAssets="All">
          <ExcludeAssets>runtime</ExcludeAssets>
        </PackageReference>
      </ItemGroup>
    </Otherwise>
  </Choose>

 

Then finally we get to the actual building of the Revit 2021 version.

 

You just copy and paste this in to your CSPROJ and away it goes, right? The "BuildAdditionalRevitYear" is not necessary similar to how the "Test" target noted in the original post was not necessary. This could have easily been omitted by the OP and for me.

 

The real item to note is the Build2021 target. The OP was sending it to "DispatchInnerBuilds", but I could never get this to fire correctly. This may have been a Visual Studio reference, but I am using Rider as my IDE and could not get it to work. I instead looked for something else to get this target to run correctly. Instead I'm using AfterTargets=Build.

 

This is waiting until the Build event happens and then running the command. Which is great and it runs! The downside as you might imagine is that every build then calls this command. So the 2018 build is called and then it also builds 2021, the 2019 build is called and then it also builds 2021, the 2020 build is called, and so on. So every build also then builds the 2021 build. But this takes a lot of time waiting for a build to run over and over again when it is not necessary. I would really like it to only build the 2021 build if I build the 2022 build because this is the only time that I'm using net48. (You know, with the hopes that future Revit version target .NET instead of Framework). We will see if we have to revisit this in 2023. I have my condition statement written only to run this command when my Revit Version of 2022 is called. Now the build is only called once and everything builds correctly.

 

<Target Name="BuildAdditionalRevitYear">
    <Message Importance="high" Text="-- Building $(MSBuildProjectFile), TF = $(TargetFramework), Config = $(Configuration), Revit Version = $(RevitVersion) --" />
  </Target>
  <Target Name="Build2021" AfterTargets="Build" Condition="$(RevitVersion) == 2022">
    <Message Importance="high" Text="*** running pre-dispatch builds ***" />
    <MSBuild Projects="$(AssemblyName).csproj" Properties="Configuration=$(Configuration);TargetFramework=net48;RevitVersion=2021" />
  </Target>

 

You will also notice that I changed to the $(AssemblyName) in the Projects, which just made it easier for me to copy and paste between my different projects CSPROJ files.

 

I did have to copy and paste this in to every project in my solution to get everything working correctly. I need to spend a little time looking at a core project with imports or targets that way everything is not needed to be copy/paste. But it is working for my Solution for the time being. 

 

I hope this was helpful for your build where you are targeting .NET Framework 48 and also working with Revit 2021 and 2022.

Message 11 of 11

Found a fix.  I was indeed just a weird glitch.  I created a new project, put all my files in it, copy and pasted the csproj code from the old project into the new one, fixed a few paths and now it works.

 

James

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Rail Community