dcc is a C/C++ compiler driver that that adds, parallel,
dependency-based building to an underlying C or C++ compiler. dcc
adds many of the build-related functions of build tools such as make
to the compiler itself and allows build systems to not perform that
work.
dcc uses compiler generated dependency information, along with
hard-coded make-like rules, to determine if compilation or linking
is actually required. This allows dcc to avoid running commands if
they are not required - dcc only re-compiles, or re-links, when an
output file is out-of-date with respect to its inputs. Inputs include
not only the files used in the compilation, both sources and dependent
header files, but also the compilation options.
Unlike building with tools such as make users don't have to do
anything to get this behaviour. They can use dcc as if it were cc
and obtain automatic, parallel, dependency-based builds for free.
Moving dependency checking into the compiler driver simplifies build systems. Instead of using a build tool to do the work of correctly, and efficiently, building the program or library using knowledge of the language dependency model, we have the language's compiler take care of that doing that.
dcc adds a number of features to the compiler-driver model to
simplify the development process. For instance, dcc can uses files
to store compiler options which are used as dependencies in builds
allowing automatic re-compilation when build options change.
dcc has been used with a number of compilers and target OSes. dcc
can be used on UNIX-like OSes, e.g. macOS, FreeBSD, common Linux-based
OS distributions, and on Microsoft Windows.
dcc supports what it calls gcc style compilers, gcc, clang and
Intel's icc, and on Windows Microsoft's cl.exe can be used.
dcc is written in Go and obviously requires Go installed to build
(see golang.org). dcc uses only standard Go
packages and is trivially built using the go build command.
To install dcc to your Go $GOBIN use go install otherwise
simply copy the dcc executable to the desired bin directory.
dcc usage is similar to that of cc(1), gcc(1) and similar
compiler drivers,
$ dcc <option...> <pathname...>
Like cc et al dcc compiles source files to object files using the
options passed on the command line. If a -c is passed dcc stops
following compilation but if no -c option is supplied dcc runs the
linker to form an executable from the object files.
However, unlike cc et al dcc automatically generates and uses
dependency information and will only compile or link if an output
file needs to be re-created. This is entirely transparent to the
end-user. The effect being that re-compilation is far faster when
files are already up to date.
dcc can be used as a mostly drop in replacement for cc/c++(1) in
existing build systems. Doing so adds additional dependency checking
to builds. There is a difference in behaviour with respect to existing
compiler drivers that may affect results, dcc does not remove
object files when no -c switch is used. Most build systems however
invoke the compiler for each source file passing -c.
Although dcc is similar in usage to cc(1)1 et al, enough so to permit it to be used directly in its place, dcc` does behave
differently in certain situations.
Normally, without a -c option, cc compiles the source files,
generating object files, and runs the linker to link those object
files into an executable. It then removes the object files. dcc
does not remove the files.
dcc wraps the underlying compiler driver and passes it options to
have it output dependency information. dcc automatically determines
the names of the files to store this information and reads them when
re-compiling a file to obtain the dependencies.
When re-compiling a file dcc performs make-like dependency
checking to determine if compilation is actually required. If not,
dcc does nothing and exits as if it had compiled the file (note,
file modification times are not altered). Otherwise dcc runs the
compiler and lets it generate its output. Dependency generation and
checking is entirely transparent to the end-user and, dcc implements
additional checks on the libraries and other files used in the build.
The dcc command line consists of options for the underling compiler,
a number of dcc-specific options and filenames to be processed.
Options to the compiler are passed through unalterted. dcc does
recognize a number of options which control its behaviour or supply
dependency information (libraries).
These options apply to dcc itself and are not passed on to the
compiler,
- --help
Get help. - --version
Output the dcc version number and exit. - --debug
Enabledccdebug output. - --cpp
Compile source as C++ rather than C. - --force
Rebuild everything, ignore dependencies. - --quiet
Don't output the commands being executed. - --exe path
Compile and link an executable called path. - --dll path
Compile and create a shared library called path. - --lib path
Compile and create a static library, path. - -j_number_
Use number parallel compilations. - -objdir directory
Create object files in directory (passed to the underlying compiler but also used to defne where dcc writes files). - --write-compile-commands
Output acompile_commands.jsonfile to the same directory where object files are written. - --append-compile-commands
Append compilation commands to thecompile_commands.jsonfile in the same directory.
cc-style compiler drivers traditionally worked in two modes. They
either compiled source files to object files or did that and linked
the object files to form an executable (and removed the object files).
Shared libraries added options to have the linker create a shared
library but the overall structure is the same as for an executable.
dcc has options that make these uses more explicit and adds the
feature of having the compiler driver generate a static library to
round out the various use cases.
The dcc-specific --exe, --dll, --plugin and --lib options
are used to tell dcc what is being built and the name of the output
file.
The --exe option means "build an executable", --dll means "build a
dynamic, or shared, library", --plugin means build a shared library
to be used as a plugin (see below) and --lib means "build a static
library".
Some platfoms, e.g. macOS, make a distiction between dynamic libraries
and object files intended to be used as plugins, what macOS calls
bundles. To accomodate this dcc uses the idea of plugin to
refer to libraries meant to be loaded as plugins and dll to mean
dynamic libraries. On other platforms, Windows and ELF-based systems
such as Linux and FreeBSD, plugins are DLLs.
dcc determines the language being compiled, C or C++, using a number
of rules and uses the appropriate underlying C or C++ compiler. C++ is
selected if,
- the
dccprogram name ends with++, e.gdc++ - an input file uses a C++ extension
.cc,.cpp,.cxx - the
--cppswitch was supplied
The choice of lanugage affects the choice of options files (see below).
dcc uses dependency information generated by the compiler itself
and information inferred from the filenames and system environment.
With gcc-style compilers dcc uses the -MF and -MD options to
have the compiler output make-format dependencies to a file which
dcc reads on the next run. With Microsoft's cl.exe the compiler's
/showIncludes flag is used to output the names of included files
which are then scraped and used to create .d files used by
the next compilation.
Dependency files are stored in a .dcc.d directory that resides in
the same directory as the object file being created. The DCCDEPS
environment variable can be set to use a name other than .dcc.d for
this directory.
dcc can read compiler and linker options stored in files called
options files. Options files are simple text files that contain the
options that would normally be passed on the command line.
Unlike passing options on the the comand line options files allow options to be split across multiple lines and support '#'-based line comments. Options files are also treated as dependencies and when changed, which presumably means the options within the file have been change, cause recompilation. This helps ensure all files are built in the same way.
The names adopted for options files are derived from the typical macro names used with make(1) for the particular options,
CFLAGSC compiler options.CXXFLAGSC++ compiler options.LDFLAGSLinker options.LIBSLibraries and library paths.
Option files are looked for by searching the directory hierarchy towards the root for a file with the particular name, e.g CXXFLAGS.
Files are searched for either in the specific directory or within a
$DCCDIR directory within that directory. $DCCDIR defaults to .dcc
but can be override by the environment variable so we call it $DCCDIR
even though it is rarely changed from the default .dcc.
Looking for the files in a $DCCDIR directory is a quick hack to get
the files out of the current directory and perhaps in the future some
other method may be adopted (ha ha).
dcc uses a Go-style method to support platform-specific options.
When searching for an options file dcc first searches for platform
and architecture specific variants of the file. dcc forms a file
name extension using names for for the host's architecture and
operating system and appends that extension to the filename. If a file
with that name exists it is used in place of the unadorned filename.
E.g. when searching for the LIBS file on a 64-bit FreeBSD host
the following files will be searched for in order,
$DCCDIR/LIBS.freebsd_amd64$DCCDIR/LIBS.freebsd$DCCDIR/LIBS
The LIBS options file is used to define the libraries and library
directories used when linking programs and DLLs.
The LIBS options file behaves in a similar manner to the compiler
options and executables depend on the file and relink when it changes.
Lines starting with -l (elle) and -L (capital-elle) are special.
Any library name starting with -l has the -l removed allowing
users to use UNIX linker-style naming for familarity. libraries with
names starting with -L are the names of of library directories.
Options files may include other files using the !include directory
The !inherit directive is similar to include but inherits
options by automatically searching for a file with the same
name as the one in which the directive occurs. The search for
the file starts in the directory above that which contains
the file.
With no arguments !inherit directive for a file with the same name
as the file that includes the directive in a higher level directory.
With argument !inherit searches for a file with that name, or
the platform-specific version of it.
Options files may include conditional directives to conditonally define compiler and linker options, and for "LIBS" files, libraries.
As with !include conditional directives mimic the C pre-processor's
#ifdef and #ifndef but use environment variables in the place of
macros as with the C/C++ pre-processor.
Conditionals must start in the first column.
The !error directive allows options files to purposefully raise
errors. !error is useful with conditional sections to raise
raise errors if required environment variables are not defined.
Any text following the !error directive is reported as the error to
the user.
!includefilename!inherit[filename]!ifdefenvvar!ifndefenvvar!else!endif!error[ text ]
dcc is written in Go and uses only standard packages in its
implementation. dcc should build in any supported Go environment and
be trivially cross-buildable.
dcc itself supports the various Linux distribtions, the BSD's, MacOS
and mostly likely other UNIX systems that use gcc, clang or
similar.
dcc has not really been used in anger and I expect many changes if
it is used more extensively. There are many areas where I've just
hacked things in, e.g. frameworks on MacOS, which would be better
expressed in a more structured manner, i.e. more comprehensive
abstracted interfaces to the compiler and other tools to remove the
platform-specific conditiona.
The code has lots of comments. Many of them correct! The commenting style is the result of using Visual Studio Code and its Go package's default configuration which golints your code producing lots of annoying warnings about naming, comment style and so on. Rather than disabling the tool like a sensible person I appeased it and wrote the things it told me to write. That stopped it drawing little squiggles and annoying little icons everywhere.
dcc is released under the GPL, version 2. If you advance dcc, and
distribute, you must share the advancements. The reasoning being that
a utility such as dcc is infrastructure and we should share, and
advance, infrastructure so we all get ahead.
As per convention the license text is in the file LICENSE.
Using dcc in a project can vastly simplfy its build system. Instead
of implementing build rules via make or generating them via cmake
or autotools you can just use dcc. It takes care of the building
part.
A complete development Makefile for a simple program, with all
source files in one directory, can be as small as:
.PHONY: program clean
program:
dcc $(CFLAGS) *.c -o $@
clean:
rm -f program *.o
rm -rf .dcc.d
The program target builds everything using dcc. It is marked
marked phony as we rely on dcc to take care of things.
- CC (or $CCFILE)
Name of the C compiler. - CXX (or $CXXFILE)
Name of the C++ compiler. - CCFILE
Name of the file that names the C compiler. - CXXFILE
Name of the file that names the C++ compiler. - CFLAGSFILE
Name of the C options file. - CXXFLAGSFILE
Name of the C++ options files. - LDFLAGSFILE
Name of the linker options file. - LIBSFILE
Name of the linker LIBS file. - DCCDIR
Name of the.dccdirectory. - DEPSDIR
Name of the.dcc.ddependency file directory. - OBJDIR
Name of the object file directory. - NUMJOBS
Number of compilations to run in parallel.
Initial support for Microsoft compiler on Windows.
Use '!' as the options file directive prefix in place of '#'
Allow !inherit directives to define the, base, filename of
the file to be inherited
Allow environment variables to be used in conditionals in options files.
Add --plugin option and support for linking bundle files on macOS
Added C-preprocessor style conditional and #error directives to optons files.
Initial alpha version.