Visual Studio 2008 locks custom MSBuild Task assemblies - c#

I'm developing a custom MSBuild task that builds an ORM layer, and using it in a project. I'm being hampered by Visual Studio's behaviour of holding onto MSBuild task DLLs and not letting go.
I'd like to organize my solution like this;
My Solution
|
+- (1) ORM Layer Custom Task Project
| |
| +- BuildOrmLayerTask.cs // here's my task
|
+- (2) Business Logic Project // and here's the project that uses it.
|
+- <UsingTask TaskName="BuildOrmLayerTask" AssemblyFile="$(TaskAssembly)" />
However, when project (2) builds, it locks onto the assembly from project (1). So now I can't build project (1) again without closing the solution and re-opening it.
Is there any way I can organize things so that the custom build task is not kept locked by Visual Studio?

(Edit: Sayed Ibrahim Hashimi, who literally wrote the book on msbuild, suggests the AppDomainIsolatedTask class for a better approach)
I've managed to solve this one myself...
Found this forum post from Dan Moseley, one of the MSBuild developers from Microsoft:
Hi there,
Unfortunately this is because MSBuild loads task assemblies in the
primary appdomain. The CLR does not allow assemblies to unload
from an appdomain as this allows important optimizations on their
part.
The only workarounds I suggest is to call out tomsbuild.exe to
build the projects that use the task. To do this, create
MSBuild.exe <> as an external tool in VS.
Dan
developer on msbuild
DanMoseley - MSFT
So, it seems that to stop the locks, you must spawn out a new MSBuild.exe process. It can't be the one that runs inside Visual Studio, because when MSBuild runs, it loads the tasks into Visual Studio's primary app domain, and that can never be unloaded.
create a new MSBuild project (a .csproj or similar) which overrides the 'Build' Target and performs your custom actions, eg;
<!-- fragment of Prebuild.csproj -->
<Target Name="Build">
<BuildOrmLayerTask Repository="$(Repository)" />
</Target>
Add it to visual studio if you want, but use Configuration Manager to make sure it is notbuilt in any configuration. Just let VS take care of source control and suchlike, not building.
Edit the .csproj file of the project that depends on Prebuild.csproj. Add a BeforeBuild target which invokes MSBuild using the Exec task. This will start a new process, and when that process ends, the file locks are released. Example;
<PropertyGroup>
<PrebuildProject>$(SolutionDir)Prebuild\Prebuild.csproj</PrebuildProject>
</PropertyGroup>
<Target Name="BeforeBuild">
<Exec Command="msbuild.exe "$(PrebuildProject)"" />
</Target>
Now, when you build the dependent project, it executes MSBuild in a new process before running the compile.

Can you edit the project files and include the following property declaration
<PropertyGroup>
<GenerateResourceNeverLockTypeAssemblies>true</GenerateResourceNeverLockTypeAssemblies>
</PropertyGroup>
Let me know if that works for you.

As I mentioned in a comment directed at #Al-Muhandis, it seems possible to create a wrapper around the custom task so that the wrapper gets locked but not the custom task DLL. I have taken an initial shot at doing so with the isolated-task project. It may be buggy, and it only works with VS2008 for now. Pull requests welcome.
The idea for the project was based on the observation that tasks deriving from MarshalByRefObject (using, perhaps, AppDomainIsolatedTask) appear to be loaded into the main application domain for reflection purposes, but a new application domain is created to execute the task. Since loading into the main application domain still seems to lock the DLL, it was useful to create a DLL with a task derived from AppDomainIsolatedTask that loads the custom task DLLs. That way, the wrapper DLL gets locked, but because it executes in its own app domain, the custom task DLLs are unloaded when the wrapper task's executing domain is unloaded. This procedure avoids keeping a lock on the custom task DLLs after the build is complete.

Related

Preventing VS 2019 from rebuilding a solution though no source has changed

