Chapter 4

First steps with the X# compiler

In 99% of all cases X# developer will use either Visual Studio with a (xml) project file (or XIDE). Whenever you press F5, Visual Studio runs the X# compiler through a predefined build task. For simple console applications with no UI setting up a project each time might to many files and directories to take care of. The alternative is to use a simple editor for typing in the source code and compiling it from the command line. Whereas the C# compiler is a program file with the name csc.exe and the Visual Basic compiler is a program file with the name vbc.exe, the X# compiler is a program file with the name xsc.exe. Since the X# bin directory is part of the path environment variable, there is nothing to configure or to set for being able to run the X# compiler from either the PowerShell or the classic command window (if you use PowerShell, type Get-Command Xsc to get the location of the X# bin directory).

By the way, most examples in this book have been created that way. Either with Notepad2 or XIDE.

Hello, World in X#

There a two ways to create a typical X# program without an UI as a console application:

  1. By using a function with the name Start and a data type of void.
  2. By using a class with a Start() method of type void and using the compiler switch -main for naming that class

I prefer the second option because it's more flexible and allows declaring the entry point method with the Async keyword as asynchronously "awaitable".

NOTE: The "modern style" C# syntax for initializing the Main() with a lambda expression instead of a method body is possible in X# since version 2.8:

static void Main() => HelloWorld();

Also, the so-called "top-level statements" that came with C# 9.0 and radically simplify the structure of any console application by removing the need for a class definition and a Main method are not part of X# 2.x.

Compiling X# code directly

There is no real benefit in calling the X# compiler directly. But it can be convenient from time to time. It is also a simple way of getting an overview of all the compiler switches (and there are many).

Calling the X# compiler is trivial:

xsc HelloX#.prg

HelloX#.prg contains the source code (you probably know already that X# kept the tradition of using the prg file extension for X# source files).

The result is a file with the name HelloX.exe (if everything compiles fine of course).

Alt Compiling and running HelloX#.prg in the console

Fig 4.1: Compiling and running HelloX#.prg in the console

In case you are not getting a green number and some strange characters instead, please run the exe in a "modern" console like Windows Terminal. And here is a small challenge. Extend the tiny program so that the number will be printed in red if it is less than 183.

It's getting a little more "challenging" when the source code should be compiled with another dialect like VO. The reason for this is that each of the needed X# assemblies must be referenced with the /reference switch (or /r for short):

xsc .\HelloX.prg /dialect:VO /r:XSharp.Core.dll /r:XSharp.RT.dll

The last example assumes that both dll files are located in the current directory which is not a requirement. Each file can be addressed with either a relative or an absolute path:

xsc .\HelloX.prg /dialect:VO /r:"C:\Program Files (x86)\XSharp\Assemblies\XSharp.Core.dll" /r:"C:\Program Files (x86)\XSharp\Assemblies\XSharp.RT.dll"

To avoid long command lines putting all the arguments into a text file is a really practical alternative:

xsc --% .\HelloX.prg @xopts.rsp

The file xopts.rsp just contains the rest of the arguments of the last call.

And what about --%? This is a special "handshake" to tell the PowerShell command line parser not to parse anything beyond that sign - otherwise, you would get an error message because the @ has a special meaning for PowerShell. You can avoid PowerShell of course and use cmd.exe instead.


TIP: The text file xsc.rsp in the X# program directory (eg. C:\Program Files (x86)\XSharp\Bin) contains a list of all assemblies that are always referenced to make sure that all "standard" assemblies are automatically referenced. If you don't want this, use the /noconfig switch.


It's probably self-explanatory that as soon as the build process involves more than one source file it's "obligatory" to use MsBuild and a project file (usually with a .xsproj extension).


NOTE: Even if an assembly (file) is in the GAC (directory), the full path has to be given. There is no compiler switch for directly accessing a file that is in GAC.


Which dialect do you prefer?

I don't know of any other programming languages that offer different "dialects". But readers who know the history of X# (chapter 2) know that the development team has set themselves the bold goal of creating the successor to all languages in the "xBase world". X# offers six dialects: Core, VO, Vulcan.NET, Harbour, Xbase++ and of course FoxPro. The default dialect is Core, and the most important dialect is VO. I have not used any other of the remaining four dialects since I started developing in X# many years ago. The dialect is usually set in the project properties pages in Visual Studio. Choosing a dialect affects the behavior of the compiler and determines if data types and functions from the VO world can be used.

