Solution fails to build when using OutDir with MsBuild - c#

We have several .Net 4.0 solutions that all depend on assemblies built by one specific solution, our Server.sln (I guess Shared.sln would have been a better name for it). Our build process first builds the Server.sln to a Binaries directory using the MsBuild OutDir parameter, and then subsequent solutions to subdirectories (e.g. Binaries\Client, Binaries\Web, etc.), again using the OutDir parameter to specify the path to the appropriate subdirectory. This allows us to easily publish our separate applications to different places. This works fine for all of our solutions, except a new API.sln that we created.
Like the other solutions, API.sln references some DLLs built by the Server.sln, but the actual compile error I get is that one project (we'll call it Project1) cannot find a reference to Project2. Project1 and Project2 are both projects in API.sln, and Project1 references Project2 via a project references; not a dll file reference. Also, Project2 does not have any external dependencies either; the only references it has are to System, System.Core, and System.Runtime.Serialization.
Everything works if I set the OutDir to just be the Binaries directory, but I don't want to do that as then all of the API.sln assemblies get mixed with ALL of the Server.sln assemblies, which makes deploying just the API.sln assemblies harder. When I set the OutDir to be Binaries\API then I get the build error saying that:
The name '[Project2 Class Name]' does not exist in the current context [Path to Project1.csproj]
I checked though and the Project1.dll and Project2.dll assemblies are both getting created to the Binaries\API directory, so I'm not sure why this error is getting generated. Our TFS build server has this problem, but I can also recreate it on my local machine by calling MsBuild on the .slns with the same parameters. Any ideas?
Here are the two msbuild commands that I'm using to actually do the builds:
Build the Server.sln:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe /nologo "C:\PlatformAPIBuild\BuildProcessTests\Sources\RQ4.Server.sln" /nr:False /fl /flp:"logfile=C:\PlatformAPIBuild\BuildProcessTests\Sources\RQ4.Server.log;encoding=Unicode;verbosity=normal" /p:SkipInvalidConfigurations=true /p:ReferencePath=C:\PlatformAPIBuild\BuildProcessTests\Binaries /p:OutDir="C:\PlatformAPIBuild\BuildProcessTests\Binaries\\" /p:Configuration="Release" /p:Platform="Any CPU" /p:VCBuildOverride="C:\PlatformAPIBuild\BuildProcessTests\Sources\RQ4.Server.sln.Any CPU.Release.vsprops"
Then build the API.sln:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe /nologo "C:\PlatformAPIBuild\BuildProcessTests\Sources\IQ.Platform.PublicAPI.sln" /nr:False /fl /flp:"logfile=C:\PlatformAPIBuild\BuildProcessTests\Sources\IQ.Platform.PublicAPI.log;encoding=Unicode;verbosity=normal" /p:SkipInvalidConfigurations=true /p:ReferencePath=C:\PlatformAPIBuild\BuildProcessTests\Binaries /p:OutDir="C:\PlatformAPIBuild\BuildProcessTests\Binaries\Platform.PublicAPI\\" /p:Configuration="Release" /p:Platform="Any CPU" /p:VCBuildOverride="C:\PlatformAPIBuild\BuildProcessTests\Sources\IQ.Platform.PublicAPI.sln.Any CPU.Release.vsprops"
And here is the log file from the API.sln build. You'll notice that it has errors like,
Warning as Error: Reference to type 'IQ.Platform.Framework.WebApi.Model.Hypermedia.AccessControl' claims it is defined in 'c:\PlatformAPIBuild\BuildProcessTests\Binaries\Platform.PublicAPI\IQ.Platform.Framework.WebApi.Model.dll', but it could not be found
but the file it says isn't there, is actually there when I look for it after the build fails, and you can see that one of the first things it does at the top of the log file is copy that assembly to that path, so I'm not sure why it can't find it. The IQ.Platform.Framework.WebApi.Model is the Project2 in my discussion above.
Here is the log file from the API.sln build with all of the Test projects removed, so that only one error remains, which will hopefully make looking through the log file a little easier. And here is the same log, but with diagnotic verbosity.

Related

Unpredictable System.DllNotFoundException

I downloaded a package from SourceForge, PlanEph, which has 64 and 32 bit DLLs for C#. I got the 32 bit included C# demo to work by putting the DLL in my bin/Debug directory (I'm using Visual Studio 2015 Community) and adding the DLL as a reference.
Then I tried to make my own version of the demo in a separate solution, and got the System.DllNotFoundException. Various experimentation lead me to believe I can't have two identical namespace names anywhere in my Visual Studio installation, so I erased everything and started over.
I made a directory C\GJAbin, put the DLL in it, and added it to the system Path variable. I also put a helloWorld type program in that dir and executed it from the command line to verify the directory really was in the path. Then I recreated the demo solution, added the DLL as a resource, and built the solution "successfully". Then I ran it and got the System.DllNotFoundException.
So I can't understand why the DLL is being found when compiling but not at run time.
Go to project settings, go to "publish" tab and on the top most button (labeled something like "application files"). Chose "Show all files" checkbox if you don't see your DLL. Set the DLL's publish status to "Include" (NOT "Include (Auto)"!!) and publish it again.
Now the DLL should be inside the publish folder.
So I can't understand why the DLL is being found when compiling but not at run time.
Locating the assembly at compile time is done differently (by MSBuild) than at runtime (by the CLR).
At compile time, MSBuild has specific search paths that it knows about, or in most cases like this, there will be something in your project file telling MSBuild where to look for the file. Usually the <Reference> has a <HintPath>.
At runtime, the CLR will attempt to find the assembly from its own set of well-known paths. It will look in your app's config file (if applicable), then in the Global Assembly Cache (GAC), then in your app's root directory. Much more detail on this is available here.
You can tell MSBuild to copy the reference to your build output directory (usually the same as your app root directory when running). In VS, you can do this by selecting the reference and looking at the Properties tool window (or press F4 by default). Set the CopyLocal state to True. In the project file, this will add a <Private>True</Private> on the <Reference>.
You can also add the assembly to the GAC using the gacutil tool, but this does make it harder if you want to share your app with others. Usually it's preferable to keep a copy in your app root directory.
If it's still not working, you can also see the log for how the runtime is trying to find this assembly. The fuslogvw.exe tool in the Windows SDK (you can run it from the VS command prompt and it will be on the %PATH%) allows you to enable logging for assembly loads. You do need to be able to run this as an administrator to configure the setting.
As you can see in the screenshot, you can either log the results in the exception (so that you can see it while debugging), or you can log it to a file on disk (so you can see it whenenver).
The problem turned out to be an unfortunate interaction among the way the author chose names and the way Visual Studio displays information and error messages. The author created a c# dll Astronomy.PlanEph32.dll containing a namespace PlanEph32, which which was really just a wrapper for the c dll PlanEph32.dll. So all the error messages about not being able to load PlanEph32.dll were referring to not finding the c dll; the c# dll was being found just fine.

VSIX can't find a static method in another DLL

Problem
I have a static (extension method) that is referenced in my VSIX project. When I compile the VSIX project, I can see the dll in the bin/debug folder along with the VSIX dll (Same folder).
In addition, intellisense has no problems with this nor does the compile! When I run the VSIX project in debug mode. I see this error:
"Method not found: "System.String MethodName(System.String)" which is the extension method in another assembly (in the bin/debug folder).
I've read other posts like these:
Can't find VSIX dlls with DllImport
Include external .dll in Visual Studio Extension
Both solutions don't look right to me, after all, the dll is in the proper folder, it finds its own code, but not the other assembly.... The references and using statements and intellisense work!
This tells me that it's a loader issue, somehow VSIX projects are failing to load other referenced assemblies or maybe it's by design.
I've tried loading them as Assets but they are not "other" VSIX projects so all of the asset options are out.
Notice however that these asset types are all prefixed with Microsoft.VisualStudio. I've tried Microsoft.VisualStudio.Assembly and they do not work, in fact it screws up the manifest attempting to do that.
Another oddity, I did include a reference to yet another assembly that comes in as an EXE. All of those methods work fine. It's just the dlls that are not getting loaded. For these, I cannot use a USING statement rather, I have to use the fully qualifed NameSpace name to get it to work... But if I try the same trick on the DLLs it doesn't work.
One post suggested adding the assemblies manually to the manifest file... but that post was in 2010.
This doesn't work either:
To include satellite DLLs from referenced assemblies in the VSIX package, add SatelliteDllsProjectOutputGroup to the Output Groups Included in VSIX property.
What am I missing?
The root cause of this problem is that by default VSIX want's everything to be Strong Named. I had unchecked that option due to 3rd party DLLs which were not signed. I was able to get the source and "Strong Name" them.
I then strong named the VSIX project and everything worked.
Go to properties page of the project:
I began looking into tools like this, which can strong name DLLs when the source code is unobtainable.
https://brutaldev.com/post/net-assembly-strong-name-signer

NuGet targets wrong folder when publishing web application via MSBuild.exe in CI

I am trying to set up a project in our CI server (bamboo).
I have an API solution containing multiple projects (data access, service interfaces, WebApi, tests, a few others... you get the idea). I run nuget.exe in a script to pull in the requisite packages at the solution level. The packages go into the solution directory .nuget.
When I use MSBuild to create the binaries, everything is fine. I then use the MSTest runner on the test projects; still everything is fine. I then shut down the destination web service, and then run msbuild.exe against the WebApi project with the parameters /p:DeployOnBuild=true /p:PublishProfile=INTENV.
This is where bamboo barks at me. I get the failure error message like so:
error : This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is ..\(My Solution Folder)\\.nuget\NuGet.targets.
The project is obviously looking for the dependencies in the project folder, in this case and in this case only.
How do I tell MSBuild.exe that this folder is one level up? This is the only place where it gets confused.
So what was happening when I targeted the project instead of the solution with MSBuild.exe is that it considered $(SolutionDir) to be the project directory. It was looking for the the nuget targets at $(SolutionDir).nuget\NuGet.targets, which was an incorrect path if starting at the project level, but perfectly fine at the solution level.
On my MSBuild.exe command line, I added a parameter as follows:
/property:SolutionDir="(path of my solution)"
This did the trick.
I also tried to change the project settings, but NuGet always replaced them; the above manner is the only way I could get it to work.

Installation of dll's into GAC

I would like to have a smooth and efficient installation of the solution, but what I "inherited" is very far from that, and the guy who programmed most of it has left the company.
At present I am trying to install it on a test-server, and not all the dll's land in the correct places after the installation.
Firstly, if I use log4net in a project, then I need the log4net.dll in the folder after the installation (I guess). How do I get the log4net.dll to be copied with the project dll?
Secondly, Project A expects Project C's dll to be in the GAC or so it seems when I debug in Visual Studio and check where the modules are loaded from.
I also see that this is entered in the post build event commandline of Project A:
"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\x64\GacUtil.exe" -i "$(TargetPath)"
copy "$(TargetPath)" "C:\Program Files\MySolution\bin"
So how do I get Project C's dll into the GAC by way of the installation? I get an error on the Test Server because it can't load C.dll.
UPDATE WITH MORE DETAIL
After the solution has been installed with Windows Installer, a few folders are created in the parent folder such as Apps, bin, Engines, Service etc.
In the Apps folder, I have A.exe, which is looking for C.dll. However, C.dll lands up in the bin folder. As mentioned above, during execution of A.exe, it actually loads the modules of C.dll from the GAC (and on my laptop, those files are in the GAC because of the post-build event command line specified in the properties of Project C, but not in the GAC of the Test Server to which I am trying to install this solution).
So yes, I assume I could run something like this:
gacutil -i C.dll
after the installation, but it doesn't seem right.
There are two parts to your question relating to the GAC, and ensuring DLLs are copied.
GAC
Check out this link (https://msdn.microsoft.com/en-us/library/dkkx7f79%28v=vs.110%29.aspx) from MSDN on how to install into the GAC. The key thing is it must be strongly named or it will fail.
DDLs
Depending on how you are referencing Log4Net, there are a few ways to do this.
If you can add a reference in your project, make sure the property CopyLocal is set to true
If you just have the file locally, you can add it to a sub folder of your project with a symbolic link (https://support.microsoft.com/en-us/kb/306234), and then set the CopyToOutputDirectory property.
Hopefully these help you along.
I found what I was looking for!
Select the Setup project, then go to the menu "View" -> Editor -> File System.
It seems you can specify where the dlls must go, and what should be copied to the GAC during installation.

In Teamcity, how to add reference of dll residing in parent directory of solution when running MSBuild?

There is a project in c# which references few dlls that are present in the parent directory of solution. When I try to build using MSBuild in TeamCity, it fails because it cannot find the dlls. I tried providing the dlls as fixed path using Artifacts but no luck!
Could somebody please tell me if there is a way to add the reference of the dll present in parent directory in TeamCity?
Thanks.
Generally speaking MSBuild doesn't have any idea of what a solution is. MSBuild considers each project an independent entity and the files under the project folder are in its 'cone'. You are better off by staging the dlls in the project folder as a pre-build step of the project.
You should try to build your solution with MSBuild but without TeamCity. If it works check whether your dlls are being checked out during the build.
Teamcity does nothing else than invoking msbuild.exe.

Categories