I am trying to track down a VS 2019 build issue for a few weeks which drives me crazy. I have a C# project (targeting .Net Framework 4.8) which VS rebuilds regularly even when nothing has changed. The project is not very complex and has no specific dependencies, but a postbuild event which must always be executed. Hence I used the approach described in this answer, which forces msbuild to do the "up-to-date" check instead of the VS IDE. This has worked well for years, but started to make trouble a few weeks ago.
To create a minimal reproducible example:
use the VS project wizard to create a trivial "hello world" console app, .Net Fw 4.8, AnyCpu
add the following lines to the csproj file:
<PropertyGroup>
<RunPostBuildEvent>Always</RunPostBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
</PropertyGroup>
change the "Run Post Build" setting in the IDE's project settings to "On Build Success" (or back to "Always", this does not really matter), save the settings
The Postbuild action is intentionally left empty, one can add arbitrary actions here, but even an empty action will produce the issue.
When I choose to rebuild the solution in the IDE (using Ctrl-Shift-B), without touching anything the source code, the executable is recreated. This effect occurs when the time between two consecutive builds is approx. 10 seconds or more, when I rebuild the solution quicker, the exe file stays untouched.
To make the effect more visible, I set "AssemblyVersion("1.0.0.*"), stripped the "AssemblyInformationalVersion" attribute from AssemblyInfo.cs, so the build system assigns a new build number to the exe file with each new creation, which allows to observe the effect in the Windows Explorer more easily (by activating the "File Version" column in the Explorer view).
Note this effect does not seem to occur when I comment out either the post build event, or the DisableFastUpToDateCheck setting.
I observed this with VS 2019 V16.11.9 and V16.11.10 (currently the latest versions in the "2019" product line).
In my real project, this happens for a central DLL inside a solution where more then 70 other projects depend on, including a large C++/CLI dll, resulting in a build time of ~2 minutes - every time I only want to start the debugger, since this causes a new build! And yes, I also tried to set the "project build output" settings to "Diagnostic", but could not find anything suspicious in the large amount of messages.
PostBuildEvent is problematic and 'old style' since it doesn't define its inputs and outputs. Because of that msbuild can't calculate whether it caused any files to change and thus forces a rebuild.
By replacing the postbuildevent with a custom target and by correctly specifying the inputs and outputs of the target, MSBuild can check whether any of the inpu ts have changed and whether the outputs are up to date to properly decide to skip the build altogether.
See:
https://learn.microsoft.com/en-us/visualstudio/msbuild/incremental-builds?view=vs-2022
PostBuildEvent is also no longer supported in new style SDK projects. Visual Studio will now automatically generate a new target when you setup a postbuild event in the UI.
Starting with jesshouwing's answer, and using this information from Microsoft how to extend the VS build process, I implemented this workaround: in the csproj, I added the following section the end:
<Target Name="CustomAfterBuild" AfterTargets="Build"
Inputs="... input for postbuild step ..."
Outputs=" ... output of postbuild step ..">
<Message Text="... some message here ..." />
<!-- here are the post build actions -->
</Target>
This seems to work well for now without the nasty effects. The only drawback here is that this custom build step does not show up in the Visual Studio project editor, but I can work with that.

Run a c# console application in build process

Suppose we have a solution with three projects:
DownloadDataBase is a console application that downloads data from web and creates a sqlite db file
Test is the main windows from application that uses the downloaded sqlite database.
TestLibrary is a class library that is referenced by the Test project.
I know How I can reorder the build of these three projects using Configuration Manager from build menu. But The main question is : How can I customize build process of the solution so that:
It first builds the DownloadDataBase
Then executes this console application DownloadDataBase
After that copy the created sqlite file from the last step to the resource directory of the Test Project
And Finally builds the Test project using the updated sqlite file?
Therefore every time that I build the new release I will have the latest data needed by my application.
Thanks for any help
You'll need to customize your MSBuild project files (i.e. .csproj files).
For example, in your DownloadDatabase project, add at the bottom of the XML file, inside the <project> element:
<Target AfterTargets="AfterBuild">
<Exec Command="$(ProjectDir)$(OutputPath)$(AssemblyName).$(OutputType)" />
<Copy SourceFiles="$(ProjectDir)$(OutputPath)file.db" DestinationFolder="$(SolutionDir)Test" />
</Target>
Maybe there's some mistake, but at the end of the day, you need to use MSBuild tasks to perform these actions.
See Exec and Copy MSBuild task documentation pages to get further details.

