MSBuild output in subfolder for each targetframework when using OutDir - msbuild

When using the new SDK style csproj format, it is possible to use a <targetframeworks> element for a semicolon-separated list of target frameworks to build the project for.
This results in one subfolder per target framework in the build output folder:
However, when passing the OutDir property on the msbuild command line, it does NOT create the subfolders, and the built assemblies are all placed in the same folder.
Command line that works, but doesn't allow output location:
msbuild projectfile.csproj
Command line that selects output directory, but places built assemblies in same folder (effectively overwriting the target framework assemblies that are built first):
msbuild projectfile.csproj /p:OutDir=buildtemp
Is there a way to place the build output in a non-default folder while still retaining the targetframwork subfolders?

The property that is now used is OutputPath, however setting it from the CLI makes it a global property and overrules the automatic appending of the output path. The workaround is to make an intermediate property that is global and consume it from the project.
Add this to your project file inside a PropertyGroup:
<OutputPath>$(BaseOutputPath)</OutputPath>
Then you can set this property globally when calling the build command:
martin.ullrich#martins-imac:~/tmp$ dotnet build /p:BaseOutputPath=bin/foo
tmp -> /Users/martin.ullrich/tmp/bin/foo/netstandard1.4/tmp.dll
tmp -> /Users/martin.ullrich/tmp/bin/foo/netstandard1.6/tmp.dll
The problem here is that the SDK tries to change the OutputPath property based on TargetFramework and AppendTargetFrameworkToOutputPath. But if the value is specified via CLI, the project logic cannot overwrite it.
Also note that BaseOutputPath is actually used in the SDK defaults (defaulted to bin\), but it will try to append the configuration name by default..

Related

MSBuild not placing files where requested

So coming across a very strange issue with MSbuild using TFS 2015 build definition.
The web application I am trying to build has multiple csprojs that are used as class libraries throughout the application and of course my actual web application.
Bellow are the MSBuild arguments I am passing through currently to get a fully completed build.
/p:UseWPP_CopyWebApplication=True
/p:PipelineDependsOnBuild=False /p:OutDir=C:\Agent\_work\1\s\Application\Package\bin\
/p:WebProjectOutputDir=C:\Agent\_work\1\s\Application\Package\WebSite\
/p:NoWarn=0067
/v:Minimal
What I would actually like to do is the following.
/p:UseWPP_CopyWebApplication=True
/p:PipelineDependsOnBuild=False /p:OutDir=C:\Agent\_work\1\s\Application\Package\Website\bin\
/p:WebProjectOutputDir=C:\Agent\_work\1\s\Application\Package\WebSite\
/p:NoWarn=0067
/v:Minimal
The issue I run in to is after the first assemblies are run and placed in the bin folder it then works on the main webproject csproj and throws a unable to locate file for copy message.
I don't understand why this would be the case? Is it because MSBuild is expecting the bin folder to be empty/not there?
Am I missing something in the build order or different Argument needs to be passed. I have also to tried 'outpath' as well.
In the second arguments, the "OutDir" path is the same with "WebProjectOutputDir". This cause the issue since MSBuild will clean the "WebProjectOutputDir" folder before run "_WPPCopyWebApplication" task which copies files to "WebProjectOutputDir" folder. That means the files generated in "OutDir" during the build will be cleaned because it use the same path with "WebProjectOutputDir", so the task will cannot locate the files to copy.
To avoid this issue, you'd either change the "OutDir" different with "WebProjectOutputDir" or add one more arguments:
/p:CleanWebProjectOutputDir=False

AllowedReferenceRelatedFileExtensions not working/recognized in build

I am trying to ensure that the .dll.config file generated in a reference project is pulled into the parent project's /bin directory during a build. I've read here and here about using the AllowedReferenceReleatedFileExtension setting in the parent project's .vbproj file, but I'm getting a warning:
The element 'PropertyGroup' in namespace 'http://schemas.microsoft.com/developer/msbuild/2003' has invalid child element 'AllowedReferenceRelatedFileExtensions' in namespace 'http://schemas.microsoft.com/developer/msbuild/2003'
the property group I added to the end of the .vbproj file looks like this:
<PropertyGroup>
<AllowedReferenceRelatedFileExtensions>
.pdb
.xml
.dll.config
</AllowedReferenceRelatedFileExtensions>
</PropertyGroup>
I'm a little out of my depth with .net builds, so I'm not sure where to start trying to resolve this warning. There are a few other similar warnings in the .vbproj file that don't seem to be causing any problems, but running the build after I added this section didn't actually copy the reference project's .dll.config file over.
Any ideas?
According to this answer (which worked for me), it seems you need to add a semi colon after each extension.
Not sure if that is mandatory
Set the build output verbosity to Detailed and see where the dependencies are being copied from. It is possible that your target dll is referenced indirectly from multiple projects and it is being copied from the project that does not have the AllowedReferenceRelatedFileExtensions set.
In the build output search for:
Dependency "your target dll"
Resolved file path is "path where the target dll is being copied from"
P.S.: "Target dll" means the dll whose .dll.config is required to be copied to the referencing project's output folder.
Did you try to use MSBuild instead of add tags manually?
MSBuild.exe MyProject.csproj /t:build "/p:AllowedReferenceRelatedFileExtensions=.pdb;.xml;.dll.config"

Invoke Custom MSBuild Target on Solution File

