Compiling scripts

XSharper requires no installation. Sometimes this is not good enough.

Imagine a situation when you wrote a script that needs to be used by someone else. It is an inconvenience to explain to users of your script all the prerequisites, from where to download what, and how to specify command line parameters, and alike. It can go wrong and it will.

It is also an inconvenience to distribute an MSI or setup.exe, that may require admin rights, will have to be launched, and then users will need to dive into c:\program files (x86)\your-application\something.bat (that will launch xsharper.exe yourscript.xsh) . Nor you want to distribute a bunch of DLLs and .config files for "XCopy deployment" which used to be a popular buzz-word when .NET was introduced.

Simple single .EXE , that just works, is the best option here. It's very simple with XSharper too.

For the purpose of demonstration, let's the hello.xsh script be

    <while maxLoops="3">
        <print>Hello, World</print>
    </while>

Compile script into .EXE

This operation is as simple as (hello.exe is actually optional and may be skipped)

C:\>xsharper hello.xsh //genexe hello.exe
Generating C# source code...
Compiling hello.exe...
Executable saved to hello.exe...

Now there is a single hello.exe, which can be run as

C:\>hello
Hello, World
Hello, World
Hello, World

//genexe produces a Windows console executable but a Windows executable may be produced instead. There will be no console output in this case though.

C:\>xsharper //genwinexe windir.exe //ref System.Windows.Forms /// "System.Windows.Forms.MessageBox.Show(`Hello. Your windows directory is `+c.GetEnv(`WinDir`))"

and that produces windir.exe, which if executed displays
Windir Screen shot

Generate C# code

What C# code does XSharper generate and compile?

R:\>xsharper hello.xsh //gencs hello.cs
Generating C# source code...
C# source code saved to hello.cs ...

In the produced file you would see something like

using System;
...

namespace Generated33c26cc8138342a98ec9f8d43ef8af14 {

    // Generated XSharper script class
    public class Hello
    {
        XS.Script _script;
        public XS.Script Script { get { return _script; } }
        
        public object Run(XS.ScriptContext context, IEnumerable<string> args)
        {
            context.Initialize(_script);
            return context.ExecuteScript(_script, args);
        }
        
        public Hello()
        {
            _script=new XS.Script((System.Diagnostics.Process.GetCurrentProcess().MainModule!=null)?System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName:null   ){
				Parameters = new List<XS.CommandLineParameter>() ,
				Items = new List<XS.IScriptAction> {
					new XS.While(){
						MaxCount = 3,
						Items = new List<XS.IScriptAction> {
							new XS.Print(){
								Value = @"Hello, World"
							}
						} 
					}
				} 
			};
        }
        #region -- Code snippets --
        
        #endregion -- Code snippets --
    }
}

You may control the name of the generated class and namespace via //namespace and //class parameters, which come handy if the generated code is included into another project.

Note that using the new C# 3.0 syntax makes the generated code relatively readable, yet it's possible to revert back to a less readable, but slightly faster, C# 2.0 notation with //forcenet20 parameter.

Producing complete C# code

//gencs parameter by default produces only script source code, without any boiler plate code. void static Main and all the whistles may be added by //main option, which is added automatically when .EXE files are produced.

It's also possible to produce full and complete source code, including all needed DLLs, icons, manifests and a batch file to build it:

C:\>xsharper hello.xsh //genexe //codeout c:\hello
Generating C# source code...
Compiling hello.exe...
Executable saved to hello.exe ...

C:\>dir c:\hello
 Volume in drive C has no label.
 Volume Serial Number is 4832-7B26

 Directory of c:\hello

01/09/2009  07:39 PM    <DIR>          .
01/09/2009  07:39 PM    <DIR>          ..
01/09/2009  07:39 PM               562 compile_hello.bat
01/09/2009  07:39 PM            46,839 hello.cs
01/09/2009  07:39 PM             1,496 manifest.xml
01/09/2009  07:39 PM             4,150 xsh.ico
01/09/2009  07:39 PM           378,880 XSharper.Core.dll
01/09/2009  07:39 PM           161,782 XSharper.Core.dll.gz
               6 File(s)        593,709 bytes
               2 Dir(s)  12,089,413,632 bytes free

compile_hello.bat will invoke C# compiler and produce the very same hello.exe .

Get script out of the code

Another not-so-hypothetical situation. Once upon a time you wrote a useful script hello.xsh that just works, which was compiled into to an executable.

Now, 18 months later, it does not work anymore because it has Hello, World hardcoded into it, yet now the missing exclamation sign was detected and Hello, World! must be there instead. Source code of the original utility is, well, somewhere. Long deleted from your hard-drive, perhaps it's on one of those backup discs of the late year, on that shelf, or may be the other, oh well...

Writing from scratch is possible but no volunteers. There is an option to use Reflector to decompile the code into a Visual Studio project, that can be then compiled and run. That's again something to download and install.

With XSharper there is a better way. Just run the hello.exe with //save:

C:\> hello.exe //save hello.cs
C:\>type hello.cs
<?xml version="1.0" encoding="utf-8"?>
<xsharper xmlns="http://www.xsharper.com/schemas/1.0">
  <while maxCount="3">
    <print>Hello, World</print>
  </while>
</xsharper>

Another use of the //save option is to provide a way out of too long command lines. For example, there is a fine command line piece that prints first 64 bytes of explorer.exe:

C:\>xsharper ///p "=.ToHexDump(.ReadBytes((.Expand('${% WINDIR %}\explorer.exe')),0,64),16,null,true)"
4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00  MZ?.........yy..
B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00  ?.......@.......
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00 00 00 00 00 00 00 00 00 00 00 00 E0 00 00 00  ............a...

Now it needs to be converted into a more generic program that accepts a command-line parameter with the filename to be dumped. Instead of writing everything from scratch, the script may be saved into a file:

C:>xsharper //save dumpheader.xsh ///p "=.ToHexDump(.ReadBytes((.Expand('${% WINDIR %}\explorer.exe')),0,64),16,null,true)"
Saving script to dumpheader.xsh ...
Script file dumpheader.xsh saved...

And now it can be easily changed to use a command line parameter instead:

<?xml version="1.0" encoding="utf-8"?>
<xsharper engineVersion="0.9.1057.0" netVersion="3.5" id="generated" xmlns="http://www.xsharper.com/schemas/1.0">
  <param name="filename" required="true">Filename</param>
  <eval>c.WriteLine(.ToHexDump(.ReadBytes($filename,0,64),16,null,true))</eval>
</xsharper>