How to embed EDMX in a Code First assembly?

We're using Code First in EF6.1 - our model is over 300+ tables now and startup time is ridiculous. We've tried pregenerating views already but it didn't help much, it's the model compilation in the Code First pipeline that takes most of the time.
We're going to try using the Database / Model First approach for initializing the context by using an entity connection string with metadata links to CSDL, SSDL and MDL files, instead of a direct SQL connection. This would be our ideal process:
After the project containing the Code First model is compiled, a post-build task runs that generates an EDMX file from our DbContext, splits it into component CSDL, SSDL and MDL files, and embeds these files in the assembly as resources
When we create a context via our own factory, we wrap the original SQL connection string in an EntityConnectionStringBuilder, with the Metadata property pointing to the embedded resources, and use the builders connection string to initialize the DbContext
Initial testing shows an ~80% improvement in startup time - the tricky part here is doing the post-build resource embedding in step 1!
Can anyone provide any clues as to how step 1 could be done in MSBuild? Is there an alternative strategy that would work as well? Basically we want a zero-maintenance solution so that developers don't have to manually do anything other than build their code, with no special deployment considerations either.
EDIT:
We ended up using a new, separate class library project that references the project containing the Code First model. This project contains a T4 template that writes the EDMX from the DbContext into memory, then saves the component parts into project files that are already marked as embedded resources, so we get source control as well.
The build order guarantees that the resources will always be up to date, and the entity connection string references this resources assembly at runtime. The MSBuild integration was done by using the T4 MSBuild integration targets so that the template always runs during a build of the project.
You should certainly be able to do this with MSBuild. You will have to pick up a little bit of build script, but shouldn't be too bad.
How are you doing it now? Do you have a console application that you run to generate the edmx? It sounds like you have already done the hard part -- integrating with MSBuild should be easy. I will assume that you do, and go from there.
btw: One thing to know up front is that you .csproj files are MSBuild scripts, so any custom MSBuild scripting can go into those csproj files.
In order of increasing complexity, you could:
Add an "After build" event to your project that executes your console app. This option does not require any MSBuild script -- you just set up an after build event in the project options. It will always run, though. (I don't think you can make a post-build event dependent on configuration), so it could slow down your compile times.
You could use the Exec task in MSBuild to execute your console application. This will require a little editing of your csproj file, but you can make it conditional if you need to. Here's a link to the Exec task: http://msdn.microsoft.com/en-us/library/x8zx72cd.aspx If you put it in a target named "AfterBuild", it will automatically execute after your build has completed.
You could write your own build task -- this is a c# class that will be loaded and executed during build. This is the most sophisticated way to do it, but it also gives you the most control: http://blogs.msdn.com/b/msbuild/archive/2006/01/21/515834.aspx
One of the nice things about the last option (custom build tasks), is that you can write error messages back into the build process. That should help with getting helpful information if the task fails, and if you use a build server, those messages should be picked up by the server in the same manner as any other build message.

MSBuild copy web project references without building

I'd like to have a web project in a solution that is set to "not build" in the solution configuration, however I would still like the project's references (and their dependencies) to be copied into that project's bin folder. There are class library projects in the sln that are actually built, and the web project references those.
Our current "build" just calls devenv, which does exactly this. For obvious reasons, I'd rather use MSBuild.
I am not looking for methods to do manual file copying (either individually or *.dll). There are many ways to do this. I am looking specifically for a way to replicate the behaviour that devenv.exe gives us - automatically copy references (and their dependencies) based on what is in the project section in the solution file (below).
These references come from the solution in this section:
Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}")="TheWebProjectName",
"Web\Project\Folder",
"{1CBD1906-0C2E-4C92-A81D-63C2AD816EA1}"
ProjectSection(WebsiteProperties) = preProject
TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0"
ProjectReferences = "{B125568E-E80C-4080-B8D3-5602B604734C}|Some.Reference.dll;
{117E8B0A-F0D6-47D0-BB51-71099969566D}|Some.OtherRef.dll;"
...
EndProjectSection
EndProject
MSBuild uses target files to determine it's build steps. Take a look at the relevant target files and determine how MSBuild copies those files, create your own target file and modify your build configuration to use the new target.
Relevant target files:
Location: C:\Windows\Microsoft.NET\Framework\v$(version)
Microsoft.CSharp.targets
Microsoft.Common.targets
Relevant documentation:
MSBuild
Specifying Targets
You can do build operations such as this using MSBuild Tasks: http://msdn.microsoft.com/en-us/library/ms171466(v=vs.80).aspx
In your specific case you'll probably be interested in the built in Copy Task: http://msdn.microsoft.com/en-us/library/3e54c37h(v=vs.80).aspx
Try using powershell instead of msbuild!! Ria services also relies strongly on powershell commandlets.
Cheers..
Call devenv.exe using an Exec task - we do it all the time for BizTalk solutions because old versions of MSBuild do not support the BizTalk project files...

