I use TFS 2010 and I'm having to change references of assemblies that I build from Debug to Release when I merge into the Main or Release branch.
When working on the Dev branch, I use the Debug assemblies, but I merge down to the Main or Release branch I need to manually change, and it's really time consuming.
Has anyone got best practices, or solutions?
Why dont you refer to them using:
<Reference Include="Assembly">
<HintPath>..\$(Configuration)\Assembly.dll</HintPath>
</Reference>
Then you dont need to change anything.
If the assemblies are identical I suppose the only difference between a *.csproj in Dev & the same *.csproj in Main/Release would be something like:
<Reference Include="Assembly">
<HintPath>..\Debug\Assembly.dll</HintPath>
</Reference>
and then
<Reference Include="Assembly">
<HintPath>..\Release\Assembly.dll</HintPath>
</Reference>
As a first step, you could create a small console application that automates this with find & replace in all the project files involved. You could then run this app each time you do a merge.
The only thing you would need from TFS-SDk is the ability to checkout the *.csproj files.
(Another possible angle: Do you really need the Debug-version of the assembly in the DEV-branch?)
Related
I'm trying to create couple nuget packages for internal company use:
OurCompany.Infrastructure.Logging.Interfaces and just OurCompany.Infrastructure.Logging.
So I created two class library projects in the same solution, provided an implementation for OurCompany.Infrastructure.Logging.Interfaces and created a package.
Now I have a question: how to properly reference the OurCompany.Infrastructure.Logging.Interfaces library in OurCompany.Infrastructure.Logging project?
Should I add it as a nuget package or simply add as a project reference?
There is no perfect solution.
When you use a NuGet package but need to make a change in the library while working on an app that uses it, you need to make the change in the library, publish a new version of the package, update your reference in your app, then hope the change you made in the library worked as needed. Particularly when working on a new feature this is very slow because your software design often changes multiple times between when you first start to when you finish, and debugging is more difficult. If your company doesn't have tooling to check for updates and remind teams that updates are available, then you can get into a fragmentation situation with some teams still using very old versions of the library and if they try to upgrade their app breaks and need extra time to adapt to the breaking changes in the shaed library.
On the other hand, if you use project references, debugging and implementing new features is easier, and breaking changes are detected immeidately, assuming you have adequite automated tests, but it requires all the code to be in the same repository, or to look like it does (maybe using git submodules, or have a convention where multiple repos are always clonsed in the same relative path).
Both Google and Microsoft had to implement their own source control system tools to deal with massive repos that are too big for a single developer machine to work with, plus speed up compile times both of dev boxes and on CI agents, because you don't want changing 1 line of code in one app causing every single app the company has being rebuilt and tested. But unless your company wants to dedicate engineers to working on the build system and not contributing to customer applications, this is infeasible.
So, the submodule, or multiple-repos with relative path conventions sounds appealing, but it still requires the library to be recompiled on every CI build that uses the library. Not a big deal, but if the frequency of changing the shared library is very low, there's little benefit to making code changes to the library easy. Plus when someone else updates the library, how will you know that you need to update the submodule? The NuGet tooling is probably better for checking for updates and actually updating.
Personally, I always use project references references for projects that are already in the same solution and nuget references for everything else. What I've done in the past is when I need to either bug fix or add a new feature to the packaged library, I clone both repos side, temporarily add the library's project(s) to my app's solution and change the nuget references to project references. Then I develop as if they're all in one repo. Finally, I need to manually undo the solution/project reference changes and increment the package reference version number before check in. Since it's manual it sometimes goes wrong. But it's the best balance I've found so far. You need to decide on what's best for yor own situation.
The best solution is: Do Both!
With the Choose directive, you can set conditional referencing:
<Choose>
<When Condition="'$(Configuration)'=='Debug'">
<ItemGroup>
<ProjectReference Include="Path\To\Your.Awesome.Package.csproj" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<PackageReference Include="Your.Awesome.Package" Version="1.0.0" />
</ItemGroup>
</Otherwise>
</Choose>
In this example, your project will use the direct project reference while running in Debug mode, then, the rest of the time it will use the NuGet package reference.
Even though you are still responsible for maintaining the proper NuGet package version, nothing is perfect.
I hope this would help somebody.
For an F# project with dual platforms "Xbox 360" and "x86," I have the following in my project file:
<ItemGroup Condition="'$(Platform)' == 'Xbox 360'">
<Reference Include="Arands.ContentTracker.Xbox">
<HintPath>..\..\..\..\..\..\_Libs\XNA\ContentTracker\ContentTracker\bin\Xbox 360\Release\Arands.ContentTracker.Xbox.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup Condition="'$(Platform)' == 'x86'">
<Reference Include="Arands.ContentTracker">
<HintPath>..\..\..\..\..\..\_Libs\XNA\ContentTracker\ContentTracker\bin\x86\Release\Arands.ContentTracker.dll</HintPath>
</Reference>
</ItemGroup>
For some reason, neither Arands.ContentTracker.dll nor Arands.ContentTracker.Xbox.dll are added as references in my project, regardless of which platform I select (Xbox 360 or x86).
The result is the same with the following:
<Reference Condition="'$(Platform)' == 'Xbox 360'" Include="Arands.ContentTracker.Xbox">
<HintPath>..\..\..\..\..\..\_Libs\XNA\ContentTracker\ContentTracker\bin\Xbox 360\Release\Arands.ContentTracker.Xbox.dll</HintPath>
</Reference>
<Reference Condition="'$(Platform)' == 'x86'" Include="Arands.ContentTracker">
<HintPath>..\..\..\..\..\..\_Libs\XNA\ContentTracker\ContentTracker\bin\x86\Release\Arands.ContentTracker.dll</HintPath>
</Reference>
Is the Condition attribute simply ignored in .fsproj files?
Splitting a project in two sucks. I want the current chosen platform to determine the current configuration and what goes on in a build, not (for example) which project tree I had expanded when I double-clicked a .fs code file in the Solution Explorer. Having multiple projects just to accommodate different platforms has caused me some fairly significant headaches, and even confuses Intellisense (e.g. not identifying System.Collections.Generic.HashSet as unavailable when my platform is set to Xbox 360).
UPDATE: I've managed to get it working. My mistake was expecting the Solution Explorer to reflect a change in configuration, e.g. for the References in the F# project to actually show the relevant references, as I'm used to seeing for C# projects with conditional tags. Apparently, F# projects simply display nothing in the References section when there are conditionals involved (unfortunately).
Once I got past the assumption that the Solution Explorer would accurately reflect the relevant assets after a platform change, I was able to focus on other areas of the solution (namely, the Configuration Manager and References to the F# project in the C# projects) that required more attention.
Thanks for your assistance, all! I'd prefer to award the points to Preet Sangha, since it was his question that provided the insight that allowed me to get past the aforementioned assumption.
This should just be an MSBuild thing, and should work... it may be useful to run msbuild -v:diag ... to diagnose what's going wrong; perhaps things are in the project file in the wrong order?
I'm using Visual Studio 2008 and I'm trying to get a project that I didn't create to build.
In the references folder of this project there are four DLLs with the exclamation icon whose Path property is empty. The in the .csproj file points to an output directory which I don't think is the initial location of these DLLs.
I believe that I have the most recent copies of the DLLs in question (they are used in several related projects). I could place them in this project's directory but I want to determine their original location so I don't have to modify the project and solution files (which are used by other developers in other remote locations).
I would ask the original developers but they're located in a time zone where it's 1 a.m. so I though someone here could help me out.
Thanks in advance.
Open the .csproj file in a text editor an locate the missing references.
If the entry is like this:
<Reference Include="AssemblyName"/>
then it's located in the default folder for that application and somewhere on the search path.
If the entry is like this:
<Reference Include="AssemblyName">
<SpecificVersion>False</SpecificVersion>
<HintPath>Path\AssemblyName.dll</HintPath>
</Reference>
Then the file should be put in the folder referred to by Path.
So, being completely obsessive compulsive, I was digging around in the .csproj file for one of my assemblies and was looking at the schema for the XML. I noticed in the <ItemGroup>, the various .dll files are referenced using an element called <Reference Include="..." />.
Out of curiosity, I did some digging and found that I can change things up a bit by modifying it with an included <Name> element. Like so ...
<Reference Include="Microsoft.CSharp">
<Name>System.Dynamics</Name>
</Reference>
I of course expected this to crash everything, but behold, when I reloaded the project and compiled, everything ran just fine.
Is this just there for aesthetics? Or am I doing damage by changing names around? Are there any long term effects of this? I did not experience any build, runtime, or editor issues from doing this.
I think it's just the display name of the reference.
Edit: The MSBuild schema defines the Reference\Name element as "Friendly display name (optional)."
The schema for MSBuild is located here: C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Build.xsd
Also, check out the MSBuild Reference.
Can I set the default-option of "Copy Local" in Visual Studio to False? In most times, when I add a dll as dependency of a project, I want the Copy Local property set to False. Per default, it is True. Is there a way to change the default behaviour of Visual Studio? (2008)
No - Visual Studio uses an internal set of rules to determine what to set Copy Local to.
From MSDN:
If the reference is another project, called a project-to-project reference, then the value is true.
If the assembly is found in the global assembly cache, the value is false.
As a special case, the value for the mscorlib.dll reference is false.
If the assembly is found in the Framework SDK folder, then the value is false.
Otherwise, the value is true.
Actually, you can. You need a couple things:
Create .targets file that makes copylocal (<Private> tag, to be precise) false by default.
Import the target in .csproj files. You can add it in the very last line, before closing </Project> tag, it'll look like <Import Project="..\Build\yourtarget.targets" />.
Now each project with this target has copylocal disabled by default.
The drawback is that you need to modify each and every csproj file, including new ones. You can work around the new project issue by modifying the VS project template. Instead of Class.cs described in the blog article, you need to modify Class.vstemplate (in the same zip file).
With that approach, there's one more problem - the path itself. If you use hardcoded relative path in newly-generated csproj files, they may be wrong (unless you have flat project structure).
You can:
Make VS generate correct relative path. Not sure how to do that and if that's even possible.
Ignore it and change the path manually for each new csproj (depending on the number of new project you have, while not ideal, that may be tolerable).
Use the environment variable instead of relative path. In that case every developer will need the same variable set.
There must be better solution for that, but haven't found it yet.
Starting with msbuild v 15 you can copy a single file called Directory.Build.props in the root folder that contains your source:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
<Reference>
<Private>False</Private>
</Reference>
<ProjectReference>
<Private>False</Private>
</ProjectReference>
</ItemDefinitionGroup>
</Project>
Nothing more to do! This works well with Visual Studio 2017 and also the vNext Build. You might have to close Visual Studio and than open your solution again to take the file effect.
https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build#directorybuildprops-and-directorybuildtargets
We don't use a .targets files (as suggested in the answer by ya23), so we just edit the .csproj project file manually in a text editor and add the <Private> element to the reference, like this:
<Reference Include="[...]">
<Private>False</Private>
[...]
</Reference>
The value of the <Private> element matches the value of the "Copy local" property. For instance, if <Private> is set to False, then "Copy local" is also false..
Bumping this because it seems there's now a nuget package allowing exactly this...
https://nuget.org/packages/CopyLocalFalse
Haven't tried yet, just hoping it helps.
Regarding the solution posted by #herzbube, if you want to turn off "Copy Local" for all (or most) of the references in your .csproj file, you don't need to set <Private>False</Private> individually on each Reference, you can just put the following directly in the .csproj:
<ItemDefinitionGroup>
<Reference>
<Private>False</Private>
</Reference>
</ItemDefinitionGroup>
This doesn't affect projects referenced with <ProjectReference>, but you can do the same thing--either instead or as well--for those:
<ItemDefinitionGroup>
<ProjectReference>
<Private>False</Private>
</ProjectReference>
</ItemDefinitionGroup>
If you want both of these, you can merge them into a single group:
<ItemDefinitionGroup>
<Reference>
<Private>False</Private>
</Reference>
<ProjectReference>
<Private>False</Private>
</ProjectReference>
</ItemDefinitionGroup>
Make sure you put these overrides prior to the first actual <Reference … > or <ProjectReference … > that you want to affect because these blocks will only apply to those references that appear below them. Then, if there are a few that you do actually want to be locally copied, you can just override those back individually (i.e., within the individual tag itself), this time using True.
For more advanced cases you can switch the overriding value back and forth between True and False multiple times in the same .csproj file. Another advanced technique would be to strategically place some of your references below these blocks, and others above, so the latter won't be affected.
All of this should make the XML in your .csproj much cleaner and easier to read. But there's even more good news, so read on...
As for selecting which projects should be be marked <Private>False</Private> this will usually depend on the specific situation, but there is something fundamental everyone can and should do for starters. It's a step so basic, simple and effective and it delivers such huge MSBuild reliability improvements1. and build-time speedup--and with little downside--that every large solution that uses the default (i.e. local per-project) C# output locations should almost always make this adjustment:
In any and every Visual Studio solution which builds multiple C# class libraries with any non-trivial number of <ProjectReference> inter-dependencies, and which culminates in building one more applications (i.e. executables):
Near the top the .csproj for every class library, insert the <ProjectReference> block shown above.
REASON: There is no need for any .dll to gather any of the libraries it references into a sub-directory of its own, since no executable is ever run from that location. Such rampant copying is useless busywork and may be unnecessarily slowing down your build, possibly quite dramatically.
On the other hand, do not modify the .csproj for any of your solution's applications.
REASON: Executables need to have all the privately-built libraries they need in their respective sub-directories, but the build for each app alone should be responsible for individually gathering each dependency, directly from its respective sub-directory, into the app's sub-directory.
This works perfectly because the .csproj for a class library may reference multiple other class libraries, but the .csproj for an executable usually never references another executable. Thus, for every locally-built library, the only .dll in its bin folder will be itself, whereas every locally-built application will contain the full set of locally-built libraries it references.
Conveniently, nothing changes for the referenced libraries that are not built by your solution, since these usually use <Reference> instead of <ProjectReference>, and we didn't modify the former tag at all. But do note the assumption just mentioned; if it is violated by some of your projects, you may need to make some adjustments.
[1.] Reliability improvements could be related to file collisions that may occur when gathering the same library from multiple disjoint paths in a dependency graph, especially in concurrent builds.