Scconfig developer documentation
Scconfig is a Simple C Configuration tool, a minimalistic
Instead of depending on shell scripts, scconfig is entirely
implemented in ANSI C (C89), thus it requires only a standard
C compiler to run. The reason for this is that writing complex
shell scripts (which are self-contained, so one doesn't need a
full featured system with m4 and other extras to generate
configure.sh) is not very easy using only the very portable
subset of the shell.
Some exotic systems may still fail compiling and running scconfig.
The rule of thumb for those systems is simple: where scconfig fails,
most probably the application being configured would fail too.
Scconfig consists of the following main parts:
- a database
- simplified API for some file manipulation and compiling
- internal dependencies
- a file generator "template system"
1. The database
To keep things simple, scconfig implements a database that represents
detected data in key-value pairs, where keys are actually long and look
more like paths in a filesystem. Accessing the database is a simple
get(key) or put(key, value). For more information
abour the API please read db.txt , about the
standard key names tree.txt .
2. File manipulation and compiling
The strenght of shell scripting is running commands and looking at files
are much simpler than in C. To overcome this drawback, scconfig provides
an extremly simple API for the following common operations.
- determine the size of a file"
int file_size(const char *name);
- create a temporary file:
char *tempfile_new(const char *suffix);
char *tempfile_dump(const char *testcode, const char *suffix);
- load the content of a file into the memory:
char *load_file(const char *name);
- (and much more)
Compile and run operations:
- try to compile a C source from a file or from a memory buffer:
int compile_file(int logdepth, const char *fn_input, char **fn_output, const char *cc, const char *cflags, const char *ldflags);
int compile_code(int logdepth, const char *testcode, char **fn_output, const char *cc, const char *cflags, const char *ldflags);
- run a command and save standard output into a memory buffer:
int run(int logdepth, const char *cmd_, char **stdout_saved);
- run a shell command and save standard output into a memory buffer:
int run_shell(int logdepth, const char *cmd_, char **stdout_saved);
(shell is detected in an early phase)
- compile a C source from memory buffer, run it and save stdout in memory:
int compile_run(int logdepth, const char *testcode, const char *cc, const char *cflags, const char *ldflags, char **stdout_saved);
- list files in a directory by calling
void filelist(int logdepth, const char *dir, int *argc, char ***argv);
(result is stored in argc/argv, argv is allocated by filelist())
3. Internal dependencies
Most tests for detecting libs will require the C compiler to be detected first,
but sometimes libs need to be detected before other libs can be detected. To
solve this problem, each detection routine hardwires its own dependencies
and those dependencies are resolved in a recursive
manner, on demand. This means there is no fixed order of detections, but
just as with Makefiles, the software works out the minimum set of things
to be done before the real target can be reached. When writing a new
detection subroutine, adding a dependency for the C compiler is as easy
as inserting the following line in the C code:
require("cc/cc", logdepth, fatal);
where logdepth is an integer for indenting the log properly (recursion
may be confusing without indentation), fatal is either 0 or 1 indicating
whether failure of the dependency is a fatal condition.
Scconfig provides simple calls for dumping log messages in the central log
file. Further calls are provided to merge other log files into the central
log. In practice this means error messages and warnings of test compilations
end up in the log file, not in the standard error or standard output.
Instead, the standard error and output are reserved for informing the user
about the progress of configuration.
5. File generation
After detecting necessary libs, compilers, tools, everything is in a big
tree of key-value pairs. This is not directly
useful for the software we
are trying to configure. To let detected settings take effect, configure
systems usually create Makefiles, config.h files, shell scripts, etc.
Scconfig doesn't try to do all these itself, instead the user needs
to add the C code to write those files.
Scconfig helps generating files by providing an optional
minimalistic template system that allows the user to process template
files into output files. While processing, simple if-then-else constructions,
switches, regex substitutions, for loops (with lists), variables can be used.
Template files can include other template files (even conditionally) so
that a modular template system can be built. This option does not cost
any external dependency and the template language implementation is small
(less than 1600 sloc, or 50 kilobytes, in C).
6. Cross compilation
Some projects may want to crosscompile. This means the compilation is
done on a host system while the output of the process is for a
different target system. Scconfig fully supports this by splitting
the database tree between host and
target. When host and target are the same (not crosscompiling),
they are "symlinked" so no CPU or memory is wasted. For more details,
please read host_target.txt