.net command-line compiler multiple projects - c#

I need to build a batch file that runs on a directory and digs inside for suitable c# projects to compile. (and compile them of course).
I don't know the name of the projects inside the directory.
I can assume all project are in c# and written in VS2008 and above (if that helps).

setlocal
SET CMD= msbuild.exe
for /R %%d in (*.csproj) do %CMD% %%d
endlocal
This will build each project into individual assemblies, with all default build properties for each project. You can specify additional properties, or an MSBuild config file to use.

Related

How to invoke a target of a msbuild project without recursively calling the same target on sub projects

I created bunch of targets in msbuild project. Lets call this project TopLevelProject. Lets say this has a target called CollectNZip. TopLevelProject depends on SubProjectA, SubProjectB and SubProjectC.
I have a solutions targets file Directory.Solution.targets that contains all the projects below its folder including TopLevelProject.
As part of a target in this file say BuildAll, I like to invoke CollectNZip target of TopLevelProject. So I added TopLevelProject:CollectNZip as dependency.
When I invoke BuildAll, I do see TopLevelProject is invoked with target CollectNZip. But this sucker as part of dependency started invoking SubProjectA:CollectNZip, SubProjectB:CollectNZip etc. As those sub projects don't have CollectNZip, the buildall target is failing.
What is the trick to invoke a target of a project, but don't invoke the sub projects as part of the invocation?
If I understand the scenario:
There is a solution file with a set of projects. Let's say the solution is named 'MySolution.sln'.
I assume the solution file was created by either Visual Studio or the dotnet tool.
The set of projects in the solution include: 'TopLevelProject.csproj', 'SubProjectA.csproj', 'SubProjectB.csproj', and 'SubProjectC.csproj'.
I assume the project files were created as C# projects by either Visual Studio or the dotnet tool.
The project 'TopLevelProject' has ProjectReferences to 'SubProjectA', 'SubProjectB', and 'SubProjectC'.
The project 'TopLevelProject' also has a target named 'CollectNZip'.
There is a 'Directory.Solution.targets' file that is a peer of 'MySolution.sln' or in a parent directory.
'Directory.Solution.targets' contains a 'BuildAll` target.
The 'Directory.Solution.targets' file is ignored by Visual Studio so the 'BuildAll' target is only available when running from the command line.
Projects can be added to a solution file (SLN) but can't be added to an MSBuild file. The 'Directory.Solution.targets' file is an MSBuild file. It can't be a container for projects. I don't know what the following statement means:
I have a solutions targets file Directory.Solution.targets that contains all the projects below its folder including TopLevelProject.
Note that the Import element is a textual include. It doesn't "add" a project; it adds the content of the file in the Project attribute into the content of the current project.
From the command line, you can invoke the 'CollectNZip' target of project 'TopLevelProject' via the solution file.
e.g.
msbuild MySolution.sln /t:TopLevelProject:CollectNZip
This will invoke only the 'CollectNZip' target on only the TopLevelProject project. It will not run other projects from the solution.
I don't know what the following statement means:
As part of a target in this file say BuildAll, I like to invoke CollectNZip target of TopLevelProject. So I added TopLevelProject:CollectNZip as dependency.
The <ProjectName>:<TargetName> syntax is supported for the command line /target switch. It is not supported within the code of an MSBuild file. TopLevelProject:CollectNZip can't be a dependency of a target.
MSBuild doesn't have any notion of "sub projects" although there are two mechanisms which can add dependencies between projects.
A project dependency can be added to the solution file. The solution level project dependency effects the build order -- and does nothing else. It does not share files.
A ProjectReference can be added to a project file. The ProjectReference is an ItemGroup and is part of the C# project build system that is built on the general MSBuild build engine. ProjectReference is specific to certain targets of the C# build system, most importantly the build and clean targets. build and clean will evaluate the ProjectReference ItemGroup, will run the referenced projects, and on a build will copy in the product of the referenced project.
If I add a target named 'Fred' to all the projects and I invoke 'Fred' on one project via the solution, 'Fred' will not be called on projects in the ProjectReference ItemGroup.
A project is an encapsulation. It doesn't know its 'caller' and, excepting ProjectReference, it doesn't know about other projects.
The described behavior is not how MSBuild works and I'm guessing that the description is imprecise and/or there is pertinent code not shown.
If 'CollectNZip' should only run within the 'TopLevelProject' project, then only add the target to that project. If you want to be able to build with and without 'CollectNZip', define a property that can be used as a flag, e.g. add an 'EnableCollectNZip' property and add a Condition on the target that tests the value of the 'EnableCollectNZip' property.

