How to set assembly references for Visual Studio solution using CMake? - c#

I'm working with C code where the production compiler and CI runs in Linux but the development environment is Windows and VS. The test framework used is NUnit, which is written in C# and imported as a DLL via Nuget.
I'm used to C and writing target oriented CMake but VS solutions and .NET is unknown territory for me and the weird mix of C and C# doesn't make things easier. To complicate things further there is a tool generating a mock for the platform code which the C code is to run on (I think the platform Mock is written in C++).
So, I'm using CMake 3.15.3 and Visual Studio 2017, and the setup I need to get working is the following.
A top CMakeLists.txt like:
cmake_minimum_required(VERSION 3.3)
project(MyApplication)
include(SUTWrapper)
add_subdirectory(source)
add_subdirectory(suttest)
A CMakeLists.txt in ./source/ like:
find_package(PlatformInterface)
add_library(${CMAKE_PROJECT_NAME} STATIC
MyApplication.c)
target_link_libraries(${CMAKE_PROJECT_NAME}
PUBLIC
PlatformInterface)
A CmakeLists.txt in ./suttest/ like:
enable_language(CSharp)
add_executable(${CMAKE_PROJECT_NAME}_tests
MyApplication_suite1.cs
Program.cs
TestUtils.cs)
add_sut_wrapper()
add_test(
NAME
Test${CMAKE_PROJECT_NAME}
COMMAND
MyApplication_tests.exe
CONFIGURATIONS
Debug)
And in the cmake folder containing all the CMake modules, the file SUTWrapper.cmake:
function(add_sut_wrapper)
set(PATH_TO_WRAPPER path/to/wrapper/here)
add_library(${CMAKE_PROJECT_NAME}_wrapper STATIC
${PATH_TO_WRAPPER}/foo.cs
${PATH_TO_WRAPPER}/bar.cs
${PATH_TO_WRAPPER}/baz.cs)
set_property(
TARGET ${CMAKE_PROJECT_NAME}_wrapper
PROPERTY
VS_DOTNET_REFERENCES "System")
set_property(
TARGET ${CMAKE_PROJECT_NAME}_tests
PROPERTY
VS_PACKAGE_REFERENCES "NUnit_3.7.1;NUnitLite_3.7.2")
set(PATH_TO_PLATFORM_MOCK path/to/PlatformMock/here/)
add_library(${CMAKE_PROJECT_NAME}_Platform_Mock STATIC
${PATH_TO_PLATFORM_MOCK}/Swc_${CMAKE_PROJECT_NAME}.cpp)
add_library(${CMAKE_PROJECT_NAME}_shared SHARED
../source/MyApplication.c)
target_link_libraries(${CMAKE_PROJECT_NAME}_shared
PUBLIC
PlatformInterface
${CMAKE_PROJECT_NAME}_Platform_Mock)
target_link_libraries(${CMAKE_PROJECT_NAME}_tests
PUBLIC
${CMAKE_PROJECT_NAME}_shared)
endfunction()
Then there is another file in the cmake folder, FindPlatformInterface.cmake:
add_library(PlatformInterface INTERFACE)
target_include_directories(PlatformInterface
INTERFACE
path/to/PlatformInterface/headers/here
path/to/more/PlatformInterface/headers/here)
The unorthodox use of find_package for PlatformInterface is a temporary work around and the add_sut_wrapper function is a way to handle that a whole mountain of wrapper code is already generated before this CMake project is built. I'm working with plenty of legacy forcing some odd work arounds, sorry for the weirdness.
As I understand it the NUnit setup expects the test to be built as an executable linking to a DLL of the Application code (which in turn is linked with the PlatformInterface, PlatformMock).
This all generates a VS solution when running CMake, without any warnings, but when I try to build the solution in VS I get this error:
Error CS0246 The type or namespace name 'Vector' could not be found
(are you missing a using directive or an assembly
reference?) MyApplication_tests
Vector is a namespace in the static library called MyApplication_wrapper. So obviously I link the code containing Vector. I got a similar problem before, with the namespace System and with linking to NUnit, I solved that by adding
set_property(
TARGET ${CMAKE_PROJECT_NAME}_wrapper
PROPERTY
VS_DOTNET_REFERENCES "System")
set_property(
TARGET ${CMAKE_PROJECT_NAME}_tests
PROPERTY
VS_PACKAGE_REFERENCES "NUnit_3.7.1;NUnitLite_3.7.2")
to the function add_sut_wrapper. I just can't figure out how to add the assembly reference for Vector.
I'v had to obfuscate the names and paths a bit and I tried to remove some irrelevant details but I think the essentials are there.

