How can I change the folder that Exec tasks in a csproj file generates its temporary exec.cmd files in? - msbuild

I have a .NET 6 project that includes some Exec nodes, and those commands are failing because (as in this discussion on the msbuild repo) the paths of the generated tmp<blah>.exec.cmd files are not whitelisted.
The suggested fix in there is
The location of this file is controlled by the environment variable
TEMP. Can you set that to a custom value before invoking MSBuild?
Which I'm sure would work - but I don't know how to do that. According to this question (which is for C++ not C#, but it's the best I can find) you can use EnvironmentVariables="<blah>" in that same node, but the files are still generated in %LOCALAPPDATA% despite my trying to set TEMP to something else. A failing example is below - what am I doing wrong?
<Target Name="ToolRestore" BeforeTargets="PreBuildEvent">
<Exec Command="dotnet tool restore" StandardOutputImportance="high" EnvironmentVariables="TEMP=C:\MSBuildTemp" />
</Target>
An answer should ideally be valid for building/debugging in Visual Studio and via dotnet build/test/publish. Even better would be a method of making the value of TEMP be variable per-user, but that's not necessary.

According to the code for the Exec task it does use the standard GetTempPath function, which means it really should react to user-level environment variables. However that function is documented like:
This method checks for the existence of environment variables in the
following order and uses the first path found:
The path specified by the TMP environment variable.
The path specified by the TEMP environment variable.
...
so the suggested fix you found is not entirely correct: you might need TMP not TEMP. And indeed on my machine I have TMP set and msbuild uses it for its temporary batch files which can be seen using a target which prints the path of the batch file Exec uses:
<Target Name="TempTest">
<Exec Command="echo %~dp0"/>
</Target>
Running on cmd.exe:
>set TMP=c:\temp
>msbuild temptest.proj /t:TempTest /v:m /nologo
c:\temp\
>set TMP=c:\Users
>msbuild temptest.proj /t:TempTest /v:m /nologo
temptest.proj(7,5): error MSB6003: The specified task executable "
cmd.exe" could not be run. Failed to create a temporary file. Temporary files folder is full or its path is incorrect.
Access to the path 'c:\Users\tmpb31f9faffaab49e9b3bd5479a6823550.exec.cmd' is denied.

Related

Can I specify fileloggerparameters in msbuild project file

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.

Redirecting the output of InstallShield using MSBuild

I'm trying to use the InstallShield MSBuild task to produce an installer on our build machine and put the output in the drop folder. I can produce the installer but it remains located in the source tree.
I tried using OutDir in the task. This worked on my local machine but it changes the actual .ism file; thus, failing on the build machine.
Next, I tried using TaggedOutputs ItemGroup. I'm just not sure how to make it work. I don't see any changes in my output. Here's my script:
<ItemGroup>
<!-- The TaggedOutputs items allow you to explicitly add extra files to output groups. Each item must include both Name and OutputGroup, as well as TargetPath metadata values. -->
<TaggedOutputs Include="P:\">
<Name>AvApp</Name>
<OutputGroup>Primary output</OutputGroup>
<TargetPath>My Test Exe.exe</TargetPath>
</TaggedOutputs>
</ItemGroup>
<!-- Run interactive InstallShield on the developer machine -->
<InstallShield Project="R:\src\Setup\AvSetup\AvSetup.ism"
ProductConfiguration="Product Configuration 1"
ReleaseConfiguration="Release 1"
OutputGroups="$(TaggedOutputs)"
/>
where P is mapped to the target location.
Is my syntax incorrect or is there another tag I can use?
InstallShield version is 2012.
InstallShield's Targets file has this built in but it's not designed correctly and only works based on certain assumptions that may not be true.
The way I like to do it is:
1) Define a Path Variable in the ISM called ISBUILDDIR and give it a defined value of
<ISProjectDataFolder>
2) Under Product Configurations, Release Configuration, set the Build Release location to \ProductName
This essentially gives you an abstraction that by default behaves like before but can be overridden during the build.
3) In your .ISPROJ (MSBuild) create the following item group:
<ItemGroup>
<InstallShieldPathVariableOverrides Include="$(OutDir)">
<PathVariable>ISBUILDDIR</PathVariable>
</InstallShieldPathVariableOverrides>
</ItemGroup>
Now the $(OutDir) property will be assigned to the ISBUILD path variable and the product/release configuration will output to $(OutDir)\ProductName In the case of TFS Builds $(OutDir) gets assigned $(BinariesRoot) so your build output will get picked up and placed in the drop location archive.

Use path system environment variable in MSBuild exec task

I'm calling an external tool to do some post processing in my build sequence using the Exec task in MSBuild. When I update the tool to a new version the name of the directory of the executable changes, but the directory is added to the system PATH variable. Is it possible to use the system PATH variable in MSBuild? I tried the following code but it does not work:
<Exec Command=""$(PATH)\mytool.exe" />
If the directory has been added to the PATH variable, I would expect that the file path would be resolved automatically. I.e. you should be able to use:
<Exec Command="mytool.exe"/>
Maybe first check if the resolution is working ok by opening up a command prompt, cd'ing to some root directory that is different to where the 'mytool.exe' lives, and see if it is resolved when you simply enter "mytool.exe" at the prompt.