If you start a new project or if you want to use X# as an alternative to Visual Basic or C# and want to stay 100% compatible with the rest of the .Net world, keep the Core dialect. Otherwise, the choice will be most likely VO.

Alt There are six X# dialects to choose from but VO is the most important one

Fig 4.2: There are six X# dialects to choose from but VO is the most important one

The rich choices of X# templates in Visual Studio are a little confusing since not all dialects offer the sample template types. Templates for a Windows application are only available for the Core and the VO dialect. Be aware that choosing a template only creates a couple of source files and a project file with certain settings. There is nothing that cannot be changed afterward.

Compiler switches

The X# compiler (as with version 2.13.2.2) offers an impressive number of 120 (if I have counted them correctly) switches that influence the compile process. One example is -target for specifying the compile target (exe or library for example), and another is -dialect for specifying another dialect than Core. The most important switch is probably -reference, or -r for short, for referencing assemblies the target is depending on. All switches have shortcuts like -t or -d. The -? displays all available switches.

It's important to understand how far-reaching the compiler switches are and that they can influence important parts of the X# syntax. If the switch -cs:+ would be set, X# would become case sensitive like C# (what a nightmare that would be;). The switch -az would force the compiler to use zero-based arrays and not arrays with 1 as the index for the first element.

Usually, there is no need to use any of these switches. In Visual Studio, the most important switches can be found in the Language tab in the project properties.


TIP: The Visual Studio build process is more complicated than just a call of xsc.exe. It's controlled by the various settings of the project file and predefined "build target" files contained in the X# MSBuild directory.


If compatibility with Visual FoxPro or even Clipper is still important, you will find all the specialized switches in the Dialect tab.

ALT The important switches are part of the language tab

Fig 4.3: The most important compiler switches for the X# compiler are part of the language tab

Switch Specifies
-target (-t) the compiler output type like exe or lib.
-out the name of the output file.
-reference (-r) one or more assembly files to include in the compilation process. If the assembly is not in the GAC, the full path is needed. Otherwise just the full name of the assembly. For multiple references use -r for each file.
-dialect the dialect to compile for (like VO)
-s that the compiler only does as a syntax check.
-cs that the compiler checks case sensitivity for names.

Tab 4.1: A few of the many switches of the X# compiler


Important: If you set the case sensitivity flag /cs not only do class names, member names, and variable names become case sensitive, namespace names become case sensitive too. In this mode a System.Data. Sqlite is not the same as a System.Data.SQLiteanymore.


Compiling with references

In almost all scenarios the compiler needs a couple of assembly libraries for dissolving references. This is always done by the /r: switch. Either one /r switch for each assembly file or a text file that contains all the references. The path of the DLL file can either be relative or absolute.

The following example is typical for X# development. Whenever another dialect than core is used, a reference to two assemblies is needed:

xsc .\test.prg /dialect:Vulcan /r:../assemblies/XSharp.core.dll /r:../assemblies/XSharp.Rt.dll

Whereas in C# it's possible to combine multiple references with a comma, in X# each reference needs a separate /r switch.

If you want to stay in the command line for building source files that require several compiler switches, a response file makes calling xsc.exe simple and clean. A response file is just a text file that contains the compiler switches that would be part of the command line:

/dialect:Vulcan
/r:../assemblies/XSharp.Core.dll
/r:../assemblies/XSharp.Rt.dll

It's a convention that response filenames own the extension .rsp but that doesn't matter.

Calling the X# compiler now becomes easy:

xsc .\test.prg @test.rsp

If you are using PowerShell, the call has to be

xsc --% .\test.prg @test.rsp

Conditional compilation

There is a reason, why Visual Studio always distinguishes between a Debug and a Release configuration. With the Debug-Configuration the project will be compiled with the /debug-Switch on, so there will be a (sometimes huge) pdb file with the information for the debugger. Also, the output directory will be different. Another compiler switch (/d) defines a constant with the name DEBUG.

This constant is used for conditional compilation with preprocessor commands like #IfDef and #EndIf.

The following example compiles a console output only when the constant DEBUG is defined.

#IfDef DEBUG
   Console.WriteLine("Just a message that everything works as expected")
#Endif

If the prg file will be compiled without the /d switch, the WriteLine call will not be part of the exe:

xsc .\XS_CondCompilation.prg

To include all commands between IfDef and EndIf the DEBUG constant must be defined through the /d switch:

 xsc .\XS_CondCompilation.prg  /d:DEBUG