If you want the Vector namespace to be accessible from your MyApplication_tests target, you have to link the wrapper library containing Vector to that target. Try changing your target_link_libraries() call to include MyApplication_tests.
Also, using STATIC C# targets in CMake is discouraged, and is not guaranteed to be supported in the future. The C# terminology doesn't really use the "static" and "shared" library terms used in C/C++; rather, "netmodule" and "assembly" are typically used (see this blog post). You should use assemblies (SHARED) when defining C# libraries using CMake:
add_library(${CMAKE_PROJECT_NAME}_wrapper SHARED
${PATH_TO_WRAPPER}/foo.cs
${PATH_TO_WRAPPER}/bar.cs
${PATH_TO_WRAPPER}/baz.cs)
...
target_link_libraries(${CMAKE_PROJECT_NAME}_tests
PUBLIC
${CMAKE_PROJECT_NAME}_wrapper
${CMAKE_PROJECT_NAME}_shared)

Related

.Net Projects Integration and reference to those doesnt work properly

I have more than one .Net projects to work and i want to reference these all projects into one integrated project and add them as a reference in this project and want to run any method or function from this integrated project.So i have added all these projects in the same solution in the directory and added them to the solution and added as a reference.
Now My question
I havent converted those projects to class library as i want the main method to be there to kickoff that function from my integrated project ,if i make it class library will i still be able to access that main method???
I did add those projects reference and made them to class library and also as exe,but in both the cases , when i did import them ,but when i try to call those functions,it doesnt create an instance and doesnt show any options in intelligence,on top of that,once i try to build it back again,it throws an error "The type or namespace name could not be found(are you missing a using directive or an assembly reference?)"
Can someone please answer them !!!!
Internally there is little difference between a .NET exe and a .NET DLL. For most purposes a .NET .exe is just a .NET dll with some data saying "this class has the main function". It is one of those things they copied really well from Java and improoved upon.
There might be secondary differences (I am not sure the .exe provides full COM/.NET interop support, for example). But for most purposes, you can use a Compiled .NET exe like a compiled .NET dll. Including putting it into the references of a 3rd project.
The main issue here is that only the compiled code in the propert directory is considered. Especially if you still plan on working on those backend References, you need to be aware if you referenced the Debug or Release version of the .dll/.exe

F# dll in F# script

I have a C# dll that I need to reference in F#. I can do this fine in a .fs file, but I can't seem to get it to work in an F# script (.fsx) It is strange because in my script, I have no problem referencing F# dlls. I thought C# dlls and F# dlls were essentially the same.
EDIT:
I have two dlls (1) csharp.dll and (2) fsharp.dll built with csharp and fsharp respectively. I reference them with:
#r "bin\Debug\csharp.dll"
#r "bin\Debug\fsharp.dll"
which the compiler recognizes. The line where this occurs looks like this:
let new_object = new fsharp.type(Observable<csharp.type>)
Where I'm creating an object defined in fsharp.dll which takes an observer of a type defined in csharp.dll. When I try to run it in FSharp interactive, I get the following error:
error FS0074: The type referenced through 'csharp.type' is defined in an assembly that is not referenced. You must add a reference to assembly 'csharp'.
The thing that doesn't make sense is that csharp.type had been used successfully in a previous line. But when we introduce the csharp.type with an fsharp.type, I get an error all of a sudden saying the csharp type is missing an assembly reference, even though the problem seems to be with the fsharp.type.
It seems you'll need also refer in the script System.Reactive.dll or wherever the class Observable<T> is defined
#Petr is correct. You need to add all references, and their references.
If you are using Visual Studio, I suggest that you install Visual F# Power Tools. When installed, right-click 'References' under your project and choose to 'Generate references for F# interactive'.
This will generate (and keep updated!) the files
{ProjRoot}/Scripts/load-references.fsx
{ProjRoot}/Scripts/load-project.fsx
So in your F# script (assuming it is in the project root), you just do
#load "Scripts/load-project.fsx"
These generated files should go into your version control of choice.

How to sign C++/CLI Project to use it in C# Signed Solution?

