Using GufBuild

WARNING: GufBuild is very new, and is still changing. This information may change without warning. Worse, this document could easily be out-of-date. Watch out.

GufBuild is a tool for building software written in C++. It is similar to Make, but with certain advantages and disadvantages. The main disadvantage is flexibility -- GufBuild is only designed to build C++ programs which are organized in a certain way. However, while GufBuild can do far less than Make can, what it can do it does much better than Make ever could.

Packages and Modules

GufBuild requires programs to be organized into packages and modules. A module includes a C++ source file and a header file which define a single class. The class may have helper classes embedded within it, but only one class should exist at the global scope. Both the source file and the header for a module must be named exactly the same as the class, including case. (Note to Windows users: Most OS's have case-sensitive filesystems. If you want your stuff to be portable, make sure to use the correct cases in your filenames.)

Packages contain modules and other packages. Physically, all of the contents of a package must be inside one directory. That directory must be named exactly the same as the package. Again, this is case-sensitive. Logically, all of the contents of a package must be within the same C++ namespace. This namespace must have the exact same name as the package.

The "executable" Property

GUF programs have no main() function. Instead, you declare a class derived from guf::GProcess. Then, you declare that module as "executable" in your build file. GufBuild will then add some information to the module when you compile it which will allow external programs to find and execute the module like a program. Additionally, if the module is compiled directly into an executable program, GufBuild will add to your program a main() function which will initialize GUF, allocate an instance of your class, and run it.

Of course, in order for GufBuild to generate such functions, it needs to know how to allocate an instance of your class. It assumes that the class has a static function New() which returns a smart pointer to a new instance of it. (This is the standard way to allocate objects in GUF.)

Packages can also be declared "executable". In this case, it must declare one of its modules as the "main" module. Then, that module will represent the package if the package is executed as a program.

Executables and Libraries

Any module or package can be compiled into either an executable or a libary. Of course, the module or package must be declared "executable" in order to be compiled into an executable, but no special properties are required to compile a library. Executables are regular programs -- you can run them directly. Libraries are mainly for use by other programs. GufBuild only supports building dynamic libraries. Static libraries, while useful, have too many little quirks and problems to use effectively in a cross-platform way.

public and private

Packages and modules can be declared "public" or "private" within their parent package. If a package is declared "private", then all of its contents are made private, no matter how they are declared, but if a package is "public", it can define whether its contents are public or private. After all properties are assigned, any module which ends up being "public" will have its header file placed in the "include" directory for the software. This is mainly useful for libraries, allowing external programs to include those headers and link against the library.

The Syntax

GufBuild parses package definition files to determine what to build. These files are written using a syntax very similar to C++. Packages and modules are declared like classes, with the keyword "package" or "module" in the place of the "class" keyword. For example:

package hello
{
   module HelloWorld {}
}

Note that no semicolon is required after the closing brace.

Everything which is declared between the open and close braces of a package is contained within the package. You can declare both packages and modules there. The above example declares the package "hello", containing the module "HelloWorld". This could be the beginnings of a "Hello World" program, but a couple more things are necessary.

To declare a module as executable, place the keyword "executable" between the braces, followed by a semicolon.

module HelloWorld {executable;}

To declare a package as executable, you must also specify the module to use as the main module. You do this by placing the "main" keyword infront of that module's definition.

package hello
{
   main module HelloWorld {executable;}
   executable;
}

The module given as the main module must be within the package, and cannot be nested within a sub-package.

Our Hello World example still won't compile, however, until we tell it to output an executable. We do this by declaring an executable, which is similar to declaring a package.

executable hello_world
{
   main hello;
}

This is placed after the package declaration. The "main" statement within the executable declaration indicates which package (or module) to compile into this executable. In this case, we specified the "hello" package.

Now our example will compile the Hello World program into an executable called "hello_world".

You can also specify packages (or modules) that are nested within other packages. In order to do this, you must specify the complete path using "::" as a separater. This is just like C++.

package pkg1
{
   package pkg2
   {
      module MainModule {executable;}
   }
}
executable myprogram
{
   main pkg1::pkg2::MainModule;
}

Libraries can be specified the same way as executables. Just use "library" in the place of the "executable" keyword.

library mylib
{
   main mypackage;
}

Everything in the main package (or module) will be compiled into the library. Of course, the main package does not have to have the "executable" flag in this case.

Often, one wishes to specify special flags to the compiler or linker when compiling a program. GufBuild allows you to do this on a per-executable and per-library basis. Simply use the "cflags" or "ldflags" keyword in your executable or library definition. Either keyword should be followed by a string which can contain any number of parameters, separated by spaces. The parameters specified with "cflags" will go to the compiler, and those specified with "ldflags" to the linker. You can use these keywords multiple times in the same executable or library. If you do, all the parameters from all of the declarations will be used. (Usually, that fact isn't very useful.)

package hello
{
   main module HelloWorld {executable;}
   executable;
}
executable hello_world
{
   main hello;
   cflags "-O2 -Wall";
   ldflags "-O";
}

GufBuild also lets you specify what files you want deleted when one of the clean commands is run. There are three clean commands: "clean", "distclean", and "cvsclean". "distclean" will delete everything "clean" deletes (along with its own stuff), and "cvsclean" will delete everything "distclean" deletes (along with even more stuff). All three commands will delete the "bin", "lib", "build", and "include" directories, but you must specify any additional files you want deleted. This is done by using the "clean" declaration.

clean level
{
   files
}

level must be one of: "normal", "dist", or "cvs", which indicates which clean command you are specifying files for. files is a list of files which should be deleted at that level. Each filename should be enclosed in quotes, and names should be separated by semicolons. Here is an example:

clean dist
{
   "config";
   "gufbuild.log";
}

This says that when either "distclean" or "cvsclean" is run, the files "config" and "gufbuild.log" are to be deleted.

Preprocessor

Package definition files parsed by GufBuild are run through a preprocessor before they are parsed. This means that you can use preprocessor directives to perform conditional compilation or to include files in other files. It also means that you can use C- and C++-style comments in your package files.

The recommended way to organize your package definition files is as follows: Place the definition for each package in a file in that package's directory. Then, include that file into the parent package using a #include directive. See the source code for GUF for a good example of this.

Intermediate and Output files

GufBuild keeps files as organized as possible. All source code must be placed in the directory "src" within the build directory. All intermediate files generated during a build are placed in the directory "build". Output executable files are placed in "bin" and libraries are placed in "lib". Public headers are placed in "include".

On Windows, libraries are exported in two parts. There is the .dll file which contains the executable code, and a .a file which programs may link with in order to use the library. The .dll is placed in the "bin" directory, as Windows requires that .dll's be either in your PATH or in the same directory as the executables that use them. The .a file is placed in "lib" as one would expect.

Running GufBuild

To run GufBuild from the command line, use the following syntax:

gufbuild [build|clean|cvsclean|distclean]

If you do not specify one of the possible parameters, "build" will be assumed. The commands work as follows:

  • build: This builds everything defined in the package definition file "Buildfile.gbld" in the current directory. If no such file is found, GufBuild prints an error and exits.
  • clean: This deletes all output and intermediate files generated by build. This will remove the directories "bin", "lib", "include", and "build" from the current directory, so don't put anything important in those directories! It will also delete any files specified by the buildfile.
  • distclean: Like clean, but this will additionally delete files specified by the buildfile to only be deleted on a distclean.
  • cvsclean: Like distclean, but this will additionally delete files specified by the buildfile to only be deleted on a cvsclean.

Imminent Changes

I don't have any imminent changes planned at this time.

Future Changes

These are ideas I have for future features of GufBuild:

  • Instead of having an "executable" attribute, simply have each executable object name its main module. Simpler that way.
  • Ability to build both source code and binary distributions of the software automatically. Binary distributions will include an installation script.
  • Cross-compilation support. Imagine being able to build binaries of your program for five different platforms all at once, on the same computer, without rebooting. Combined with GUF (the ultimate operating system abstraction layer), this is a multi-platform developer's dream-come-true, as well as Microsoft's worst nightmare.
  • Simple documentation generator, which basically just takes the source code header files, adds syntax highlighting and cross-referencing, and outputs it as HTML. (What more is really necessary? I find that more complex documentation generators are easily confused by my coding style.)