Telling *.csproj formats apart - c#

I have an application that needs to parse the ProjectReference elements from *.csproj files. It could do this well with the old (.net) format where I used the Name element to get the name of a project:
<ProjectReference Include="..\MyProject\MyProject.csproj">
<Project>{guid..}</Project>
<Name>MyProject</Name>
</ProjectReference>
The new format however (.net-core) makes it crash now because there is no Name element anymore.
I found a few differences between both files but I'm not sure which one I should use tell that I'm working with the new core-file. The differences are:
the new format does not contain xml declaration
the root element starts with the <Project Sdk= attribute and does not contain any default namespace wheres the old one has xmlns=" declared
the ProjectReference element does not contain any children
Which property would be the most reliable way to recognize the core-file like Visual Studio does? Am I on the right track or is there any other criteria I should use to tell the file formats apart?

If you are using MSBuild to evaluate the project file, the sdk sets the UsingMicrosoftNETSdk property to true.
If you are only use XML based tooling to read the file, you can check if a <TargetFramework> or <TargetFrameworks> (plural) property (inside a <PropertyGroup>).
This is the same mechanism that visual studio uses to determine whether the new or classic project system is used for the project (see Opening With CPS document).

Related

understanding the content of a csproj file

I have some older projects in non-SDK style which were created using Visual Studio; the csproj files were almost never modified manually. They were done partly by colleages before I came to this company, so I have very incomplete knowledge about what should or shouldn't be in these files.
Now I am trying to turn them into SDK-style projects by manually modifying the csproj files. In order to do that, I need to understand for each tag what it is for, and if it can be omitted or must be modified.
There might be
non-SDK-style leftovers that can just be deleted,
content we were not aware of (because we don't always know what we are doing when playing around with settings in a project),
content that needs to be modified to fit the new SDK-style specification,
content that can be omitted because it contains just its default value,
etc.
I try getting by by googling a given tag together with "csproj", but more often than not I cannot find useful content. I also noticed that most of the time I DO NOT find a source from Microsoft, mostly blog entries by private guys that sometimes, sometimes not tell me what I want to know.
There are some Microsoft docs like sdk reference or common properties, but they do not cover all tags that I have in my project.
So, how can I understand the content of my csproj file? Is the documentation there and I am merely not finding it? If so, what to search for? Shouldn't Microsoft have a comprehensive list of all possible tags? They have invented the csproj format, so they should know. Or do I need to learn something else first that is a prerequisite of working with csproj files (like before constructing bridges one should know about basic algebra and calculus), that will help me figure out stuff on my own?
cproj files can have a lot of project configurations. Visual Studio Extensions might also modify cproj files and add custom values.
As per the comments on this answer, there isn't a complete list of available properties for Visual Studio projects. The links provided in the question contains most used properties, so that should cater for the average project.
It is very rare that properties will be used that is not contained in those links. If you do run across an unknown property, try searching online, if that fails, ask on SO.
A good search query that hasn't failed me yet, {Project Type} "{Property}" csproj property : .Net "TestProjectType" csproj property.

Update files in visual studio project from build script

Scenario
I am in a situation at the moment where one of the projects I am working on depends upon a 3rd party API, which exposes a swagger descriptor in JSON which we consume using auto rest to generate the C# files for use in the system.
The c# files are generated as part of the build script however there have been instances where the API is updated, and although we do not use any of the new changes it does change some of the c# code which is generated which in turn may depend upon new files which are output from the build script but VS does not know they exist so does not include them.
Issue
So I am trying to find a way to tell visual studio from a build script or some sane way (Without manually changing a *.csproj file) to automatically include everything within a folder in its project, so is there a way to do this?
Other Info
I have deliberately not mentioned build script technologies as it is not really important, so I don't mind if I have to use msbuild tasks or other command line stuff as long as it works I can probably find a way to hook it in.
Also I know some people will think "why do you need to keep re-generating the files?", and we don't really, and currently we try to keep to a specific version, however ignoring that the question still stands, i.e how do you update files VS knows about from outside VS.
You can use wild cards in .csproj file. You only need to do this once
since you didn't give the folder name, I am suggesting a generic solution for you.
To prevent the Visual studio from expanding the wild card once you modify the porject list in Visual studio, you have to add it as property.
In PropertyGroup , add a property with like this
<PropertyGroup>
<IncludeFolder>yourFolder\**</IncludeFolder>
</PropertyGroup>
Then in add the following line to ItemGroup
<ItemGroup>
<Content Include="$(IncludeFolder)" />
</ItemGroup>
This will add everything in that folder to your project.
Also this wild card will not be expanded by the visual studio once you modify them from Visual studio solution explorer. (A weird process from Visual Studio)

MSBuild handling circular dependencies

I am new to MSBuild. Just started trying it two days ago, and now I am just testing it. I have run into a problem where I get this error:
"c:\Users\martinslot\Documents\Visual Studio 2010\Projects\MultifileAssembly\SpecializedBuild.xml" (BuildNumberUtil target) (1) ->
c:\Users\martinslot\Documents\Visual Studio 2010\Projects\MultifileAssembly\SpecializedBuild.xml(4,34): error MSB4006: There is a circular dependency in t
he target dependency graph involving target "BuildNumberUtil".
My MSBuild script look like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="BuildNumberUtil" DependsOnTargets="BuildStringUtil" >
<Message Text="=============Building modules for NumberUtil============="/>
<Csc TargetType="Module" Sources="NumberUtil/DoubleUtil.cs; NumberUtil/IntegerUtil.cs" AddModules="/StringUtil/StringUtil"/>
<Copy SourceFiles="#(NetModules)" DestinationFolder="../Output/Specialized"/>
</Target>
<Target Name="BuildStringUtil" DependsOnTargets="BuildNumberUtil" >
<Message Text="=============Building modules for StringUtil============="/>
<Csc TargetType="Module" Sources="StringUtil/StringUtil.cs;" AddModules="/NumberUtil/IntegerUtil;/NumberUtil/DoubleUtil"/>
<Copy SourceFiles="#(NetModules)" DestinationFolder="/Output/Specialized"/>
</Target>
</Project>
I understand the problem, actually I created this small example to see if MSBuild understood and could somehow correct the problem. How do I solve this?
My problem is that the two targets compile modules that rely on eachother. Does someone here have a solution on how to handle this kind of problem with MSBuild? Maybe I am constructing this in the wrong way?
You simply cannot build projects with circular dependencies. How could you? Which do you build first? There may be some esoteric, convoluted, incorrect way of doing so, but why do it? Circular dependencies usually indicate a design flaw. Fix the design, and you no longer have a circular dependency issue.
It is possible to construct Circular Modules within the scope of MSBuild and Visual Studio; however, doing so has a very limited set of situations where it would be valid to do so.
One key way to do this, if you're planning on using Xaml within your code, is to remove the Sources aspect of the Csc tag and generate your own .response file which actually points to the code you wish to inject. Within the Csc tag attributes you'd specify this file yourself in the ResponseFiles attribute.
Within your .response file, you would then break your application down into its assembly and netmodule components, making sure to include the core assembly's files first at all times. Typically the Csc tag's attributes are directly translated into Csc.exe command line parameters. The parameter names do not always match up. For the sake of resolution it's best to use full, non-relative, paths when referring to files (example, partial, .response below):
"X:\Projects\Code\C#\Solution Name\InternalName\ProjectName - InternalName\SearchContexts\StringSearchType.cs"
"X:\Projects\Code\C#\Solution Name\InternalName\ProjectName - InternalName\UI\Themes\Themes.cs"
/target:module /out:bin\x86\Debug\InternalName.UI.dll
"X:\Projects\Code\C#\Solution Name\InternalName\ProjectName - InternalName\UI\EditDatabaseImageControl.xaml.cs"
"X:\Projects\Code\C#\Solution Name\InternalName\ProjectName - InternalName\obj\x86\Debug\UI\EditDatabaseImageControl.g.cs"
You'll notice that this will end up with merging your multiple sets of Targets into one, and that I've included the xaml generated code myself. This is partly why you remove the Sources aspect, as the Xaml Page generator part of the MSBuild task automatically injects information into the #(Compile) set. Since there's a Debug/Release configuration, in the area where you define the response file to use, I create two versions of the response (since I'm using a T4 template):
ResponseFiles="$(CompilerResponseFile);InternalName.$(Configuration).response"
If you intended to include more than one platform in your code you'd likely need C*P response files where C is the number of configurations (Debug|Release) and P is the number of platforms (x86, x64, AnyCpu). This kind of solution would likely only be a sane method by using a generator.
The short version of this: it is possible to create circular modules so long as you can guarantee that you'll compile it all in one step. To ensure that you maintain the build functionality that is afforded to you with the Xaml build step, your best bet is to start with a normal C# project, and create your own .Targets file from the $(MSBuildToolsPath)\Microsoft.CSharp.targets in the <Import ... tag near the bottom. You'll also likely need a secondary csproj for design purposes since a large portion of intellisense is lost by using this workaround (or use a csproj Condition attribute where the target is selected by some flag you set). You'll also notice certain Xaml editors don't seem to like the binding to netmodule namespaces, so if you bind to types in a netmodule you'll likely have to do them in codebehind (I haven't tested workarounds for this since there's usually ways around static namespace binding)
For some reason within all this, the .baml compiled .xaml files are implicitly understood by the Csc compiler, I haven't been able to figure out where it's deriving this from a command argument, or if it's just implicit by design. If I had to guess they're inferred by the g.cs files associated to what you include in your list of included files.
Observe that this is occurred for web application (either ASP.NET standard web application or ASP.NET MVC application) and fix for this problem is to be removed the below line in ".csproj" file.
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
Package
</BuildDependsOn>
</PropertyGroup>

How do I coordinate assembly version information in my tree of many C# projects?

What approaches have you taken to coordinating changes to version and Copyright for groups of assemblies?
I have a tree of many csprojs each with its own AssemblyInfo file. As part of my build process I want to set copyright year and version. It looks like the following options be be available but file modification is the only one I've seen thus far:
Modify all files via script
Include resource with date (a macro, a singleton class with public static variables)
Override AssemblyVersion on command line ( -DAssemblyVersion="1.0.0.1" ?)
Reference Environment variable ([assembly: AssemblyFileVersion("${ENV.VERSION")])
I'm used to keeping the source tree clean of generated files in Java/C++ via command line params, defines and centralizing all version specification in a single file.
What approach to do you use to keep the versions in sync and manageable?
Thanks
Peter
In the top folder, I place a CommonAssemblyInfo.cs that contains the ... common stuff, such as copyright and FileVersion. This file is just contains some default attributes and is regenerated by a script(a simple .bat file) on the build server, Hudson in my case. Remove these common attributes in each of the projects AssemblyInfo.cs.
In each project in the solution, I add this file. but use "Add as link" - available in the drop down of the "Add" button when you add an existing file. i.e. all projects just contains a reference to the same file, so there's only one file to update.
Regardless of which route you take to actually update the version number, you can use an individual AssemblyInfo.cs file to specify a shared version.
Create the file at some level above the other projects, in terms of structure, then add it to all projects (from within Visual Studio), but when doing so be sure to select 'Add as link'.
I've used an MSBuild task to handle the updating, too, I just can't think of it - which build engine do you use?
You can theoretically have one single assembly file. Just link the file in all other projects: Add existing file >> link file instead of adding (button dropdown)

How to set the output path of several visual C# projects

I have a solution that contains several c# projects and I would like to be able to set the output path and other properties on all the projects together in a single place. Property Sheets (vsprops) do not seem to be able available for C# projects and the $(SolutionDir) variable is ignored. Are there any other methods to set properties across several C# projects?
Update
By Following the information in the answer by Bas Bossink I was able to set the output path of several projects by creating a common csproj and importing it into the individual project. A few other points:
When building in Visual Studio if changes are made to the common project it is necessary to touch/reload any projects that reference it for the changes to be picked up.
Any properties which are also set in a individual project will override the common properties.
Setting $(SolutionDir) as the output path via the Visual Studio UI does not work as expected because the value is treated as a string literal rather than getting expanded. However, Setting $(SolutionDir) directly into the csproj file with a text editor works as expected.
A csproj file is already an msbuild file, this means that csproj files can also use an import element as described here. The import element is
exactly what you require. You could create a Common.proj that contains something like:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5"xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputPath>$(SolutionDir)output</OutputPath>
<WarningLevel>4</WarningLevel>
<UseVSHostingProcess>false</UseVSHostingProcess>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
You can import this Common.proj in each of your csprojs, for instance like so:
<Import Project="..\Common.proj" />
The import statement should precede any tasks that depend on the properties defined in Common.proj
I hope this helps. I can't confirm your problems with the $(SolutionDir) variable I've used it many times. I do know however that this variable does not get set when you run an msbuild command via the commandline on a specific project that is contained in a solution. It will be set when you build your solution in Visual Studio.
Unfortunately, these bits of information such as output path are all stored inside the individual *.csproj files. If you want to batch-update a whole bunch of those, you'll have to revert to some kind of a text-updating tool or create a script to touch each of those files.
For things like this (apply changes to a bunch of text files at once) I personally use WildEdit by Helios Software - works like a charm and it's reasonably priced.
But I'm sure there are tons of free alternatives out there, too.
I would suggest you to use a build tool such as MSBuild or NAnt which would give you more flexibility on your builds. Basically the idea is to kick off a build using (in most cases) a single configurable build file.
I would personally recommend NAnt.
You could find an awesome tutorial on NAnt on JP Boodhoo's blog here
Set the $(OutputPath) property in a common property sheet. Then delete that entry in all the project files you want to it to affect. Then import that property sheet into all your projects.
For hundreds of projects that can be very tedious. Which is why I wrote a tool to help with this:
https://github.com/chris1248/MsbuildRefactor

Categories