Import .targets file from command line in msbuild - command-line

I currently have multiple projects being build using msbuild. I have a small customisation to the build that is handled by a .targets file. One solution is to add the snippet
<Import Project="MyTargets.targets"/>
to each project file. However, ideally I would like to not touch the project files, and be able to pass this information as a parameter to msbuild. That way I could easily control whether I run this customisation from the command line, and I don't have to touch the existing project files.
Is this possible?

You can do that easily with MSBuild 4.0 (check your version by top-level attribute ToolsVersion="4.0"):
There are multiple properties you can use to import your targets before and after Common.targets and or CSharp.targets loaded.
Simplest way is to use 2 sets of self explaining properties.
First set is:
$(CustomBeforeMicrosoftCommonTargets)
$(CustomAfterMicrosoftCommonTargets)
and second one:
$(CustomBeforeMicrosoftCSharpTargets)
$(CustomAfterMicrosoftCSharpTargets)
Property names are pretty self-explained.
Just pass full file name to any of this properties via msbuild.exe
e.g.
msbuild.exe /p:CustomBeforeMicrosoftCSharpTargets=c:\mytargets\custom.targets
You can use other "ImportByWildcard(Before|After)...." properties if you need to import multiple files. But in that case you need to pass more parameters to command-line.

Starting from MSBuild 15.0, the following two files are auto-imported into your build in case they are found on the project path or in any parent folder on the path to the root directory:
Directory.Build.props
Directory.Build.targets
Remark: once the props or targets file is found, MSBuild will stop looking for a parent one.
Also see: https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build

Lets say you have a project file called "Project.msbuild". You would add this conditional import:
<Import Project="$(TargetToImport)" Condition="'$(TargetToImport)' != ''" />
Then pass the name of the target file you want to import as an msbuild property:
msbuild.exe Project.msbuild /p:TargetToImport="TargetFile.Target"

Make sure you use an absolute path to the target file and it works.
Source: Sayed Ibrahim Hashimi - MSBuild how to execute a target after CoreCompile part 2.
msbuild.exe /p:CustomBeforeMicrosoftCSharpTargets="c:\mytargets\custom.targets" /preprocess:out.xml
Use /preprocess[:filepath] to see the result of the imports.
You don't have to modify any csproj or vbproj files.
Of course, it only works where you can set MSBuild Properties.

Related

Override nuspec variable from TeamCity

I've already overridden the version number in the nuspec through the build steps in TeamCity (Nuget Pack), but I don't know how to override any specific variable - is it only possible with the Command Line Parameters or is it possible to expand it in some way so that a new parameter is possible in the build step (as is with the version parameter)?
If only through the command line, do any have suggestions? I guess a path is needed as with the Package Parameter, but other than that I'm lost.
I think that Properties field is what you're looking for:
According to the docs, it can contain:
Semicolon or new-line separated list of package creation properties.
For example, to make a release build, you define here
Configuration=Release.
I've highlighted the term package creation properties as this is the out-of-the-box mechanism to pass parameters to the NuGet package creation process.

How do I get enumerate the PropertyGroup elements in a csproj file from powershell

I'm writing a nuget install script in powershell and I want to access to all the PropertyGroup elements of the project file (*.csproj)?
I have access to a variable called $project which represents an object, I believe it to implement the Project interface from EnvDTE - an abstraction of the project file (*.csproj).
How does I get the PropertyGroup instances from the $project object?
I want the ability from the nuget install powershell script to change the
OutputPath for all PropertyGroup elements that contain this child element.
PropertyGroup is an internal implementation (MSBuild technology) of the .csproj file, and EnvDTE.Project is an interface, so with EnvDTE.Project you can't get or modify directly the MSBuild elements, because in fact before VS 2005 the .csproj was not MSBuild-based and the EnvDTE.Project interface already existed and worked with the previous technology.
But being an interface, you can certainly do your task:
The OutputPath is a property of each project configuration. You can get all the configurations of an EnvDTE.Project as explained in:
HOWTO: Get the projects configurations / platforms from a Visual Studio add-in
(add-ins use EnvDTE)
and once you have an EnvDTE.Configuration, you can access its EnvDTE.Configuration.Properties collection and specifically the "OutputPath". See:
HOWTO: Get the output build folder from a Visual Studio add-in or macro
Once all the project configurations are changed, you can call EnvDTE.Project.Save or EnvDTE.Project.SaveAs passing the EnvDTE.Project.FullName value as parameter.
I found this answer which led me to the solution: Transforming .csproj using nuget package
Here is the script I used in the NuGet package:
$msbuild = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName) | Select-Object -First 1
($msbuild.Xml.PropertyGroups | Select-Object -First 1).AddProperty("TypeScriptCompileBlocked", "true")
$project.Save()

