One of the best ways to accomplish this is to create different build configurations in your project:
<PropertyGroup Condition=" '$(Framework)' == 'NET20' ">
<DefineConstants>NET20</DefineConstants>
<OutputPath>bin\$(Configuration)\$(Framework)</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Framework)' == 'NET35' ">
<DefineConstants>NET35</DefineConstants>
<OutputPath>bin\$(Configuration)\$(Framework)</OutputPath>
</PropertyGroup>
And in one of your default configurations:
<Framework Condition=" '$(Framework)' == '' ">NET35</Framework>
Which would set the default if it wasn't defined anywhere else. In the above case the OutputPath will give you a separate assembly each time you build each version.
Then create a AfterBuild target to compile your different versions:
<Target Name="AfterBuild">
<MSBuild Condition=" '$(Framework)' != 'NET20'"
Projects="$(MSBuildProjectFile)"
Properties="Framework=NET20"
RunEachTargetSeparately="true" />
</Target>
This example will recompile the entire project with the Framework variable set to NET20 after the first build (compiling both and assuming that the first build was the default NET35 from above). Each compile will have the conditional define values set correctly.
In this manner you can even exclude certain files in the project file if you want w/o having to #ifdef the files:
<Compile Include="SomeNet20SpecificClass.cs" Condition=" '$(Framework)' == 'NET20' " />
or even references
<Reference Include="Some.Assembly" Condition="" '$(Framework)' == 'NET20' " >
<HintPath>..\Lib\$(Framework)\Some.Assembly.dll</HintPath>
</Reference>