Using an external tool for C# builds in Visual Studio

When using Visual Stdio 2008, you can make a C++ project build with an internal tool rather than having the IDE invoke MSVC directly. This improves the consistency of builds across platforms if a cross-platform build system is used.
However, I cannot figure out how to do the same as a C# project. It would be possible to simply register it as a native project with C# sources, however, you lose some of the advantages gained through having a C# project. More importantly, it will mean that allowing a project to build both directly and with an external tool (which is sadly necessary) will require two separate projects, rather than merely creating an alternate build configuration to invoke the external tool.
Does anyone know if it's possible to prevent Visual Studio from invoking csc by itself and instead call an external tool?
EDIT: Apparently there has some misunderstanding. The goal here is not to compile anything outside of Visual Studio. Instead, it's to allow Visual Studio to serve as the IDE but not the build system. There is already a (Scons-based) build system capable of compiling the C# and C++ sources, and Visual Studio has been configured to invoke Scons for compilation of C++ projects. I'm trying to configure it so that when you hit the 'Build' button, it will invoke Scons for the C# projects as well as the C++ ones.
Edit: Your question is still answered using MSBuild(if you are simply looking to compile outside the IDE). The IDE(Visual Studios) is simply a "fancy" way of constructing the build files that are built by MSBuild. Visual Studios isn't building the files, it simply is invoking MSBuild which ships with the .NET Framework 2.0 and up which compiles your code based on the project file that you create. If Scons can read and process an MSBuild file then I'm sure you can invoke it to build your project. But considering the fact that C# is a Microsoft language, I think you will be hard-pressed to find a value-add in not using MSBuild since I'd assume both the language and build tool are very tuned to work together. - End Edit
You can use MSBuild to compile your C# project. If you open your .csproj file in a text editor you will see that it is a MSBuild file. If you want to write some C# outside of the IDE you can construct a build file using the .csproj file as a starting point and invoke MSBuild to compile your apps. The IDE is just a way of abstracting the editing of the MSBuild file away for you.
If you are really industrious you can create a set of custom tasks to do things in your custom build process like move files around and versioning. MSBuild Community Tasks are a great example of using custom code to do task for you during MSBuild.
Given all the other answers, what MSBuild does when either VS or MSBuild perform a build can be found in the Targets files that ship with .Net. These can be be found in the FrameWork directory on your system. In my case:
C:\Windows\Microsoft.NET\Framework64\v3.5
Contains Microsoft.Common.targets among others. This file contains the following snippit:
<!--
============================================================
Build
The main build entry point.
============================================================
-->
<PropertyGroup>
<BuildDependsOn>
BeforeBuild;
CoreBuild;
AfterBuild
</BuildDependsOn>
</PropertyGroup>
<Target
Name="Build"
Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
DependsOnTargets="$(BuildDependsOn)"
Outputs="$(TargetPath)"/>
This means that redifining this Target you can make MSBuild an VS do anything you want. The top of the mentioned file contains an important messagge:
Microsoft.Common.targets
WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
created a backup copy. Incorrect changes to this file will make it
impossible to load or build your projects from the command-line or the IDE.
This file defines the steps in the standard build process for .NET projects. It
contains all the steps that are common among the different .NET languages, such as
Visual Basic, C#, and Visual J#.
My suggestion would be to read all you can about MSBuild and it's build file syntax and try redifining the Build target in your project(s). My impression is that after reading up on MSBuild you'll probably find an easier way to meet your requierements. You can find an example of redifining a Target like this in one of the answers of this so question .
Edit:
How to redefine a target?
Redefining is essentially defining the same target 'after' it has been defined. So for instance in your .*proj file(s) define a Build Task after the <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> line that imports all targets needed to in this case build a C# project. An example could be
<Target
Name="Build"
Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
DependsOnTargets="BeforeBuild"
Outputs="$(TargetPath)">
<Exec Command="nmake" />
</Target>
I found a question in the same direction here, where it is suggested to edit the registry. I am pretty sure there is no other way to change the compiler used by Visual Studio because there is no trace of csc.exe in any solution, config, csproj file or whatsoever, nor in the Visual Studio 9.0 folder / subfolders within the Program Files dir.
Registry locations can be found in:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\74ACAA9F1F0087E4882A06A5E18D7D32
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\9055DA7481CC1024CB23A6109FD8FC9B
but those keys may differ dependng on your installation. Conclusion: changing the compiler used by VS seems next to impossible.
Addition: The following MSDN article deals with the same question for an custom C++ compiler, and Ed Dore's answer seems to confirm my theory that there's no way to choose an custom compiler for use within VS.
Under 'Tools' > 'External Tools' you should be able to define an outside tool to do activities for you. The Command should be the path to the executible for your external tool.
Hope this helps some.
You don't have to maintain different project files to build using an external tool. MSBuild is designed to build using the same project files that Visual Studio uses.
Here's an article that describes it.
Customize Your Builds in Visual Studio Using the Standalone MSBuild Tool
It's for VS2005, but should apply to VS2008 as well.
Looking through the answers, it seems clear to me that integrating scons into Visual Studio in a way that is compatible with the debugger and so on is not going to happen...
An option you might to consider, and I understand you don't want to change build systems, but bear with me, is to use a meta-build system, ie 'cmake'. http://www.cmake.org/
Cmake doeesn't actually build the project. What it does is to create build files for you, that you can use to build the project, and on Windows, the build files it creates for you are: Visual Studio project files. You can simply load those directly into your IDE, and compile, and use normally!
CMake is I feel very easy to use, and provides a high level of transparence and maintainability.
The exact same CMakeLists.txt files on linux will causes linux makefiles to be generated.
On mingw, they can generate mingw makefiles.
There are numerous generators available within cmake. The list is here:
http://www.cmake.org/cmake/help/cmake-2-8-docs.html#section_Generators
http://springrts.com is a huge opensource rts game that used to use scons as its cross-platform build system and now uses cmake.
I understand that you don't really want to have to change build systems, so it is a medium to long term solution.
Cmake is in any case one more option, to add to those of using a custom build tool, or using msbuild, or running the scons build from the commandline by hand.
Edit your project file and update the CscToolPath keys to point to the directory containing your tool and add CscToolExe keys that holds the name of the directory:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|.NET 3.5' ">
.
.
.
<CscToolPath>path\to\custom\tool\directory</CscToolPath>
<CscToolExe>exe name</CscToolExe>
.
.
.
</PropertyGroup>
I have not tested this, and the CscToolExe key may cause problems, in which case I would simply rename the external tool executable to "csc.exe".
You can build your solution from the command line like this:
C:\WINDOWS\Microsoft.NET\Framework\v3.5>msbuild.exe "C:\path\Your Solution.sln"

Categories

Resources