I am using VS2012. I have a C++ Native Library and a C++/CLI project that I built to wrap the C++ Native library. I am bulding the projects for x64 bits.
I don't have much experience with C++ or C++/CLI, this is the first project I am trying to sign.
How I signed my C++/CLI project:
I placed my strong name in the same folder where the C++/CLI is located and then I went to:
Right Click on the project -> Configuration Properties -> Linker -> Advanced -> Key File.
I wrote the name of my file in the Key File attribute: StrongNameKey.snk.
It builds without errors and I can run the tests. I also have another Solution that uses the resulting dll, and it builds fine and the test pass. But that solution it is not using signed assemblies.
The Errors I am getting:
I have a C# Solution with all its assemblies signed.
At first I was using my C++/CLI dll in this solution without signing it, and it was mostly working. But I was getting an error similar to this when trying to use one of the Wrapper classes in a ComboBox in WPF.
That post made me realize I had forgotten to sign the C++/CLI assembly.
So then I signed them as I described above, but when I tried to use the dll in my C# Solution I was getting a lot of errors. None of them seemed related to my dll, except 2 warnings.
First Warning
Warning 18 There was a mismatch between the processor architecture of the project being built "MSIL" and the processor architecture of the reference "MyNamespace.MyCppCLI", "AMD64". This mismatch may cause runtime failures. Please consider changing the targeted processor architecture of your project through the Configuration Manager so as to align the processor architectures between your project and references, or take a dependency on references with a processor architecture that matches the targeted processor architecture of your project. MyTestApp
I don't have anything set as AMD, I just have it configured to x64. My C# Project is configured for AnyCPU.
Second Warning:
The error said like there was 2 different locations of the signature.
I was using the same "name" both for the C# and the C++/CLI signature (the files were copies). I changed the name of the C++/CLI signature, and built the project again. And now the C# project builds, but I am still not able to use the class in the ComboBox and the designer throws the error of the other post, except that the public token is not null.
I don't know what I am doing wrong.
What are the correct steps to sign a C++/CLI Project? Where should I place the signature file?

How to merge F# and C# assemblies with ILMerge so that all types would be available when referenced?

I have a solution with many F# and C# projects. My goal is to merge them all to one library using ILMerge. Resulting merged dll will be put in a NuGet package and referenced in other projects. However, I'm running into few issues when merged dll is referenced in F# projects.
The problem I have is that if primary assembly given to ILMerge is F# then referencing resulting dll in F# project allows to only access F# types. If C# dll is chosen as primary assembly for merge then extension methods from merged F# assemblies were not available when referencing in F# project. Also modules with AutoOpen attribute were no longer implicitly opened when opening enclosing namespace.
Is there a way to merge F# and C# assemblies so that all types (including extension methods) would be available?
In part of our code base, a big chunk of the library is done in F# and the rest in C#. Both F# and C# code are front facing.
We have a hellish batch file to take care of mergeing and what I see is that we are merging with this code:
echo merging %mergeapp% /keyfile:"%keyfile%" /target:library /attr:"%dstpath%%csharpdll%" /targetplatform:%targetplatform%,%targetlib% /lib:%sllib% /lib:%targetlib% /lib:"%libpath%lib" /out:"%mergedpath%..\%csharpdll%" "%dstpath%%csharpdll%" "%dstpath%%fsharpdll%"
%mergeapp% /keyfile:"%keyfile%" /target:library /attr:"%dstpath%%csharpdll%" /targetplatform:%targetplatform%,%targetlib% /lib:%sllib% /lib:%targetlib% /lib:"%libpath%lib" /out:"%mergedpath%..\%csharpdll%" "%dstpath%%csharpdll%" "%dstpath%%fsharpdll%"
and that does what we intend. However, we do not publish any extension methods nor do we do any AutoOpen. What we discovered was a bug in the F# compiler that, up until we started putting obfuscation in the mix, required us to run ildasm on the F# assembly and rip out the offending code. The other issue is that F# doesn't properly support the protected modifier on members (F# makes them public) so we created an attribute that we could hang on class members that were meant to be protected. Then we wrote a tool that uses Cecil to blow the assembly apart, rip out our attribute and change the access to those members to protected (code is in the accepted answer here).
I didn't know about AutoOpen, but I had to do a similar task, so I created class called a registrant that did that kind of work like this:
type FSharpRegistrant() =
do
// do whatever I need to get going
Then in a static constructor within the C# module, I wrote some code that instantiates the F# registrant using reflection to find the class (since in my code base the C# code builds first and doesn't know there's F# code at all). This is ugly code with a lot of error checking, but it works.

Has any one compiled lib x264 using CLI backend for gcc compiler?

Has any one compiled lib x264 using CLI backend for gcc compiler? (Compiled x264 into .net dll)
Are you using C99 features? If not, Visual C++ with the /clr:pure option should do the trick. You will need a little bit of C++/CLI mixed in to define your entrypoints that other .NET projects can call, but those can be in completely separate files (you can share entire C-only source files with native projects).
EDIT: Basic guide to making this work:
In Visual Studio, create a C++/CLI Class Library project
Add all your C source files to the project
In Project Configuration, set the include path so your headers are found
In Project Configuration, also set "Use of Common Language Runtime" to /clr:pure
In the .cpp file created by the new project wizard, add #include directive for the header files which prototype the functions you want to use.
In the ref class created by the new project wizard (in the aforementioned .cpp file), add some functions (maybe static functions) which call your C library functions.
Compile, add this .DLL as a reference of your C# project, and enjoy
As a hint, instead of creating a forwarding function in the ref class for every function in the library, you may want to make functions that do useful work (for the particular definition of useful for your particular project) by calling a bunch of library functions.
You'll probably want to get comfortable with the marshal_as template which is good for converting .NET System::String into C strings and back again.

Categories

Resources