DllImport from c# project to c++ project in same solution

I have a solution with two projects:
C# console application (.NET Core 3.1)
and C++ Dynamic Library (.dll)
I need to call the C++ DLL from the C# project, using DllImport. When I provide the full path of the DLL, the application finds it. But I want to replace the path with a relative path, and I can't figure out how to do it.
First of all, make the C++ project a dependency of the C# project. This ensures that the C++ project will be built before the C# project if it's outdated. You can set the project dependencies in the solution settings.
Now that we ensured that the dll is always up to date, we have to somehow get it in the same directoy as the C# executable. We have two options:
a post build command to copy the dll to the output directory of the C# project, or
we set the output directory of both projects to the same directory.
Post build event
We can simply use a copy command. Go to C++ project settings > Build Events > Post-Build Event and copy the following command to to the Command Line field:
xcopy /y "$(OutDir)*.dll" "$(SolutionDir)MY_CSHARP_PROJECT_NAME\bin\$(Platform)\$(Configuration)"
Replace MY_CSHARP_PROJECT_NAME with the name of your C# project. I'm using the default paths here, depending on your solution you might have to tweak the paths a bit.
Shared build directory
I wouldn't recommend this one, because you can run into trouble with it.
Go to the Build tab in the project settings of your C# project.
At the top of the page select Debug as configuration.
At the bottom of the page change Output path to match the C++ output directory for Debug builds (this one is usually in the same folder as the solution file).
Repeat 2 and 3 but this time with Release instead of Debug.

C#: how to find .DLL and .CS dependecies automatically?

We in our company have Visual Studio project with dozens of *.cs files.
We need to compile each .cs file as separate *.dll.
It's not acceptable to move files to different projects.
I need to programatically collect all actually used dependencies for each *.cs file to build it via csc.exe or msbuild, it doesn't mater.
For example: File1.cs depends on File5.cs, File6.cs, File11.cs (all in the same project), also depends on Library1.dll, Library3.dll, Library7.dll
With that information i could call csc.exe /r:Library1.dll /r:Library3.dll /r:Library7.dll File1.cs File5.cs, File6.cs, File11.cs
Any suggestions?

Solution fails to build when using OutDir with MsBuild