How to call msbuild from a batch file from msbuild from team build?

I'm using Team Build (2010) to call an msbuild script with an Exec task that calls a batch file that in turn calls msbuild. Like this:
<Exec Command="BatchFileThatCallsMSBuild.bat" />
Of course the batch file does a bunch of other junk or I'd just use the MSBuild task.
The problem is that when the batch file tries to call msbuild it can't find it.
'msbuild' is not recognized as an internal or external command, operable program or batch file.
How do I get the necessary environment set up in the exec task?
I tried changing the command to:
<Exec Command="%22$(VS100COMNTOOLS)..\..\VC\vcvarsall.bat%22&BatchFileThatCallsMSBuild.bat" />
but no dice, still msbuild is not found.
The answer I came up with was to take advantage of the seldom-demonstrated-online multi-line Command string to the Exec task.
<Exec Command="call "%VS100COMNTOOLS%..\..\VC\vcvarsall.bat" x86
set AnotherEnvVar=$(RandomMSBuildProperty)
call BatchFileThatCallsMSBuild.bat
type file_with_output_from_the_msbuilds_in_the_batchfile.log" />
This let me set up the basic build environment (call to vcvarsall), push an msbuild property out to the Exec's environment where the batched msbuilds could see it, call the batch file, and even pull the hidden msbuild output up to the level of the Exec task for clearer logging in Team Build.
I'm not thrilled with having to embed yet another reference to this specific VS version in my code, but it works for now.

How can I execute programs in my %PATH% with MSBuild?

Note: I'm using Mercurial as an example here, because that's what I'm trying to get to work with MSBuild right now.
But the problem is not limited to Mercurial, it happens with every external program that is somewhere in my %PATH% variable (I tried the same with PowerShell, for example).
So I didn't put the Mercurial tag on this question on purpose, because this is not about Mercurial!
What I actually want to do:
I want my build script to get the current revision number from my Mercurial repository and store it in a file.
The simplest way to do this from the command line is:
hg id -i >rev.txt
Mercurial is installed on my machine and the installation folder is in my %PATH% variable.
So I can run this line from anywhere on my machine (directly from the command line, or from a batch file), and it just works.
The problem occurs when I try to run this line from my build script.
I change the BeforeBuild (or AfterBuild) section of my .csproj file as follows:
<Target Name="AfterBuild">
<Exec Command="hg id -i >rev.txt"/>
</Target>
When I compile my solution with Visual Studio, it works and the rev.txt file is created in the folder where my .csproj is.
But when I compile the exact same solution from the command line with MSBuild, the build fails with the following error message:
The command "hg id -i >rev.txt" exited with code 9009.
I googled "msbuild code 9009" and found some solutions, but all of them propose to provide the full path to the executable.
When I do this, the build succeeds with MSBuild as well.
But this is not an acceptable solution for me, because I can't be sure that everyone using my project (including the build server) has installed Mercurial in the exact same folder.
That's exactly what %PATH% is for...
The same happens when I put the <Exec Command="... line directly into the build script.
If I specify the path to the executable, it works.
If I don't specify the path, it doesn't.
Is there any trick to make MSBuild execute programs in my %PATH% variable without specifying the complete folder?
EDIT:
#leppie:
Output redirection:
You mean the fact that I save the output of my command in a text file inside the command , instead of just running hg id -i as a command and using an output parameter or something like that to get the output?
Doesn't make any difference...the error is the same when I omit >rev.txt.
Command line args:
No, it throws the same error, even if I shorten the command to just hg (without any parameters).
Don't forget: if I run the exact same Exec command in the exact same .csproj file from Visual Studio, or if I just provide the path to the .exe file in the command, everything works.
So IMO output redirection and command line args can't be the problem.
Have you tried this extension pack for mercurial/msbuild?
http://msbuildhg.codeplex.com/documentation
Seems to have a task for returning revision id, which is what your trying to achieve no?
<HgVersion LocalPath="$(MSBuildProjectDirectory)" Timeout="5000">
<Output TaskParameter="Revision" PropertyName="AssemblyRevision" />
</HgVersion>
Okay, I found the solution.
I have to admit, it was a classic case of PEBKAC :-)
I'll explain it anyway, maybe it will help someone who made the same mistake:
Basically everything I have tried (plus what James Woolfenden suggested in his answer) would have been worked...if only the batch file that I use to run the build script wouldn't have looked like this:
path="%windir%\Microsoft.net\Framework\v4.0.30319"
msbuild build.proj
Yes, exactly.
I'm editing the %PATH% variable for the duration of this batch file, and I'm overwriting it with the path to MSBuild instead of just appending it.
So when my build script tries to call Mercurial, it can't find it anymore because its location is not in the %PATH% variable anymore.
No idea why I didn't see this before.
The correct way would be to append the MSBuild path, leaving the other paths intact:
path=%path%;%windir%\Microsoft.net\Framework\v4.0.30319