I see that you can use the /fileLogger and /fileloggerparameters command line arguments in msbuild to specify things like the location of the log file. Is there any way to specify this information in the Project or PropertyGroup section of the project file? I have all my other project properties imported via an include file. I really don't want to have to one set of properties in an include file and then another set that is specified on the command line.
As far as I'm aware just VC projects has ability log build into separate files. But you have to build it through devenv :-(
and you don't have control over other logging parameters.
Microsoft.Cpp.Default.props
<ItemDefinitionGroup>
<BuildLog>
<Path>$(IntDir)\$(MSBuildProjectName).log</Path>
</BuildLog>
</ItemDefinitionGroup>
Other ugly way is to execute build of each project via
<Exec Command="msbuild.exe project /fl /flp...." />
I guess you want to avid it.
I'm think it could be possible to create custom distributed file logger to do this but I sill don't have it working properly.
Related
I am hoping to be able to use MSBuild to capture a subtree of files produced during the build of a project using Microsoft.NET.Sdk.Web and include them in deployment. So far, I have found that if I simply create the files inside the project folder before deployment, then it works but only for certain filetypes. DLLs, for instance, are excluded, presumably assumed to be non-content items. I have been poking around how the deployment stuff works, and have found the <ResolvedFileToPublish> element that I can put into <ItemGroup>, but I haven't figured out how it might be possible to employ this with wildcards. Specifically, I have a post-build step that places files into a folder deployment within the project, and I want all files in that subtree to be included in the package that is produced by /p:DeployOnBuild=true. How can I tack my files onto the deployment stage so that they're included in the ZIP even if they don't look like content items?
I have found a solution, in the form of adding a new <Task> set to run immediately after the internal tasks which collect files for publishing. This is not suitable for a long-term solution, since it ties to internal state, but this is a temporary fix and as such I think it's alright.
By adding this to the .csproj:
<Target Name="__CopyDeploymentToPublish" AfterTargets="_CopyResolvedFilesToPublishAlways">
<Exec Command="PowerShell.exe -Version 3.0 -ExecutionPolicy Unrestricted $(SolutionDir)deploy_webapp.ps1 -Source $(SolutionDir)src\IQ.Auth.OAuth2.Web -Target $(PublishDir)" />
</Target>
...my PowerShell script runs right after the standard deployment logic aggregates the files it intends to package up. I can at that point do whatever I want to the files and the way they're left is what'll end up in the ZIP file.
I'm scripting my build process, and I'd like to have a single MSBuild script that clones a repo, then includes properties in a file in that repo to drive the build. The only way I've found to include properties from another file is with the Import task, which can't reside inside a Target, so the file it's importing has to exist when MSBuild is initially invoked. Is there any way to run the Import after a target has run, or another way altogether to get properties out of a file in the middle of a build?
You have to call "msbuild" task to run another msbuild process for same project with specific params (path to .props file for example).
<Target Name="Default">
<MSBuild
Projects="$(MSBuildThisFileFullPath)"
Properties="ParamsPath='./ParamsPath/name.props"
Targets="DoSomethingTarget"/>
</Target>
<Import Project=$(ParamsPath) Condition="Exists('$(ParamsPath)')"/>
<Target Name="DoSomethingTarget">
<DoSomeThingTasks/>
</Target>
But Im sure that "the right tool for right the job". Maybe you should look at solutions such as FAKE, PSake, Cake?
I decided to make two separate calls to my script in the BAT script that invokes it. The first calls my Clone task, which creates the properties file. The second calls the tasks that require those properties. I made sure the <Import> task has a conditional requiring the file to exist. I'm still open to a cleaner approach, but this works.
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.
I have a solution that has multiple projects in it, including a web application. I want MSBuild to execute "WebPublish" target against the web application project and "default target" for all other projects in the solution.
This MSDN article says that I can do it specifying the command line
msbuild SlnFolders.sln /t:NotInSlnfolder:Rebuild;NewFolder\InSolutionFolder:Clean
But I never could make it work - MSBuild return an error, something like "NotInSlnFolder:Rebuild" target does not exist. It does not matter what target to specify, Build, Rebuild or Clean - it does not work in any case.
How can I achieve my goal of specifying project-specific targets for a solution?
The MSDN documentation does not work. Or have I missed something?
NOTE: This workaround is not officially supported by Microsoft, so there is no guarantee that it will work forever.
Short Answer
In folder with the SLN file, create the file before.{YourSolution}.sln.targets, with the following content: (Replace what in curly brackets to whatever you need.)
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="{MyCompany_MyProduct_WebApp:WebPublish}">
<MSBuild
Condition="'%(ProjectReference.Identity)' == '{$(SolutionDir)MyCompany.MyProduct.WebApp\MyCompany.MyProduct.WebApp.csproj}'"
Projects="#(ProjectReference)"
Targets="{WebPublish}"
BuildInParallel="True"
ToolsVersion="4.0"
Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"
SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" />
</Target>
</Project>
After that you can execute the command line:
msbuild {YourSolution}.sln /t:{MyCompany_MyProduct_WebApp:WebPublish}
Long Answer
If you add environment variable MSBUILDEMITSOLUTION, setting its value to 1, MSBuild will not delete temporary files generated for the solution and projects.
This will allow you to find {YourSolution}.sln.metaproj and {YourSolution}.sln.metaproj.tmp files generated in the solution folder, which are just standard MSBuild project files.
For MSBuild 3.5, the generated file is {YourSolution}.sln.cache and is retained regardless of environment variables. Analyzing those files, you will understand low-level details of the process and to see the customization opportunities available.
After executing MSBuild with some project-specific target in the .Metaproj file you will find out that the list of project-specific targets is hardcoded and only standard targets are supported (Build, Rebuild, Clean, Compile, Publish; note: Publish and WebPublish are not the same). MSBuild 3.5 only generates Clean, Rebuild and Publish targets as well as a target with just the project's name that means "Build".
You also can see that NotInSlnfolder:Rebuild is just a name of an autogenerated target. In reality MSBuild does not parse it and does not care about project names and location. Also note that the autogenerated target names specify the project name with solution folders hierarchy if it's in one, e.g. SolFolder\SolSubfolder\ProjectName:Publish.
One more critically important thing you will find: The MSBuild Target Name does not support dots. All dots in project names are replaced with underscores. For example, for a project named MyCompany.MyProduct.Components you will have to specify in the command line:
/t:MyCompany_MyProduct_Components:Rebuild
That's why even standard project-specific target Build didn't work - my project name contained dots.
Analyzing file {YourSolution}.sln.metaproj.tmp, you will find out that at runtime it tries to import targets from file named before.{YourSolution}.sln.targets and after.{YourSolution}.sln.targets, if those files exist. This has a key to the workaround for this MSBuild limitation/bug.
You can open your solution file in text editor and check whether following line is exist or not if not then you can add
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> inside the <Project> tag.
Hope this help you.
Using msbuild in .NET 4.0, I can build web project with the "Package" target, and it does a nice job of putting the package in a zip file. But, when I look at the web.config in there, it's not transformed, it has "$(ReplacableToken_Web_SiteConnection-Web.config Connection String_0)"
I can run the "TransformWebConfig" target and it will do the proper transform, but just in its own silo.
I can also run the "Build" target and pass the "DeployOnBuild=True;DeployTarget=MSDeployPublish" properties and it will deploy the package on my server with the proper web.config transform done.
But, if I want to manually deploy the package to the server, how do I do a "Package" with a "TransformWebConfig" so that the zip file has the final web.config in there?
If you want to skip this from happening then you need to set a property in your build. You can do this in two ways
Edit your project file
Create a .wpp.targets file
I would recommend #2. For this case create a new file in the same directory as your project file with the name {ProjectName}.wpp.targets where {ProjectName} is the name of your project. Then inside of this file you should place the following contents.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
ToolsVersion="4.0">
<PropertyGroup>
<AutoParameterizationWebConfigConnectionStrings>false</AutoParameterizationWebConfigConnectionStrings>
</PropertyGroup>
</Project>
In this case you are setting the property AutoParameterizationWebConfigConnectionStrings which tells the Web Publishing Pipeline to not insert those {} placeholders in the web.config for the connection strings.
The way we do this is by modifiying the project build to do the transform prior to packaging it up.
The Target is call TransformXml and is a part of Microsoft.Web.Publishing.Tasks.dll
In your own targets its
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
But it will be included in a default VS C# build.
So
<TransformXml Source="web.config" Transform="web.release.config" Destination="$(DeployPath)\web.config" />
Does the trick for us.
Set up those paths with the right ItemGroup ("content" most likely) and make sure that target gets fired prior to the call to Package in your .csproj, and the build output will contain a "Web.config" like normal, with the right transformed values.
Alternatively (we've used this for packages that need to be everything to everyone), you can use that trick to do ALL the transforms and include each of them in the final package.
Then you call Msdeploy manually and use its skip and replace directives (forgot the technical term) to only output the right one at deploy-time
Assuming you have a web.usethisone.config in your package, that looks like
-skip:objectname=filepath,absolutepath=web\..*\.config
-replace:objectName=filepath,match=.*web\.usethisone\.config,replace=web.config