VO or .Net functions?

Since X# is a successor of the Visual Object Language (VO), the complete VO runtime functions are available as part of the X# runtime when a project is compiled with another dialect than Core. Since the .Net runtime also contains (at least) several hundred functions (called methods) an overlap is the (logical) consequence.

Take the string functions for example. There are many similarities but also some differences you have to know about.

Getting a substring can the done with the Substr function of VO and also with the Substring method of .Net. They accomplish the same but use a slightly different syntax.

Local s1 := "To boldly go where no man has" As String
Local s2 := Substr(s1, 1, 10) As String
Local s3 := s1:Substring(1, 10) As String

The main difference is not so much the slightly different syntax, the main difference is the fact that in VO an array index starts with 1 (except when the -az switch is used), in .Net it always starts with 0.

That means that s2 = 'To boldly g' and s3 = 'o boldly go'.

That's something to keep in mind;)

Another not-so-obvious difference has something to do with applying a string function to a null value. A VO function will not "crash" with a NullReferenceException when the argument is null.

Besides that, it's for me mostly a matter of taste. I don't assume any measurable performance difference. And it's no problem at all to mix VO functions with .Net methods.

So, the most important argument for choosing either one of these functions would be to keep the code consistent which is always a good idea.

When porting a large VO code base changing the VO functions just for the sake of making them more look like "real .Net functions" should not have a high priority.

A few words about the X# runtime

The X# compiler produces an exe or dll file (called Assembly). The program file always needs the .Net runtime which is a file named mscorlib in the simplest case. As soon as a VO function is used, the X# runtime is needed too. The X# runtime consists of several dll files. The most important ones are XSharp.Core.dll and XSharp.RT.dll. They contain, besides other things, the .Net versions of the classic VO functions.

Imagine a super simple console application:

Function Start() As Void
  ? Upper("we are all upper class")

Since the Upper function (without having to specify the VO dialect) is used, the console application has to be compiled with a reference to XSharp.Core.dll:

xsc .\test.prg /r:XSharp.Core.dll

As mentioned before, it doesn't matter if an assembly file is in the GAC, in the current directory, or another directory, a relative or a full path is always needed.


NOTE: The X# runtime has a lot of "states". State determines how strings are compared and sorted (with ASort()), how floats are compared (how many decimals), how numeric and date values are displayed etc.


A look behind the scenes - making IL code readable again

Like any .Net compiler, the X# compilers create not machine code for the CPU but machine code for the CLR. This code is called Intermediate Language or IL for short. It had been one of the most discussed features of the .Net Framework when it was released by Microsoft in the ear 2002 how easily the IL code can be disassembled in almost readable C# code. This was always by design. The tool of the hour was the infamous .Net Reflector by Lutz Roeder, a Microsoft employee at that time.

Although .Net Reflector still exists and is a very good and powerful tool (its marketing slogan is "Decompile, understand, and fix any .NET code, even if you don't have the source"), since it's not free anymore, most .Net developers, including myself, use the very good ILSpy by Christoph Wille as an Open Source alternative. Best of all (depending on your perspective of course) there is an add-in for X# developed by Fabrice Foray from the X# developer team. But you have to download the version of the add-in for the appropriate version of ILSpy.

Step 1: Download ILSpy from https://github.com/icsharpcode/ILSpy/releases and install it.

Step 2: Download the X# add in from https://www.xsharp.eu/itm-downloads?folder=general%252FTools

Step 3: Extract the zip file and copy the file ILSpy.XSharpLanguage.Plugin.dll into the application directory of the appropriate ILSpy version (Version 7 for example).

That's it.

Start ILSpy and load any (!) dll or exe file with managed code, choose a class or method and you can easily disassemble the IL Code to X# code (a big thanks to Fabrice for his excellent work). I have to admit that I had problems starting the X# add-in with ILSpy 7, but it worked with ILSpy 6.

ALT ILSpy disassembles ILCode to either C# or XSharp

Fig 4.4: ILSpy disassembles ILCode to either C# or XSharp

Of course, not every X# developer will be happy with this situation. Don't blame the developers at Microsoft and of course not the good developers of ILSpy. There are two solutions for me:

  1. Use an "Obfuscator" that obfuscates the name of variables etc. and makes it very hard to read the disassembled code. There are very good products available. Both commercial and free.

  2. Use .Net 7 or above instead of the .Net Framework since with this runtime it's possible to compile source code directly into machine code. But you will have to wait for X# 3.0.