Managing Builds

Unlike Visual Studio, where projects generally produce a single output file, QuickSharp's build model is based on each source file producing a corresponding output file. For an application based on a single source file there is no difference but a complex, multi-file application can consist of many binary files.

QuickSharp is intended to give maximum flexibility in structuring projects and is designed to make the most of the component-based architecture of .NET. Dividing a project into multiple output files allows the QuickSharp build system to be optimized by allowing only changed files to be recompiled; a 'make' style build model is provided to accomplish this.

Despite this, QuickSharp can still create monolithic applications by simply changing the application's build rules; in the absence of project configuration files these rules are specified using 'embedded file options'.

Embedded file options

QuickSharp's build tool configuration allows compiler settings to be customised at a general level but occasionally it is necessary to provide options for a specific program. For example, it may be necessary to reference a library or include a resource in a program. Embedded file options allow builds to be configured on a program by program basis by 'embedding' the options directly in the source files.

Embedded options are specially formatted comments which are ignored by build tools but are recognized by QuickSharp as additional build instructions. There are four types of embedded options: compiler options, runtime options, dependencies and QuickSharp options.

Compiler options

Compiler options allow additional configuration information to be passed to the compiler (or any build tool) and are formatted as follows:

//$ options here

The '//$' indicates the start of a compiler option and must be included without any spaces between the opening comment and the '$'. The text following the '//$' will be passed to the compiler as part of a the build tool format string in place of the macro ${EMBEDDED_OPT}. Any number of compiler options can be included in a source file and will be passed in the order they appear. Typical uses for compiler options are to reference libraries, embed resources or include additional source files in the compilation.

//$/r:System.Data.SQLite.dll
//$/res:myapp.resources
//$ file2.cs file3.cs

To use compiler options with languages such as VB.NET simply include the option within a correctly formatted comment, for example:

'//$ myfile.vb

Runtime options

Runtime options allow runtime arguments to be passed to a program when run from within QuickSharp and follow a similar format to compiler options:

//@ arguments here

Unlike compiler options only one option string will be passed to the program; the first one found will be used. The text following the '//@' will be included in the build tool format string in place of the macro ${RUNTIME_OPT}.

Dependencies

Dependencies are files which must be compiled successfully before the current file can be compiled. A dependency is specified as follows:

//? filename

Only one dependency may be specified per line and the file name should refer to a file in the current workspace. Dependencies can be any file type supported by QuickSharp; each will be compiled using the currently selected compile tool for the file type.

QuickSharp will compile a dependency if the source file is more recent than its output file, or if the output file doesn't exist. To force an unconditional compile use the Compile All menu option.

Multiple dependencies can be specified and each will be compiled in order of appearance until all have been successfully compiled or an error occurs; compilation of the main source file will be halted if a dependency fails to build.

Dependencies are only processed for the main file, if any dependent files contain their own dependencies they will be ignored.

QuickSharp options

QuickSharp options allows the QuickSharp build process to be modified on a program by program basis. As before the option follows a comment-based syntax with '&' as the distinguishing character:

//& option_name [parameters...]

A number of QuickSharp options are available, each consisting of a keyword followed by any required parameters.

RunInOwnWindow

//& RunInOwnWindow

This causes the program to be run in a window of its own rather than within the QuickSharp output view. This is useful if the program requires user interaction, for example to provide input.

Build tasks

There are four build task keywords which allow Windows shell commands to be issued at various stages of the build process.

//& DoPreCompile shell_command

This allows a shell command to be issued before the compile stage of the build process. The text following the keyword is passed to the Windows shell and the output sent to the output view. This is useful for performing operating system tasks before the build starts such as copying or deleting files, running other tools and so on. Any number of pre-compile tasks can be included and each will be performed in the order they are found; should any tool exit with a non-zero return value, the task will be assumed to have failed and the build will be halted.

The remaining keywords behave the same way and allow commands to be issued at the post-compile, pre-run and post-run stages of the build:

//& DoPostCompile shell_command
//& DoPreRun shell_command
//& DoPostRun shell_command

Build task commands can use the following macros for common Windows locations:

${IDE_HOME}    the QuickSharp home directory (usually C:\Program Files\QuickSharp)
${USR_HOME}    the QuickSharp user data directory
${USR_DOCS}    the user's "My Documents" folder
${SYSTEM}      the Windows system directory (usually C:\WINDOWS\system32)
${PFILES}      the "Program Files" directory

To use these as part of a path append a '\' to the macro, for example:

${SYSTEM}\cmd.exe

Managing multi-file projects

QuickSharp doesn't require project or solution files to manage projects but includes two features that together can provide the benefits of a project system without requiring separate configuration files.

Embedded dependencies

The basic philosophy of QuickSharp is that each source file should act as its own build configuration file. Embedded options allow compiler settings, runtime arguments and even QuickSharp configuration options to be defined on a file-by-file basis; embedded compilation dependencies allow relationships between source files to be defined so that a given file can coordinate the compilation of any source files it depends on providing a simple 'make' facility for QuickSharp projects.

To build a multi-file project simply select the main program file (this will usually be the file containing the 'Main' method) and include dependency options for all the files it depends on. For example, if you have a main file ('Main.cs') and two libraries ('Lib1.cs' and 'Lib2.cs') you can include the following options in 'Main.cs':

//? Lib1.cs
//? Lib2.cs

Whenever 'Main.cs' is compiled, QuickSharp will first check that the dependencies are up to date and will recompile only those that aren't before compiling the main file itself. Perfoming a conditional build in this way reduces overall compile time by avoiding the need to compile unchanged files. To perform an unconditional build use the Tools menu Compile All option.

Pinning files

Selecting a main file in this way requires that it be the active file in the editor before starting the compilation. This can be a nuisance when working on other files in the project so QuickSharp provides a means to fix or 'pin' the main file. This allows it to be the target of the build commands without having to be the active file in the editor.

Pinned file screenshot

To pin a file select it from the toolbar menu next to the 'pin' button; the menu will list all the source files in the current workspace. Selecting a file will make it the current 'pinned' file but will not make it active until the pin button is clicked; this will cause it to become the active file. The build tools available from the tool menus will be those associated with the file type of the pinned file and all build operations selected while a file is pinned will act on the file. Unchecking the pin button will unpin the file and cause the build system to revert to using the currently active editor.

Creating monolithic applications

The build techniques presented so far assume the default model where each source file creates a corresponding output file. A complex application could consist of several source files and will therefore produce a corresponding number of binary files; this may be inappropriate for deployment where a single or smaller number of redistributable files may be preferred.

To create a single file output from multiple source files use embedded compiler options to include the source files in the compiler invocation. Select the main driver file in the project and add embedded options like this:

//$/out:ProgramName.exe
//$ Library1.cs Library2.cs
//$ OtherFile.cs AnotherFile.cs

Make sure the source file lists are the last embedded compiler options so that the they are correctly inserted into the compiler command. Note that multiple lines can be used as long as they appear together and no further compiler options follow. Note also that the name of the output file must be explicitly declared to prevent the compiler inferring the wrong name from the file list. Finally, there should be no dependency options for any of the files listed as this will cause them to be compiled twice; as individual files and also as part of the main build.

This approach allows multiple source files to be combined into a single output file in the style of Visual Studio but be aware that this will bypass the QuickSharp 'make' system and the entire source will be recompiled on every build.