We have several .Net 4.0 solutions that all depend on assemblies built by one specific solution, our Server.sln (I guess Shared.sln would have been a better name for it). Our build process first builds the Server.sln to a Binaries directory using the MsBuild OutDir parameter, and then subsequent solutions to subdirectories (e.g. Binaries\Client, Binaries\Web, etc.), again using the OutDir parameter to specify the path to the appropriate subdirectory. This allows us to easily publish our separate applications to different places. This works fine for all of our solutions, except a new API.sln that we created.
Like the other solutions, API.sln references some DLLs built by the Server.sln, but the actual compile error I get is that one project (we'll call it Project1) cannot find a reference to Project2. Project1 and Project2 are both projects in API.sln, and Project1 references Project2 via a project references; not a dll file reference. Also, Project2 does not have any external dependencies either; the only references it has are to System, System.Core, and System.Runtime.Serialization.
Everything works if I set the OutDir to just be the Binaries directory, but I don't want to do that as then all of the API.sln assemblies get mixed with ALL of the Server.sln assemblies, which makes deploying just the API.sln assemblies harder. When I set the OutDir to be Binaries\API then I get the build error saying that:
The name '[Project2 Class Name]' does not exist in the current context [Path to Project1.csproj]
I checked though and the Project1.dll and Project2.dll assemblies are both getting created to the Binaries\API directory, so I'm not sure why this error is getting generated. Our TFS build server has this problem, but I can also recreate it on my local machine by calling MsBuild on the .slns with the same parameters. Any ideas?
Here are the two msbuild commands that I'm using to actually do the builds:
Build the Server.sln:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe /nologo "C:\PlatformAPIBuild\BuildProcessTests\Sources\RQ4.Server.sln" /nr:False /fl /flp:"logfile=C:\PlatformAPIBuild\BuildProcessTests\Sources\RQ4.Server.log;encoding=Unicode;verbosity=normal" /p:SkipInvalidConfigurations=true /p:ReferencePath=C:\PlatformAPIBuild\BuildProcessTests\Binaries /p:OutDir="C:\PlatformAPIBuild\BuildProcessTests\Binaries\\" /p:Configuration="Release" /p:Platform="Any CPU" /p:VCBuildOverride="C:\PlatformAPIBuild\BuildProcessTests\Sources\RQ4.Server.sln.Any CPU.Release.vsprops"
Then build the API.sln:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe /nologo "C:\PlatformAPIBuild\BuildProcessTests\Sources\IQ.Platform.PublicAPI.sln" /nr:False /fl /flp:"logfile=C:\PlatformAPIBuild\BuildProcessTests\Sources\IQ.Platform.PublicAPI.log;encoding=Unicode;verbosity=normal" /p:SkipInvalidConfigurations=true /p:ReferencePath=C:\PlatformAPIBuild\BuildProcessTests\Binaries /p:OutDir="C:\PlatformAPIBuild\BuildProcessTests\Binaries\Platform.PublicAPI\\" /p:Configuration="Release" /p:Platform="Any CPU" /p:VCBuildOverride="C:\PlatformAPIBuild\BuildProcessTests\Sources\IQ.Platform.PublicAPI.sln.Any CPU.Release.vsprops"
And here is the log file from the API.sln build. You'll notice that it has errors like,
Warning as Error: Reference to type 'IQ.Platform.Framework.WebApi.Model.Hypermedia.AccessControl' claims it is defined in 'c:\PlatformAPIBuild\BuildProcessTests\Binaries\Platform.PublicAPI\IQ.Platform.Framework.WebApi.Model.dll', but it could not be found
but the file it says isn't there, is actually there when I look for it after the build fails, and you can see that one of the first things it does at the top of the log file is copy that assembly to that path, so I'm not sure why it can't find it. The IQ.Platform.Framework.WebApi.Model is the Project2 in my discussion above.
Here is the log file from the API.sln build with all of the Test projects removed, so that only one error remains, which will hopefully make looking through the log file a little easier. And here is the same log, but with diagnotic verbosity.

Cleanest Method for copying Native DLLs in a .NET Project

I have a C# GUI application that references a Managed C++ project, which requires 7 native C++ DLLs. I'm looking for the cleanest method for copying these 7 DLLs to the final project output.
What works
Add all DLLs to the C# applications, specifying:
Build Action == "Content"
Copy To Output Directory == Copy Always"
This will make the base folder of the project a mess of DLLs in some cases, all of which are requirements of referenced projects, and not that project itself.
What does not work
Adding these DLLs to a folder named "Required DLLs" with the above settings. It copies it to a folder with the same name in the output, causing them to be in an incorrect location. I can't see a way to specify the output directory.
Embedded Resources: In C# P/Invoke, you can add DLLs you're referencing as embedded resources, and the DLLs are embedded inside your final library. I don't see this possibility in Managed C++, and I'm not even sure if it works with reference chains.
Adding the DLLs as content within the Managed C++ project. The files do not get copied to the output directory.
What is the best solution in this case? I'd prefer the Managed C++ project to be able to handle it's own DLL requirements if possible, and preferably in a way that won't prevent the project from being used across multiple applications.
As far as having a clean project goes, is it better to insert all my code files within subfolders in the project, and have the DLLs at the root to make the first solution work?
Solution:
Using the post-build suggestion from Joseph, the following command does the trick to use a "Required DLLs" folder.
xcopy "$(ProjectDir)Required DLLs*.*" "$(TargetDir)" /Q /Y
/Q hides the individual files from the output, and /Y suppresses overwrite prompts.
You can use a post-build event to copy the contents of a directory (e.g., your "Required DLLs" directory) into the project's output directory.
You can work with static libs instead of dynamic, it will make your dlls bigger but single dll instead of multiple is just time saver and not only in your aspect.
Route all the projects in the solution to a single directory (managed and unmanaged).

Categories