How to Set PublishUrl of ClickOnce Application From CommandLine

I am working on a ClickOnce application. I am trying to publish it from command line using this:
msbuild Project.csproj /t:Publish /p:configuration=release;
The problem is I want to set some other properties along with configuration, like 'PublishUrl' etc.
I've tried this:
msbuild Project.csproj /t:Publish /p:configuration=release;publishurl="\\sdmm\publish\"
It builds successfully but the output of that project will be copied to the debug folder of application in app.publish folder.
How should I handle this thing? Thanks.
You could set any property you want from the command line but before doing so, you need to open your .csproj file in some texteditor(notepad etc). Find the property that you want to edit. In your case it is publish url. Remove this property from csproj file.
Then you could do this
msbuild /target:clean,publish /p:publishurl=c:\publish_location\
you must clean the project before you publish it.
Try to change your target to
msbuild /target:clean,rebuild,publish
because property you are overriding (PublishUrl) was not embedded into application file if only "Publish" target is used.

Nant: can't get current directory from .include

It's not exactly easy for me to summarize this... I have this structure on disk:
[dir] project
[dir] foo
[file] foo.build
[dir] bar
[file] bar.build
[file] default.include
The file default.include contains a couple of properties which are the directories used during the build. E.g:
property name="build.dir" value="${directory::get-current-directory}".
The default.include file is included by foo.build and bar.build using its relative path:
include buildfile="..\default.include"
Now the problem: when I run foo.build from project\foo dir, I get the wrong value for build.dir. I need "project" and I get "project\foo" instead. Is there a way to get the directory in which the .include file exists?
I could do this in a batch file using %~dp0
you can specify the base directory of a project by using this
<property name="build.dir" value="${project::get-base-directory()}">
and make sure you set the build output path to be build.dir
You could set the build path property to be the relative difference between the working folder and the project root before including your shared include stuff. So, in \project\foo\foo.build:
<property name="build.dir" value="..\" />
<include buildfile="..\default.include" />
If you want to then refer to things relative to the project root then just build paths from build.dir. Let's say you had a \project\utilities\ folder where you put some custom build tools or something, then you can refer to that folder as:
<property name="utils.dir" value="${build.dir}utilities\" />
utils.dir would be ..\utilities\ which would be the right value from your working folder (\project\foo\ in this case).
Personally I do things the other way around - I have the main build file at the root of the project and it includes a build file for each sub-project. Physically the build is distributed across the projects, but logically it all runs as if it were one big script in the project root. The downside being that global property names and target names can collide so you need a naming convention to prevent that from happening.
Just got exactly the same problem:
NAnt - how to get the directory path of the included script
In my include file I need to get the directory of the include file, to refer to something else relative to it.
Seems like when NAnt includes another build file, it includes the content of it, without changing anything in its environment (directory::get-current-directory, project::get-buildfile-path, etc).
Another solution in your case might be to use <nant> task instead of <include>. Obviously it works if you don't need anything returned from the included script and just want to run it.

Is there a way to specify more than one app config file in one nunit project file?

I have a collection of unit test that I need to all run from one command line call. One of the assemblies uses a different config file than the rest of the assemblies. Is there a way to specify more than one app config file in one nunit project file?
Not in a project file, but you can specify a config file per assembly (e.g. TestAssembly1.dll.config)
I would say when you script running your tests, you rename the right app.config to replace the other one so that when your tests start the right app.config is loaded. Let me know if you need an example of how to do that.