I've just implemented a new build script file to share code signing details among projects and allowing us to easily switch depending on if our main certificate is available or not. This is in our main application project as well as two WiX projects (msi and exe).
It seems like WiX remembers settings from the last build - for example, if I build with cert.sign in the correct place, then remove it and build again, it recalls the settings loaded from cert.sign during the first build rather than using test.sign as expected. This is not an issue in regular .csproj files.
The .sign file I've set up (one per certificate):
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Certificate>test.pfx</Certificate>
<FilePath>$(SolutionDir)</FilePath>
<Pwd>cert_pwd</Pwd>
<TimeStamp>/tr http://timestamp.server.com</TimeStamp>
<AppName>My App</AppName>
<Url>www.example.com</Url>
</PropertyGroup>
</Project>
The main .csproj file (this works):
...
<Import Project="$(SolutionDir)..\..\.cert\cert.sign" Condition="Exists('$(SolutionDir)..\..\.cert\cert.sign')"/>
<Import Project="$(SolutionDir)\test.sign" Condition="!Exists('$(SolutionDir)..\..\.cert\cert.sign')"/>
<PropertyGroup>
<PostBuildEvent>signtool sign /f $(FilePath)$(Certificate) /p $(Pwd) $(TimeStamp) /d $(AppName) /du $(Url) $(TargetPath)</PostBuildEvent>
</PropertyGroup>
...
One of the .wixproj files (this doesn't work):
...
<Import Project="$(SolutionDir)..\..\.cert\cert.sign" Condition="Exists('$(SolutionDir)..\..\.cert\cert.sign')"/>
<Import Project="$(SolutionDir)\test.sign" Condition="!Exists('$(SolutionDir)..\..\.cert\cert.sign')"/>
<Target Name="SignMsi">
<Exec Command="signtool sign /f $(FilePath)$(Certificate) /p $(Pwd) $(TimeStamp) /d $(AppName) /du $(Url) #(SignMsi)" />
</Target>
...
Any ideas on how to fix this? Could it be an issue in the SignMsi and SignExe targets? I'm on Visual Studio 2015 u2, WiX v3.10.2.2516
Related
I have a solution that contains a console application with a .csproj file like the this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project>
I also have a library project that uses the console application to generate a heap of C# code that get compiled into the library, the library .csproj file looks like this.
<Project Sdk="Microsoft.NET.Sdk" InitialTargets="RunGenerator">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../generator/generator.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<Target Name="RunGenerator">
<Exec Command="dotnet run -p "../generator/generator.csproj" input output" />
</Target>
</Project>
This fails because the dependency analysis says that a netstandard1.4 assembly cannot reference a netcoreapp1.1 assembly. That is correct except that I am not referencing the assembly.
I can work around that issue by building the generator project like this:
<Project Sdk="Microsoft.NET.Sdk" InitialTargets="RunGenerator">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
</PropertyGroup>
<Target Name="RunGenerator">
<Exec Command="dotnet build "../generator/generator.csproj"" />
<Exec Command="dotnet run -p "../generator/generator.csproj" input output" />
</Target>
</Project>
The problem is that the generator project no longer takes part in the dependency analysis when these projects are built using the containing solution file and the explicit build of the generator project sometimes runs concurrently with another build of the same project initiated by the solution build and this results in errors because files are locked etc.
Is it possible to have a project dependency without checking the target framework?
Can anyone suggest a workaround?
Thanks.
Here are some MSBuild tips. You might need to combine a few of these ideas.
You can use your solution file to add an explicit project dependency. See https://learn.microsoft.com/en-us/visualstudio/ide/how-to-create-and-remove-project-dependencies (This question was originally asked here: Visual Studio 2010: How to enforce build order of projects in a solution?). Unfortunately, this is really hard to do if you don't have VS. The format is .sln files is kinda a nightmare.
To avoid the concurrent build issue, use the MSBuild task instead of the Exec task. See https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-task
<Target Name="CompileAnotherProject">
<MSBuild Projects="../generator/generator.csproj" Targets="Build" />
</Target>
dotnet-run invokes "dotnet build" automatically. This is actually problematic in concurrent builds. You can instead add a target to your generator.csproj that runs the app after it has been built. "dotnet filepath.dll" runs the compiled app without building it.
<Target Name="RunCodeGen" AfterTargets="Build">
<Exec Command="dotnet $(AssemblyName).dll input output"
WorkingDirectory="$(OutDir)" />
</Target>
How do I compile with mc.exe in the correct way. Currently I have a build step which runs the relevant command but looking at developer network
There seems to be a better way.
I am not a expert with msbuild so please excuse how easy this question is. Googling has revealed no help
<Project
DefaultTargets="Build"
ToolsVersion="14.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Message Source Files">
<Extensions>mc;</Extensions>
<UniqueIdentifier>{B796B525-44D3-4260-8C76-705DBADA1043}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<MessageCompile Include="a.mc">
<GenerateBaselineResource>true</GenerateBaselineResource>
</MessageCompile>
</ItemGroup>
<Target Name="Build">
<DontKnowWhatGoesHere Sources="#(MessageCompile)"/>
</Target>
</Project>
MSBuild build are usually extended via .targets files, that have to be included in the project, and they extend the existing build proces. The WDK tasks for MSBuild page confirms this:
These command-line tools need to be exposed to MSBuild as tasks (contained in targets) so that they can be run during the build process.
The WDK MSDN page also has a help page on Windows driver targets:
The WindowsDriver.Common.targets, WindowsDriver.masm.targets, and WindowsDriver.arm.targets files provide the targets that are necessary to build a driver.
A quick grep in my C:\Program Files (x86)\Windows Kits\10\build directory showed that the MessageCompile target (the step that actually processes the MessageCompile items) is defined in the build\WindowsDriver.Common.targets file.
After importing the targets in your project you can do one of the following:
<Import
Project="C:\Program Files (x86)\Windows Kits\10\build\build\WindowsDriver.Common.targets" />
<!-- Option A: -->
<Target Name="Build" DependsOnTargets="MessageCompile">
<!-- no need to do anything, the dependency target should do the work -->
</Target>
<!-- Option B: -->
<Target Name="Build" DependsOnTargets="MessageCompile">
<!-- Use the Mc task which is the actual wrapper around the .exe,
see the .common.targets file for the list of all parameters -->
<Mc
Sources ="#(MessageCompile)"
ToolExe ="$(MessageCompileToolExe)"
ToolPath ="$(MessageCompileToolPath)"
Generated
/>
</Target>
I am a beginner with MSBuild. So far I have been able to create a custom task called 'MakeTextFile' which creates a text file in C:\ based on the contents property you pass it. This works running from a command line prompt.
I have also included this in my .targets file (under the project tag):
<ItemGroup>
<AvailableItemName Include="CreateTextFileAction" />
</ItemGroup>
When I use the Import tag on my client applications .csproj I can now set items build actions to 'CreateTextFileAction', however the action never triggers (as no text file on C:\ is created)
How do I get all the file paths of items that were marked with my build action 'CreateTextFileAction' and pass them onto my custom task?
For reference, my .targets file:
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<AvailableItemName Include="CreateTextFileAction" />
</ItemGroup>
<UsingTask AssemblyFile="CustomMSBuildTask.dll" TaskName="CustomMSBuildTask.MakeTextFile" />
<Target Name="MyTarget">
<MakeTextFile Contents="TODO HOW DO I GRAB MARKED FILES?" />
</Target>
</Project>
A csproj file has a defined set of targets. The three main entry points are Build, Rebuild and Clean. These targets each have a set of dependencies. If you write your own targets to be part of the standard csproj build you need to find a suitable injection point within these dependencies.
For ease of use there are two standard targets for you to override called BeforeBuild and AfterBuild. If you define this in the csproj file (after the import of the csharp targets file) and call your custom task in there then it should work (or at least move further along).
<Target Name="BeforeBuild">
<MakeTextFile Contents="TODO HOW DO I GRAB MARKED FILES?" />
</Target>
I am executing MSBuild from a batch file. The MSBuild script is in a different directory than the directory I want MSBuild to consider the working directory when running the script. When invoking MSBuild.exe, how do I change its working directory?
Edit: More details
Let's say I have an MSBuild script located on some other server. I want to run a command thusly:
msbuild.exe \\my_server\c$\My\Path\To\Scripts\TestScript.msbuild
I run that command with my command prompt at c:\temp. Let's say my TestScript.msbuild has a task to create a file. The file has no path just a filename. I would expect that the file gets created inside c:\temp. But it doesn't it gets created next to the msbuild file that is sitting on the server. This is the behavior I want to change.
Edit #2
Here is the script I'm using in my test:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Files Include="HelloWorld.txt" />
</ItemGroup>
<Target Name="TouchFiles">
<Touch Files="#(Files)" AlwaysCreate="True" />
</Target>
</Project>
I am going into a command shell CDing into c:\temp and then executing the script. With or without the /p:OutDir switch that #Nick Nieslanik mentions, the HelloWorld.txt file appears in the folder where the *.msbuild file is and not c:\temp.
I ran across this while looking for a solution to my problem. Here's my solution (build script):
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Default">
<Exec Command="build.bat" WorkingDirectory="..\[your dir]\" />
</Target>
</Project>
I believe that's more what you were originally looking for?
My problem was that my batch file called another that it expected to be in the same directory, but since my ms build script was being run elsewhere, the batch file failed to find the second batch file.
#jkohlhepp - I see now. You are doing the opposite of what I described in my comment to some degree.
MSBuild common targets use the MSBuildProjectDirectory to determine the output folder unless you override that. So in your case, you could run
msbuild.exe \\my_server\c$\My\Pat\To\Scripts\TestScript.msbuild /p:OutDir=c:\temp
to force the output to be dropped in that location.
EDIT:
Given the project file above, you'd need to edit it to do something like the following for this to work:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutDir Condition=" '$(OutDir)' == '' ">bin\debug\</OutDir>
</PropertyGroup>
<ItemGroup>
<!-- Without prefacing files with paths, they are assumed relative to the proj file -->
<FilesToCreate Include="$(OutDir)HelloWorld.txt" />
</ItemGroup>
<Target Name="TouchFiles">
<Touch Files="#(FilesToCreate)" AlwaysCreate="True" />
</Target>
</Project>
In current versions of MSBuild the well-known property MSBuildStartupDirectory can be used in the msbuild file to retrieve the absolute path of the folder where MSBuild is called.
https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-reserved-and-well-known-properties?view=vs-2019
This option perhaps did not exist in msbuild around the time when the question was asked. I didn't want to spend too much time investigating it.
I'm trying to make a batch file to publish the few ClickOnce application we have in one click. I'm using msbuild for that, and as an example the below command line shows how I'm doing it:
msbuild
MyApp.sln
/t:Publish
/p:Configuration=Release
/p:PublishUrl="C:\Apps\"
/v:normal > Log.txt
(wrapped for easier reading)
when I run the above command it builds and publish the application in the release directory, i.e. bin\release! Any idea why msbuild doesn't respect PublishUrl property in my example above?
PS: I tried also different combinations including remove 'Configuration', use 'Rebuild' and 'PublishOnly' as targets, and remove the the quotation marks but without any success.
You are setting the wrong property. Try PublishDir instead.
You can pass it into MSBuild as you are or you can set it in the project file (or maybe the sln file too, not sure I always use the project file.) like this
<PropertyGroup>
<PublishDir>C:\Dev\Release\$(BuildEnvironment)\</PublishDir>
</PropertyGroup>
I've just done a few blog posts on MsBuild and ClickOnce stuff, check it out you 'should' find them useful...
Some features are done by Visual-Studio and not by the MSBuild-script. So the click-once-deployment behaves differently when it's executed from the command-line.
The ApplicationRevision isn't increased with every build. This works only when is exectued from Visual Studio
In in somecases, the PublishUrl isn't used. Quote from MSDN:
For example, you could set the PublishURL to an FTP path and set the InstallURL to a Web URL. In this case, the PublishURL is only used in the IDE to transfer the files, but not used in the command-line builds. Finally, you can use UpdateUrl if you want to publish a ClickOnce application that updates itself from a separate location from which it is installed.
I've created a special MSBuild-file which does this things. It runs the publish-target and copies then the files to the right location.
An example of the build-file, as requested by alhambraeidos. It basically runs the regular VisualStudio-build and then copies the click-once data to the real release folder. Note that removed some project-specific stuff, so it's maybe broken. Furthermore it doesn't increase the build-number. Thats done by our Continues-Build-Server:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Publish" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- the folder of the project to build -->
<ProjLocation>..\YourProjectFolder</ProjLocation>
<ProjLocationReleaseDir>$(ProjLocation)\bin\Release</ProjLocationReleaseDir>
<ProjPublishLocation>$(ProjLocationReleaseDir)\app.publish</ProjPublishLocation>
<!-- This is the web-folder, which provides the artefacts for click-once. After this
build the project is actually deployed on the server -->
<DeploymentFolder>D:\server\releases\</DeploymentFolder>
</PropertyGroup>
<Target Name="Publish" DependsOnTargets="Clean">
<Message Text="Publish-Build started for build no $(ApplicationRevision)" />
<MSBuild Projects="$(ProjLocation)/YourProject.csproj" Properties="Configuration=Release" Targets="Publish"/>
<ItemGroup>
<SchoolPlannerSetupFiles Include="$(ProjPublishLocation)\*.*"/>
<SchoolPlannerUpdateFiles Include="$(ProjPublishLocation)\Application Files\**\*.*"/>
</ItemGroup>
<Copy
SourceFiles="#(SchoolPlannerSetupFiles)"
DestinationFolder="$(DeploymentFolder)\"
/>
<Copy
SourceFiles="#(SchoolPlannerUpdateFiles)"
DestinationFolder="$(DeploymentFolder)\Application Files\%(RecursiveDir)"
/>
<CallTarget Targets="RestoreLog"/>
</Target>
<Target Name="Clean">
<Message Text="Clean project:" />
<MSBuild Projects="$(ProjLocation)/YourProject.csproj" Properties="Configuration=Release" Targets="Clean"/>
</Target>
</Project>
I'll put in my 2 cents, this syntax seems to work (right or wrong):
/p:publishUrl="C:\\_\\Projects\\Samples\\artifacts\\Web\\"
For me, the soultion was to escape the path.
Instead of:
/p:PublishUrl="C:\Apps\"
Put:
/p:PublishUrl="C:\\Apps\\"