Overview
hbcxx uses the Unix #!/path/to/interpreter
technique to make C++ (and C) source
code directly executable.
At the technical level hbcxx is a build system that automatically launches the resulting executable but, although this is strictly true, this is probably not the best way to look at it.
Quoting Bjarne Stroustrup: Surprisingly, C++11 feels like a new language
[http://www.stroustrup.com/C++11FAQ.html#think]
. Considering its
source it is not at all surprising that this quote is absolutely on the money:
modern C++ does feel like another language. This is not
because the language has been changed massively but because the new
features encourage a different, and slightly higher level way to think
about writing C++. It’s faster and more fun, supports lambdas, has
tools to simplify memory management and includes regular expressions
out-of-the-box.
Think of hbcxx as a tool to keep things fast and fun by putting off the moment you have to write a build system and install script. For simple programs, especially for quick and dirty personal toys, the day you have to write a proper build system may never come.
Instead just copy your C++ source code into $HOME/bin
. Try it. It works.
Features
-
Automatically uses ccache to reduce program startup times (for build avoidance).
-
Enables -std=c++11 by default.
-
Parses
#include
directives to automatically discover and compile other source code files. -
Recognises the inclusion of boost header files and, where needed automatically links the relevant boost library.
-
pkg-config integration.
-
Direct access to underlying compiler flags (
-O3
,-fsanitize=address
,-g
). -
Honours the CXX environemnt variable to ensure clean integration with tools such as clang-analyzer’s
scan-build
.
License
hbcxx is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but without any warranty
; without even the implied warranty of
merchantability
or fitness for a particular purpose
. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ .
Installation
Where to get hbcxx
hbcxx is primarily hosted by sourceforge.net and releases can be downloaded from: https://sourceforge.net/projects/hbcxx/files/ .
Alternatively, if you prefer to acquire hbcxx using git then try:
git clone git://git.code.sf.net/p/hbcxx/code hbcxx
Note
|
If you prefer githib to sourceforge then there is an official mirror, https://github.com/daniel-thompson/hbcxx.git , that you can use to acquire hbcxx. Pull requests can be shared with the maintainer using both sourceforge and github. |
Prerequisites
hbcxx requires a recent C++11 compiler (GCC 4.8 or later is recommended) and both boost::filesystem and boost::regex.
Note
|
std::regex is included in C++11 but because is not yet supported by libstdc++ 4.8.x hbcxx uses the boost library instead. Similarly std::filesystem is expected to be included in C++14. Once these libraries are fully implemented in both GCC and clang’s C++ libraries then the boost dependency may be relaxed. |
It is also very strongly recommended that boost, ccache and pkg-config be installed since this provides hbcxx with a performant, batteries-included runtime environment suitable for writing portable scripts.
The prerequisites can be met on Fedora 19 or later with the following command:
yum install gcc libstdc++-devel boost-devel ccache pkgconfig
The prerequisites can be met on Debian 8 (Jessie) or later (and Ubuntu 14.04 or later) with the following command:
apt-get install gcc-c++ libstdc++-dev boost-all-dev ccache pkgconifg
The command line above should also apply to Ubuntu 14.04 or later.
Note
|
When packaging hbcxx all of the above packages should be listed as mandatory requirements. This includes packaging systems, such as dpkg, that allow packages to be recommended rather than mandatory. |
Build and install
Having installed the prerequisite packages hbcxx can be installed into /usr/local using the following commands:
./configure
make
make install
To customise the installation process run ./configure --help
and
select the desired options.
Note
|
If you have downloaded hbcxx via git then the configure script
must be generated using autoreconf -iv before building. Doing so
requires autoconf 2.69 or later and automake 1.12 or later. |
Invocation
hbcxx treats its first non-flag argument as the source file that it must compile and link. This is typically the source file that contain the main() function.
Any flags that appear before this source file will automatically be appended to the compiler and linker flags. Any arguments that appear after the source file are passed to the executable when it is launched.
For example the following command will perform a compile main.cpp with level 2 optimizaton, then it will link and run the executable, passing the argument --help to the executable.
hbcxx -O2 main.cpp --help
Typically however hbcxx will be invoked automatically by the program loader due to an interpreter directive on the first line of the source file. For example, main.cpp might commence with the following line:
#!/usr/bin/env hbcxx -O2
If main.cpp is executable then the following invocation is identical to our
original example (because /usr/bin/env
uses the same PATH lookup as the
command shell):
./main.cpp --help
hbcxx arguments
Arguments that commence --hbcxx-
are intercepted by hbcxx wherever they appear
in the argument list regardless of whether they appear before or after the
supplied source file. These arguments are not passed to the resulting
executable, instead these arguments can be used to trigger useful
diagnostic features.
For example main.cpp
, as described above, can be passed hash bang
arguments in the following way (each of which is equivalent):
hbcxx -O2 --hbcxx-verbose main.cpp --help
hbcxx -O2 main.cpp --hbcxx-verbose --help
./main.cpp --hbcxx-verbose --help
./main.cpp --help --hbcxx-verbose
The following hash bang arguments may be supplied.
--hbcxx-version
Show hbcxx version information and exit.
--hbcxx-verbose
Build in verbose mode showing the command line of all compiler and linker invocations.
--hbcxx-executable=<filename>
Compile and link the executable, storing the result as <filename>. Unlike other invocations the executable will not be launched automatically. This option allows a traditional executable to be built and shared with others.
--hbcxx-save-temps
Retain all temporary files created by hbcxx Typically this option should be combined with --hbcxx-verbose in order to discover the file names used for temporaries.
--hbcxx-debugger=<debugger>
Launch the executable inside a symbolic debugger. If the debugger is a supported debugger then the executable will be run using the arguments supplied on the command line (as normal) until it hits a breakpoint on the main() function. For other debuggers hbcxx will use the shell to execute the following command and all other arguments will be disregarded:
<debugger> <executable>
Currently the only supported debuggers are gdb and valgrind.
Note
|
When an debugger is selected the -g flag will automatically be appended to the compiler and linker flags but the optimization level will not be affected. |
--hbcxx-Ox
Forcibly alter the optimization level by adding -Ox after all other flags. This is typically used to forcibly disable optimization to make symbolic debugging easier.
Include file handling
hbcxx parses #include directives that appear in the source code. This feature is primarily used to locate other source files that must be compiled and linked. It is also used to recognise the inclusion of boost header files and automatically add the boost libraries to the link.
Any quoted #include
directive will cause hbcxx to search for source files with the
same name as the header file and, if one is found it will be compiled and linked.
For example, #include "libalpha/AlphaManager.h"
causes hbcxx to search for the
following files (relative to the source file in which the #include appears):
-
libalpha/AlphaManager.cpp
-
libalpha/AlphaManager.c
++ -
libalpha/AlphaManager.C
-
libalpha/AlphaManager.cc
-
libalpha/AlphaManager.c
Similar a bracketed include directive is checked against an internal list of
header files that imply linker options. For example the following line causes
-lboost_filesystem
and its dependancies to be added to the link line:
#include <boost/filesystem.hpp>
Hash bang directives
hbcxx uses specially formatted comments to direct the build process. These comments have the form:
//#! <directive>
Note
|
The whitespace between //#! and the <directive> is optional. |
The directive can appear anywhere on a line and like all double slash comments
extend to the end of the line. Hash bang directives are parsed before
C pre-processing. This means hash bang directives cannot be influenced
by #if 0
or any other C pre-processor conditional behaviour.
Similarly hash bang directives that appear in header files will be ignored.
For example to following line will include jack.h (through normal
operation of the C preprocessor) and also contains a hash bang
directive that directs hbcxx to use pkg-config
to lookup the compiler
and linker arguments needed by the jack package:
#include <jack.h> //!# requires: jack
Additionally hbcxx will convert any line that commences with the hash bang sequence into a hash bang directive by inserting a double slash to convert it into a comment. This ensures that if the first line of the compilation unit is a Unix style interpreter directive then it will be converted into standard C++ that can be passed to the compiler.
As an example, hbcxx will treat the following two lines identically (but a Unix-like program loader will only understand the first form):
#!/usr/bin/hbcxx
//#!/usr/bin/hbcxx
Interpreter directive
Interpreter directives are will typically follow one of the following
forms (shown here without the optional leading //
):
#!<path-to-hbcxx> <args>...
#!/usr/bin/env hbcxx <args>...
The first form is direct execution of hbcxx using the absolute path of
the hbcxx command, whilst the other indirectly executes hbcxx using the
env
command to determine the correct path.
Note
|
Using /usr/bin/env to launch hbcxx is strongly recommended. Using
/usr/bin/env increases script portability because the script need not
know the absolute path to hbcxx (which may differ between sites). |
Interpreter directives do not influence the behaviour hbcxx at all. However hbcxx may issue warnings if the interpreter directive fails basic sanity testing (for example if the first token on the line is not an absolute path to an executable).
Raw flag directives
Raw flag directives are used to provide additional command line flags for the compiler and/or linker and are of the following form:
//#! <flags>...
Note
|
The first flag must commence with a hyphen otherwise the directive will not be recognised as a raw flag directive. |
Examples:
// This program must run as fast as possible (but we don't need
// strict IEEE maths).
//#! -O3 -ffast-math
// Glue for some heavily autoconf'ed code
//#! -DHAVE_SNPRINTF=1
// Regretably libfoo does not provide pkg-config support so we must
// use direct linkage
#include <libfoo/foobar.h> //#! -lfoo
Raw flags are collected from and applied to all source files processed by hbcxx both the single file supplied on the command line and any subsequently added through auto-discovery or using the source directive.
Private flag directives
Private flag directives are similar to raw flag directives but only influence the compilation unit in which they appear.
//#! private: <flags>...
Private flag directives are comparatively rare because C++ build systems are typically configured to supply the same flags to all compilation units. However one common use is to indicate specific compilation units that should receive special optimization effort because they are where the program spends most of its time. This can yield a good trade off between initial program launch time (-O0 compiles much more quickly then -O3) and program execution.
Requires directives
Requires directives provide support for pkg-config packages and have the following forms:
//#! requires: <pkgname>...
//#! requires: <pkgname> [<=, ==, =>] <version>
The first form, without any version number, causes hbcxx to lookup the
--cflags
and --libs
requires to compile and link programs that use
<pkgname>
using pkg-config.
The second form performs all the actions of the first form but additionally checks that the version number of the package meets the specified constraint.
The two forms can be space seperated and intermixed within a single requires directive.
Examples:
//#! requires: jack
//#! requires: gtk+-3.0 >= 3.10
//#! requires: foo >= 2.0 bar teepipe <= 1.9.99
Source directives
Source directives are used to specify additional source files that must be compiled and linked into the executable and have the following form:
//#! source: <filename>...
Each filename supplied using source directives will be included in the list of files to be compiled. If the file is already known to hbcxx it is ignored making it safe for cycles to exist between source file (if is safe for a.cpp to source b.cpp even if b.cpp also sources a.cpp).
Source directives should be used when auto-discovery by #include
parsing does
not work.
Examples:
// foo.h and foo.cpp are not in the same directory
#include "foo.h" //#! source: src/foo.cpp
// bar.h requires multiple files to be compiled
#include "bar.h" //#! source: src/iron_bar.cpp src/steel_bar.cpp
Unusual directives
The directives introduced in this section are less commonly used than the other hash bang directives. Most are used only for specialist needs and beginner users will seldom need to use them.
//#! cxx: <compiler>
The cxx directive is used to specify that the software should be built
using an alternative compiler driver, such as clang. This is only
useful on distributions where there is more than one C
compiler
installed. In other circumstances the automatically selected compiler
is likely to be the best choice already.
Unsupported directives
Any unsupported directive will cause hbcxx to report an error and exit. The file that causes the error will not be passed to the compiler nor will the executable be linked or run.
Bugs and missing features
-
Compiler auto-detection does not cache its results in
$HOME/.hbcxx
(penalizing startup time). -
Executables get the wrong value passed as argument 0 when run using --hbcxx-debugger. They are passed the name of the linked executable rather than the underlying source file.
-
The hash bang directive prefix string,
//!#
, cannot appear in the source for any purpose other than parsing by hbcxx (including within strings). -
Local headers (those included using double quotes and checked for related source files, see Include file handling, above) are not currently pre-pre-processed to detect other related source files.
-
Can only generate wholly dynamic (default) or wholly static (
hbcxx -static <filename>
) executables (wholly static executables are strongly discouraged by the GNU glibc maintainers). The capacity to generate mostly static executables where unusual libraries are statically linked would be very helpful.