I have a solution file (MySolution.sln) with a single project in it (MyProject.vcxproj). I would like to execute a custom target (MyCustomTarget) on my project through the solution. It would look something like this:
msbuild MySolution.sln /t:MyCustomTarget
When I execute the command, I'll get an error message:
MySolution.sln.metaproj : error MSB4057: The target "MyCustomTarget" does not exist in the project. [MySolution.sln]
You can replace MyCustomTarget with any standard targets from Microsoft.Cpp.Win32.targets (e.g.: ClCompile, Link) or any other target of your choice you include from .targets files in MyProject.vcxproj. None of them would work.
When the environment variable msbuildemitsolution is set to 1, I can inspect the generated MySolution.sln.metaproj file. At the bottom 4 targets are specified: Build, Rebuild, Clean, and Publish. Using these targets instead of MyCustomTarget, the project builds ok. Also, if I specify the project file instead of the solution file, it builds too:
msbuild MyProject.vcxproj /t:MyCustomTarget
But using this format, I will lose the OutDir property, manually have to set the Configuration and Platform, so I just lose the benefits of having a solution file.
Is there any way I can use my custom target with the solution file I originally intended?
As far as I understand the problem is that msbuild generates this intermediate project file (mysolution.sln.metproj) but that will won have the imports from MyProject.vcxproj, including the .targets files. No wonder MyCustomTarget is not recognized.
My current workaround is using the project file with msbuild and trying not to miss anything from the solution file:
msbuild MyProject.vcxproj /t:MyCustomTarget /p:Configuration=MyConfig;Platform=MyPlatform;OutDir=MySolution\Platform_MyConfig\
But this is not a proper solution, inflexible, prone to error and does not automatically adapt changes in the solution file.
MSBuild 15 now raises custom targets automatically into the solution metaproj so your initial approach of running the target directly on the solution is now supposed to work.
I think you already answered your question. The answer is NO. There is no target called "MyCustomTarget" inside the .sln.metaproj file, so MSBuild gives you that error message.
Now, to resolve your problem with passing extra parameters on command line. Passing platform and configuration won't be required, if you set defaults in your .vcxproj file. Add this somewhere in your project file, before any of the standard target files are imported:
<Platform Condition="'$(Platform)'==''">MyPlatform</Platform>
<Configuration Condition="'$(Configuration)'==''">MyConfiguration</Configuration>
Configuring OutDir, which is shared across all projects in solution can be done like this. I will assume your solution is structured so that .sln file is in root folder, and all projects are in sub-folders (arbitrarily deep) under the root, or in the same folder as the solution. If this is not the case, you will have to tweak the code a little to adjust to your situation.
Right after you defined Platform and Configuration in your project, add this property group:
<PropertyGroup>
<RootFolder>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory),MySolutionName.sln))</RootFolder>
<OutDir Condition="'$(OutDir)'==''">$(RootFolder)\$(Platform)_$(Configuration)</OutDir>
</PropertyGroup>
The code above follows your convention of setting OutDir to MySolution\MyPlatform_MyConfiguration.
The downside of all this approach is that you have to manually modify all projects in your solution. However it will give you lots of flexibility in the future. For example, any common settings shared across all projects, could be extracted into single .props file that you can <Import> into every project, so changes to configuration could be done in one place.
In order to use the custom target that exists in your project file while building using the solution file, use the following format:
msbuild MySolution.sln /t:MyProject:MyCustomTarget
Note that if the project is in a sub folder (solution folder) you need to add the folder name:
msbuild MySolution.sln /t:src\MyProject:MyCustomTarget
and if the project name contains spaces or dots they are replaced with underscores.

MSBuild: override path to .user file

We have many standard .vcxproj files that we use MSBuild to build. For certain properties and targets we need a .vcxproj.user file to override the properties that are specific to the build in progress.
In order to use the same build process in the development environment as the build machine, we'd like to specify the path to the .vcxproj.user as a different directory to the .vcxproj file, so the build process can generate the file it needs in a separate directory without disturbing the user's own settings.
I've found very little documentation on where msbuild looks for the .user file, or if it is even possible to specify the path?
Path *.user file for cpp projects is specified in C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\Microsoft.Cpp.Default.props
<MSBuildAllProjects Condition="Exists('$(MSBuildProjectFullPath).user')">$(MSBuildAllProjects);$(MSBuildProjectFullPath).user</MSBuildAllProjects>
It is intended to specify custom user settings (for example you can specify app to run in debug) and couldn't be changed.
To change build process you can use CustomBeforeMicrosoftCommonTargets. You can create your own *.targets file which will imports special properties/targets, change build process to make some preprocessing actions and so on. I've wrote an example for AfterTargets here. All you need is to implement custom build logic and change registration to BeforeTargets.

How do I set input path for binaries in WIX project to MSBuild output path on TFS?

I want MSBuild to build WIX 3.5 project containing static files and binaries from another project's output folder. While with static files it all works just fine: I just set Source attribute of File element to "..\AnotherProject\Static\StaticFile.ext", I can't reference binaries, because they aren't in "..\AnotherProject\bin\Release\" folder, they're in MSBuild output folder which I don't know how to reference.
The only way to do so is set some variable in .wixproj file in for Release build configuration and then use it, but it seems wrong. What do I miss?
You want "bind paths". The documentation isn't great about this but you can specify BindInputPaths on the Light MSBuild Task. Any File/#Source or #SourceFile that starts with "SourceDir\" or is a relative path (doesn't start with an "X:\" or "\") will be searched in those bind paths. You can use MSBuild variables to get the BindInputPaths set correctly.
Tried $(var.Web.TargetDir) - working.