====== EDIT October 2, 2015 ====I'm having another crack at this at
https://github.com/CADbloke/CodeClonerThis time around I'm focusing on propagating source code to destination Projects which control all their own build settings. Apparently some (all) of us though that 72 builds in one project was a little unmanageable </YesItWas>.
There's an extensive readme there so I won't duplicate it here. I'd love to hear any thoughts, suggestions, WTFs.
My earlier works left here to serve as a warning for others..and also because there are a couple of useful tips in there.
...
My methodology (as of writing - this can change without notice) for supporting different versions involves using one project with different builds. One of my project actually has 36 builds, all using the same code. Well, it seemed like a good idea at the time.
A Visual Studio project file is just an XML file, so don't be shy about editing it by hand. Here is a the
MSDN landing page for more info
I need to massage my Visual Studio project template a fair bit for public consumption,
add a wizard to fix a few hard-coded fragilities and also write up an explanation for what it does and why. Here's a teaser in the meantime, just a few excerpts for the 2007 version. I also have builds for 2013, 2012 (both build on .NET 4.0) & 2010 (v3.5) ...
<!-- Version 2007 Builds -->
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug Acad2007|AnyCPU' ">
<DefineConstants>TRACE;DEBUG;ACAD2007</DefineConstants>
<AssemblyName>$projectname$Acad2007</AssemblyName>
<OutputPath>bin\Debug\ACAD2007\</OutputPath>
<DocumentationFile>bin\Debug\ACAD2007\$projectname$Acad2007.XML</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Acad2007|AnyCPU' ">
<DefineConstants>TRACE;RELEASE;ACAD2007</DefineConstants>
<AssemblyName>$projectname$Acad2007</AssemblyName>
<OutputPath>bin\Release\ACAD2007\</OutputPath>
</PropertyGroup>
......
<!-- 2007 version. -->
<PropertyGroup Condition=" $(DefineConstants.Contains('2007')) ">
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
</PropertyGroup>
<ItemGroup Condition=" $(DefineConstants.Contains('2007')) " >
<Reference Include="AcDbMgd">
<HintPath>$CadLibraryRootFolder$\2007\AcDbMgd.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="AcMgd">
<HintPath>$CadLibraryRootFolder$\2007\AcMgd.dll</HintPath>
<Private>False</Private>
</Reference>
<!-- BCL references depend on the .NET Framework you are using -->
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Data.Linq" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
<Reference Include="WindowsFormsIntegration" />
</ItemGroup>
<!-- TODO: You will probably need to restore some wildcard entris here as per the comments -->
<!-- If you need to restore wildcard links to other source files then see comments below ...
<CustomParameter Name="$CadExtensionClassesCommonFolder$" Value="Z:\z.Libraries\AutoCAD\ExtensionClasses\Common"/>
<CustomParameter Name="$CadExtensionClassesOldVersionsFolder$" Value="z:\z.Libraries\AutoCAD\ExtensionClasses\Extensions for Old Versions"/>
-->
<ItemGroup>
<Compile Include="$CadExtensionClassesCommonFolder$\*.*" >
<Link>Extensions\common\%(RecursiveDir)%(Filename)%(Extension)</Link>
<!-- <Compile Include="$CadExtensionClassesCommonFolder$\*.*" >
<Link>Extensions\common\%(RecursiveDir)%(Filename)%(Extension)</Link>-->
</Compile>
<Compile Condition=" $(DefineConstants.Contains('2012')) Or $(DefineConstants.Contains('2010')) Or $(DefineConstants.Contains('2007')) "
Include="$CadExtensionClassesOldVersionsFolder$\*.*" >
<Link>Extensions\For Old Versions\%(RecursiveDir)%(Filename)%(Extension)</Link>
<!-- <Compile Condition=" $(DefineConstants.Contains('2012')) Or $(DefineConstants.Contains('2010')) Or $(DefineConstants.Contains('2007')) "
Include="$CadExtensionClassesOldVersionsFolder$\*.*" >
<Link>Extensions\For Old Versions\%(RecursiveDir)%(Filename)%(Extension)</Link>
-->
</Compile>
<Compile Include="AppInitializeTerminate.cs" />
<Compile Include="Command.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<None Include="ReadMe.txt" />
<None Include="NetLoadScripts\*.*" />
</ItemGroup>
and I drop this into my .csproj.user file ...
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug Acad2013|AnyCPU' ">
<StartAction>Program</StartAction>
<StartProgram>C:\Program Files\Autodesk\AutoCAD 2013\acad.exe</StartProgram>
<StartArguments>/b C:\Codez\CADbloke\CADtools\$projectname$\$projectname$\NetLoadScripts\AutoNetLoadDebug2013.scr"</StartArguments>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Acad2013|AnyCPU' ">
<StartAction>Program</StartAction>
<StartProgram>C:\Program Files\Autodesk\AutoCAD 2013\acad.exe</StartProgram>
<StartArguments>/b C:\Codez\CADbloke\CADtools\$projectname$\$projectname$\NetLoadScripts\AutoNetLoadRelease2013.scr"</StartArguments>
</PropertyGroup>
</Project>
The whole .csproj file is >410 lines. There is also a .vstemplate file that defines all the parameters. There's also a batch file that builds the project template and installs it in the Visual Studio folder, plus a template for the Command.cs file.
The XML comments are there because the Visual Studio project generator tends to strip out wildcards and just make a list of what is there at the time. I prefer the wildcards so I don't have to fix every project in existence if I add, remove or rename a file in the linked folders.
Z: is a mapped drive because the VS project maker changes the links to relative ones. That's a bit fragile for my liking - if you move the project all the links break. I haven't tried it with an environment variable yet :: Edit,
yes I have and it works ::: which would a easier to manage than mapping a duplicate drive.
ReSharper will complain when you switch between pre-2013 & 2013-onwards builds because of the extra DLL (AcCoreMgd.dll). Also, the observant among you probably want to tell me that v2007 is x86-only so an "Any CPU" build will generate build warnings. Yeah, I know. It's on the list.
There's more to it than just this but I hope it gives you an idea of how this alternative works.
Edit: I have since broken the monolithic csproj file into several smaller ones, based on what is unchanged between projects. I then import them into the specific project I am working on. The bonus is that Visual Studio doesn't seem to alter imported files so I don't lose any of the wildcard links.
The order you import them is important, if there are any cascading dependencies then they need to be declared before you can use them.
============================== EDIT March 3rd, 2015 =================I don't do it this way any more, well not as confusingly. Perhaps. These days I have 2 CSPROJ files I import for each version of AutoCAD (NanoCAD, BricsCAD etc. too), the first file I import defines the build configuration name, sets the compilation constants (eg. ACAD2015) and sets the output path and sets the Debug settings. The second file I import sets the project references based on the Compilation constants. The main project file has no output paths or AutoCAD References. Here is an example...
Main Project file (the first part of it)<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<!-- Constants are important because the Reference imports are dependent on them -->
<PropertyGroup>
<DefineConstants>$(DefineConstants);TEST;CoreConsole</DefineConstants>
</PropertyGroup>
<!-- Import order is important here - Need to define the build constants before Setting conditional references -->
<Import Project="$(Codez)\z.Libraries\AutoCAD\VisualStudioCSPROJ\AutoCAD2015-20.0-DebugRelease.csproj" />
<Import Project="$(Codez)\z.Libraries\AutoCAD\VisualStudioCSPROJ\AutoCAD2015-20.0-References.csproj" />
<Import Project="$(Codez)\z.Libraries\AutoCAD\VisualStudioCSPROJ\AutoCAD2014-19.1-DebugRelease.csproj" />
<Import Project="$(Codez)\z.Libraries\AutoCAD\VisualStudioCSPROJ\AutoCAD2014-19.1-References.csproj" />
<Import Project="$(Codez)\z.Libraries\AutoCAD\VisualStudioCSPROJ\AutoCAD2013-19.0-DebugRelease.csproj" />
<Import Project="$(Codez)\z.Libraries\AutoCAD\VisualStudioCSPROJ\AutoCAD2013-19.0-References.csproj" />
...
<Reference Include="WindowsBase" Condition="!$(DefineConstants.Contains('NET35'))" />
...
AutoCAD2015-20.0-DebugRelease.csproj<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- IMPORTANT: Project reference CSPROJ must be imported after this because this defines the conditional constants -->
<!-- Debug & Release versions for 2015 -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug Acad2015|AnyCPU'">
<DefineConstants>$(DefineConstants);TRACE;DEBUG;ACAD;AutoCAD;ACAD2015;AutoCAD2015;NET45</DefineConstants>
<OutputPath>bin\Debug\ACAD2015\</OutputPath>
<DocumentationFile>$(OutputPath)\$(MSBuildProjectName).xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release Acad2015|AnyCPU'">
<DefineConstants>$(DefineConstants);RELEASE;ACAD;AutoCAD;ACAD2015;AutoCAD2015;NET45</DefineConstants>
<OutputPath>bin\Release\ACAD2015\</OutputPath>
</PropertyGroup>
<!-- These scripts are generated by the VS Project template or copy from the AutoCAD Libs folder.
If the $SolutionName$ or $projectname$ changes then you will need to Grep them to update the DLL names -->
<PropertyGroup Condition="$(DefineConstants.Contains('ACAD2015')) ">
<StartAction>Program</StartAction>
<StartProgram>C:\Program Files\Autodesk\AutoCAD 2015\acad.exe</StartProgram>
</PropertyGroup>
<!-- AnyCPU -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug Acad2015|AnyCPU'">
<StartArguments>/b "$(MSBuildProjectDirectory)\NetLoadScripts\AutoNetLoadDebug2015.scr"</StartArguments>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release Acad2015|AnyCPU'">
<StartArguments>/b "$(MSBuildProjectDirectory)\NetLoadScripts\AutoNetLoadRelease2015.scr"</StartArguments>
</PropertyGroup>
</Project>
AutoCAD2015-20.0-References.csproj<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Project References for AutoCAD 2015 Visual Studio Projects -->
<!-- IMPORTANT: Build config CSPROJ must be imported before this because it defines the conditional constants -->
<PropertyGroup Condition=" $(DefineConstants.Contains('ACAD2015')) ">
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<!-- NO tests -->
<ItemGroup Condition=" $(DefineConstants.Contains('ACAD2015')) And (!$(ProjectName.EndsWith('Tests')) Or $(ProjectName.EndsWith('CADTests')) Or !$(DefineConstants.Contains('CADTest'))) ">
<Reference Include="AcMgd" Condition="!$(DefineConstants.Contains('CoreConsole')) And !$(DefineConstants.Contains('CORECONSOLE'))">
<HintPath>$(Codez)\z.Libraries\AutoCAD\2015\AcMgd.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="AcCoreMgd">
<HintPath>$(Codez)\z.Libraries\AutoCAD\2015\AcCoreMgd.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="AcDbMgd">
<HintPath>$(Codez)\z.Libraries\AutoCAD\2015\AcDbMgd.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="AdUiPalettes" Condition="!$(DefineConstants.Contains('CoreConsole')) And !$(DefineConstants.Contains('CORECONSOLE'))">
<HintPath>$(Codez)\z.Libraries\AutoCAD\2015\inc-x64\AdUiPalettes.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- WITH tests -->
<ItemGroup Condition=" $(DefineConstants.Contains('ACAD2015')) And $(ProjectName.EndsWith('Tests')) And (!$(ProjectName.EndsWith('CADTests')) Or !$(DefineConstants.Contains('CADTest'))) ">
<!-- WITH tests -->
<Reference Include="AcMgd" Condition="!$(DefineConstants.Contains('CoreConsole')) And !$(DefineConstants.Contains('CORECONSOLE'))">
<HintPath>$(Codez)\z.Libraries\AutoCAD\2015\AcMgd.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="AcCoreMgd">
<HintPath>$(Codez)\z.Libraries\AutoCAD\2015\AcCoreMgd.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="AcDbMgd">
<HintPath>$(Codez)\z.Libraries\AutoCAD\2015\AcDbMgd.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="AdUiPalettes" Condition="!$(DefineConstants.Contains('CoreConsole')) And !$(DefineConstants.Contains('CORECONSOLE'))">
<HintPath>$(Codez)\z.Libraries\AutoCAD\2015\inc-x64\AdUiPalettes.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
</Project>
A Visual Studio project file is just an XML file, so don't be shy about editing it by hand. Here is a the
MSDN landing page for more info. Yes, I already said this at the top but that's actually the "easy" way to do this. If you are using Notepad++ then try this
Syntax Highlighter.