it took me way too long to go from nothing, to a working example of linking two csharp files together, so, for my own clarity, here are two working examples in mono:
How to merge multiple csharp source files into one exe/dll
- let us have two csharp files:
bar.cs and foo.cs
- let us want to call foo functions from bar functions.
.
├── bar.cs
├── foo.cs
└── Makefile
0 directories, 3 files
- let our foo symbol be a simple x squared function
// foo.cs
public static class foo {
public static int square(int x) {
return x * x;
}
}
- let our bar have a simple call to this square function
- let bar handle the program main entry point adapter, including conventional c99
int main(int argc, char** argv) {}; io for clarity
// bar.cs
public static class bar {
public static int three_square() {
return foo.square(3);
}
public static int main(int argc, string[] argv) {
System.Console.WriteLine("Hello World");
string prog = argv[0];
System.Console.WriteLine(System.String.Format("program is '{0}'", prog));
for (int argi = 1; argi < argc; argi++) {
System.Console.WriteLine(System.String.Format("got argument '{0}'", argv[argi]));
}
System.Console.WriteLine(System.String.Format("three_squared is '{0}'", three_square()));
return 0;
}
public static int Main() {
string[] argv = System.Environment.GetCommandLineArgs();
int argc = argv.Length;
return main(argc, argv);
}
}
As shown in this example, there is no reference from bar.cs to foo.cs at all. The relationship is established at compile-time by including the foo.cs code within the bar.cs compilation unit. Here I accomplish this with a basic Makefile
# Makefile
all: myprog.exe mylib.dll
myprog.exe: foo.cs bar.cs
→ mono-csc $^ -out:$@
mylib.dll: foo.cs bar.cs
→ mono-csc -target:library $^ -out:$@
This Makefile is equivalent to executing the bash command literal:
$ mono-csc foo.cs bar.cs -out:myprog.exe
$ mono-csc -target:library foo.cs bar.cs -out:mylib.dll
result
├── bar.cs
├── foo.cs
├── Makefile
├── mylib.dll
└── myprog.exe
0 directories, 5 files
running the program
$ ./myprog.exe
Hello World
program is '/opt/demo/myprog.exe'
three_squared is '9'
How to dynamically link multiple csharp source files as separate exe/dll files
(I found the csharp behavior extremely un-intuitive [2] here.)
- let us have two csharp files:
baz.cs and faz.cs
- let us want to call faz functions from baz functions.
.
├── baz.cs
├── faz.cs
└── Makefile
0 directories, 5 files
// faz.cs
public static class faz {
public static int square(int x) {
return x*x;
}
}
// baz.cs
public static class baz {
public static int main(int argc, string[] argv) {
System.Console.WriteLine("Hello World");
string prog = argv[0];
System.Console.WriteLine(System.String.Format("program is '{0}'", prog));
for (int argi = 1; argi < argc; argi++) {
System.Console.WriteLine(System.String.Format("got argument '{0}'", argv[argi]));
}
System.Console.WriteLine(System.String.Format("four squared from 'faz' is '{0}'", faz.square(4)));
return 0;
}
public static int Main() {
string[] argv = System.Environment.GetCommandLineArgs();
int argc = argv.Length;
return main(argc, argv);
}
}
As shown in this example, there is no reference from baz.cs to faz.cs at all. faz is just an anonymous name in baz.cs. The relationship is established at compile-time by including a reference assembly mylib.dll, presumably csharp will read some csharp specific index of all symbols within all referenced assemblies and decide to map our public static class faz to the name faz in baz.cs for us [1]. Here I accomplish this linkage with a basic Makefile:
# Makefile
all: myprog2.exe
faz.dll: faz.cs
mono-csc -target:library $^ -out:$@
myprog2.exe: faz.dll baz.cs
mono-csc baz.cs -r:faz.dll -out:$@
This Makefile is equivalent to executing the bash command literal:
$ mono-csc -target:library faz.cs -out:faz.dll
$ mono-csc baz.cs -r:faz.dll -out:myprog2.exe
result
├── baz.cs
├── faz.cs
├── faz.dll
├── Makefile
└── myprog2
0 directories, 5 files
running the program
$ MONO_LOG_LEVEL="debug" MONO_LOG_MASK="asm,dll" mono ./myprog2.exe
Mono: ...
Mono: Loading reference 0 of /opt/demo2/faz.dll asmctx DEFAULT, looking for mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Mono: ...
Hello World
program is '/opt/demo2/myprog2.exe'
four squared from 'faz' is '16'
footnotes
- [1]
I'm not sure how to gain any firm grantees here.Note, if you try to compile mono-csc with two dll's eg -r:./my.dll -r:./myevil.dll and they both declare public static class faz {...}, csc will abort with error CS0433: The imported type 'faz' is defined multiple times.
- [2] trying to access mylib.dll as a conventional dll doesn't work. eg
public static class libfaz {[System.Runtime.InteropServices.DllImport("libfaz.dll")] public static extern int square(int x);} compiles, but fails to execute. (Troubleshoot with MONO_LOG_LEVEL="debug" MONO_LOG_MASK="asm,dll" mono ./myprog2.exe to note that dlopen fails once it finces mylib.dll due to bin format issue). I expect this cannot resolve either due to dlopen() bin format incompatibility or because of some dlsym() name mangling incompatibility. csharp is magic and invisible instead.
#includecode. Type resolution for compilation occurs in more than one pass through the code, so that all visible types in the source and all visible types in referenced assemblies become eligible as symbols for type resolution. There is no longer any need to explicitly enforce includes so that types are defined 'before' they are consumed.