MS Build Import Question Path - c#

Quick question about MSBuild. I have the following MSBuild file in directory d:\MyDirectory
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<Import Project="d:\MyDirectory\BuildTest\BuildTest\BuildTest.csproj"></Import>
</Project>
When I run this the build fails because BuildTest.csproj can't find the .cs source files and it seems to be looking in d:\MyDirectory. I was expecting the working directory to be set to that of the BuildTest.csproj and thus be able to resolve the references. What am I doing wrong?

I think ,the import element is used to import other MSBuild projects into this one (see here).
If you want to specify C# projects to build, you should do it like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<ItemGroup>
<ProjectReferences Include="d:\MyDirectory\BuildTest\BuildTest\BuildTest.csproj" />
<!--more-->
</ItemGroup>
Then if you want to build all referenced projects, you can for example do this:
<Target Name="BuildAllProjects">
<MSBuild ContinueOnError="False" Projects="#(ProjectReferences)"
Targets="Clean;ReBuild">
</MSBuild>
</Target>
Remember though, that this is just 1 of many ways of doing things with MSBuild. There are a lot of good articles on MSDN on this topic, should you require more customization.

Related

Nuget package.targets not able to find files in copy task

Background: I have a library that's meant to be used with a C# game engine. That game engine has an editor that picks up plugins via a Plugins/ directory of the project (is not included in the build output). In order to make life easy for users I want to package my library and the associated editor plugins packaged and deployed together in a single nuget file. This way users don't have to manually manage the versions of two different sets of files.
Problem:
Nuget no longer has the ability to copy content files over by default. Based on searching the only way to accomplish this is with tasks set up in an msbuild package.targets file. So I have the following in my csproj
<ItemGroup>
<Content Include="../Parme.Frb.Example/Plugins/**/*">
<Pack>true</Pack>
<PackagePath>content\Plugins</PackagePath>
</Content>
<Content Include="Parme.Frb.targets">
<Pack>true</Pack>
<PackagePath>build</PackagePath>
</Content>
</ItemGroup>
The Parme.Frb.Example/Plugins folder contains the plugin files I want to include in the nuget file. My Parme.Frb.targets msbuild file contains:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<GluePluginFiles>$(MSBuildThisFileDirectory)..\content\Plugins\**\*</GluePluginFiles>
</PropertyGroup>
<Target Name="CopyGluePlugin" BeforeTargets="PreBuildEvent">
<Copy SourceFiles="$(GluePluginFiles)"
DestinationFolder="$(ProjectDir)Plugins\%(GluePluginFiles.RecursiveDir)"
SkipUnchangedFiles="true" />
</Target>
</Project>
The intention is that it will copy all files from the <nuget-cache>/<package>/content/Plugins directory and recursively copy it tho the project's Plugins/ directory.
When I look at the built nuget file I see all the included content files
So I load this nuget file into a blank project and run a build, which produces the following errors:
Build started 4/12/2021 4:56:42 PM.
Logging verbosity is set to: Normal. 1>Project "C:\Users\me\RiderProjects\NugetTest\NugetTest\NugetTest.csproj" on node 1 (build target(s)).
1>C:\Users\me\.nuget\packages\parme.frb\0.8.3-test10\build\Parme.Frb.targets(7,9): error MSB3030: Could not copy the file "C:\Users\me\.nuget\packages\parme.frb\0.8.3-test10\build\..\content\Plugins\**\*" because it was not found.
1>Done Building Project "C:\Users\me\RiderProjects\NugetTest\NugetTest\NugetTest.csproj" (build target(s)) -- FAILED.
Build FAILED.
"C:\Users\me\RiderProjects\NugetTest\NugetTest\NugetTest.csproj" (build target) (1) ->
(CopyGluePlugin target) ->
C:\Users\me\.nuget\packages\parme.frb\0.8.3-test10\build\Parme.Frb.targets(7,9): error MSB3030: Could not copy the file "C:\Users\me\.nuget\packages\parme.frb\0.8.3-test10\build\..\content\Plugins\**\*" because it was not found.
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.33
However, files most definitely do exist in this directory:
PS C:\Users\me> ls C:\Users\me\.nuget\packages\parme.frb\0.8.3-test10\build\..\content\Plugins
Directory: C:\Users\me\.nuget\packages\parme.frb\0.8.3-test10\content\Plugins
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 4/12/2021 4:56 PM Parme.Frb.GluePlugin
I've tried reorganizing the nuget package to remove the .., I've tried removing the GluePlugins. part of the RecursiveDir (which most tasks don't seem to have but that causes a different error`, etc.. I've tried a ton of stuff and can't get this to work.
Does anyone have any idea on how I can get this copying, without manually specifying each file?
I don't know specifically why this works, but after a lot of trial and error I got it working via:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<GluePluginFile Include="$(MSBuildThisFileDirectory)..\content\Plugins\**\*" />
</ItemGroup>
<Target Name="CopyGluePlugin" BeforeTargets="PreBuildEvent">
<Message Importance="normal" Text="Copying Glue plugin files" />
<Copy SourceFiles="#(GluePluginFile)"
DestinationFolder="$(ProjectDir)\Plugins\%(RecursiveDir)"
SkipUnchangedFiles="true" />
</Target>
</Project>

Specify AdditionalReferencePaths from an external .target file

I have a bunch of project that requires tweaks to be build in a continuous environement.
I put every tweaks in a separate .target file to reuse this file across all projects.
At the very end of my csproj files, I put (before the closing) Project element:
This is working quite well unless I try to include additional reference path.
If I specify using command line the path (msbuild myproject.csproj /p:ReferencePath="C:\path\to\dlls"). The project compile.
My target file is :
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- some tweaks here -->
<PropertyGroup Condition="'$(CompileFor)' == 'SP2013'">
<SomeProperty>some value</SomeProperty>
<AdditionalReferencePaths>C:\path\to\dlls</AdditionalReferencePaths>
</PropertyGroup>
</Project>
But this does not works (dll cannot be resolved).
I also tried :
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<AdditionalReferencePaths Include="C:\path\to\dlls"/>
</ItemGroup>
</Project>
This is not working, because the ItemGroup element can't be out of a Target element
Lastly, I tried:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="SomeTarget" BeforeTargets="BeforeBuild">
<ItemGroup>
<AdditionalReferencePaths Include="C:\path\to\dlls"/>
</ItemGroup>
</Target>
</Project>
This still isn't working. No error, I can see the target is called in the build log, but the DLLs are still not resolved.
How to fix it?
To give a bit of context, tweaks I include in the target file allows me to compile the project against different version of DLLs. The code is a plugin of a 3rd party application (SharePoint to name it), and I want to compile for several different versions of the product. Using some conditional, I can target either a folder with one version of the product or another folder for other version of the product.
I get rid of this issue after two fixes.
The correct property wasn't AdditionalReferencePath but ReferencePath
I also have to move the Import before the first ItemGroup of my csproj. I guess this was required to have to properties set before the Reference element

Why doesn't Exec Command work in a .csproj for netstandard2.0?

I'm busy moving my code from .Net Framework libraries to .netstandard2.0 libraries. So far it's going pretty well, but now i'm stuck with the in the .csproj file.
The existing project file has this defined
<Target Name="Rebuild">
<Exec Command="echo Now Rebuilding the package" />
</Target>
the actual command executes an exe that generates a bunch of xml classes based on an xsd.
I cannot get this to work in a .netstandard2.0 project?
I've searched everywhere but i cannot find a reason for this not working...
I suspect that in your specific instance, the Rebuild target will be overwritten by the sdk targets that are implicitly imported after your code. If you want to overwrite SDK-provided tasks, you need to change to explicit SDK imports (instead of <Project Sdk="...">):
<Project>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<!-- other project content -->
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
<Target Name="Build">
<!-- overwrite Build target here -->
</Target>
<Target Name="Rebuild">
<!-- overwrite Rebuild target here -->
</Target>
</Project>
The Exec target is supported though the echo command may or may not work depending on the platform you are running it on (since echo may be just a built-in command of the shell but no executable that can be run).
Make sure that:
The command starts with the path to an executable that is found on the PATH or is specified absolute or relative to the csproj file being built.
The target is actually executed. E.g. some programs could use /t:Clean;Build instead of /t:Rebuild.

How to build multiple projects from same solution with .bat script?

i need to build three projects from same solution.
Project Structure
Above image shows the current solution. I want to build only 3 three of them; mainly .Web(ASP.NET), .Server(C# Console), Client.Browser(Winforms).
I want to build them by creating a XML script. We have found this on Stackoverflow :
<?xml version="1.0" encoding="utf-8"?>
<PropertyGroup>
<OutputDir>C:\TESTMSBUILDOUTPUT</OutputDir>
</PropertyGroup>
<ItemGroup>
<ProjectToBuild Include="XXXXbook2.sln" >
<Properties>OutputPath=$(OutputDir);Configuration=MSBuildRelease;Platform=x86</Properties>
</ProjectToBuild>
</ItemGroup>
<Target Name="Build">
<MSBuild Projects="#(AllFiles)"/>
</Target>
This is for building for the whole projects inside solution. But we want to specify only three of them. What is the right format for this? Is there any tools to generate such a script.
Thanks in advance.

Customizations on MSBuild (like version) for a C# solution

I'm thinking that the final result is going to be "it can't be that easily done", but just seems like it should be. I have a personal project I am working on. I'd hate to have to manually (or even in script) change versions, company, copyright, and all that on ALL the assembly.cs files and would like all that to be either in a script or in a file I can change (so the script stays the same mostly) when I want to update the version. But it seems like MSBuild is mostly a "build as is specified in Visual Studio". I'd just hate to have all that history of these files where I change just the version and possibly even make a mistake as this project will continue to get bigger and bigger. I'd like to just be able to add a new project to Visual studio and have whatever command line in my powershell script just say "compile this, but give it this company name and this file version instead of whatever is listed in the code file".
Google has NOT proven fruitful in this. I've even found it difficult to build my files to a specific folder. I've had to so far make sure all my projects are 2 folders deep and was able to say to build them at ....\, but I would like to be able to change that randomly if I like and have them built elsewhere if I so desire.
Is MSBuild perhaps not the way to go? Is there someway else to build visual studio that would be better from command line? Eventually I also want to auto build the install with wix and be able to match its version with the binary versions.
thank you
Since csproj is xml, you can use XmlUpdate "helpers" to modify the values inside the csproj file before you do your build.
For other files, you can use some other Tasks to do the job.
Here is one helpful target:
http://msbuildtasks.tigris.org/ and/or https://github.com/loresoft/msbuildtasks has the ( FileUpdate (and SvnVersion task if that is your Source-Control) ) tasks.
<Target Name="BeforeBuild_VersionTagIt_Target">
<ItemGroup>
<AssemblyInfoFiles Include="$(ProjectDir)\**\*AssemblyInfo.cs" />
</ItemGroup>
<!--
<SvnVersion LocalPath="$(MSBuildProjectDirectory)" ToolPath="$(SVNToolPath)">
<Output TaskParameter="Revision" PropertyName="MyRevision" />
</SvnVersion>
-->
<PropertyGroup>
<MyRevision>9999</MyRevision>
</PropertyGroup>
<FileUpdate Files="#(AssemblyInfoFiles)"
Regex="AssemblyFileVersion\("(\d+)\.(\d+)\.(\d+)\.(\d+)"
ReplacementText="AssemblyFileVersion("$1.$2.$3.$(MyRevision)" />
</Target>
Below is an example of manipulating the csproj(xml).
How to add a linked file to a csproj file with MSBuild. (3.5 Framework)
But basically, when you build, you can put all the repetative stuff in a msbuild definition file (usually with the extension .proj or .msbuild)...and call msbuild.exe MyFile.proj.
Inside the .proj file, you will reference your .sln file.
For example:
$(WorkingCheckout) would be a variable (not defined here)...that has the directory where you got a copy of hte code from your source-control.
<Target Name="BuildIt" >
<MSBuild Projects="$(WorkingCheckout)\MySolution.sln" Targets="Build" Properties="Configuration=$(Configuration)">
<Output TaskParameter="TargetOutputs" ItemName="TargetOutputsItemName"></Output>
</MSBuild>
<Message Text="BuildItUp completed" />
</Target>
So below is the more complete example.
You would save this as "MyBuild.proj" and then call
"msbuild.exe" "MyBuild.proj".
Start .proj code. (Note, I did not import the libraries for the FileUpdate Task)
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="AllTargetsWrapped">
<PropertyGroup>
<!-- Always declare some kind of "base directory" and then work off of that in the majority of cases -->
<WorkingCheckout>.</WorkingCheckout>
</PropertyGroup>
<Target Name="AllTargetsWrapped">
<CallTarget Targets="BeforeBuild_VersionTagIt_Target" />
<CallTarget Targets="BuildItUp" />
</Target>
<Target Name="BuildItUp" >
<MSBuild Projects="$(WorkingCheckout)\MySolution.sln" Targets="Build" Properties="Configuration=$(Configuration)">
<Output TaskParameter="TargetOutputs" ItemName="TargetOutputsItemName"></Output>
</MSBuild>
<Message Text="BuildItUp completed" />
</Target>
<Target Name="BeforeBuild_VersionTagIt_Target">
<ItemGroup>
<AssemblyInfoFiles Include="$(ProjectDir)\**\*AssemblyInfo.cs" />
</ItemGroup>
<!--
<SvnVersion LocalPath="$(MSBuildProjectDirectory)" ToolPath="$(SVNToolPath)">
<Output TaskParameter="Revision" PropertyName="MyRevision" />
</SvnVersion>
-->
<PropertyGroup>
<MyRevision>9999</MyRevision>
</PropertyGroup>
<FileUpdate Files="#(AssemblyInfoFiles)"
Regex="AssemblyFileVersion\("(\d+)\.(\d+)\.(\d+)\.(\d+)"
ReplacementText="AssemblyFileVersion("$1.$2.$3.$(MyRevision)" />
</Target>
</Project>
To enhance the above, you would create a new target that would run before "BeforeBuild_VersionTagIt_Target", that would pull your code from source-control and put it in the $(WorkingCheckout) folder.
The basic steps would then be: 1. Checkout code from Source-Control. 2. Run the targets that alter the AssemblyVersion (and whatever else you want to manipulate) and 3. Build the .sln file.
That's the basics of a .proj file. You can do much more. Usually by using helper libraries that already exists.

Categories