Skip to content

Documentation: The general

Ingwie Phoenix edited this page Apr 25, 2014 · 4 revisions

cDetect is a tool to help you configure projects - if on a larger scale, or just a small project. Whatever it is, cDetect can help you find out details about the host that your code is being compiled on.

To help you understand the API better, I will walk you thru a basic tutorial to construct a simple configure program. For this case, we are going to have a small project, but it has a little problem. We don't know, if malloc.h is located directly via <malloc.h> or via <malloc/mallocc.h>. Actually, the latter only seems to be the case on Apple...but we never know, so we want to make sure.

Let's start!

The basic setup

cDetect is very portable, as it can be placed basically everywhere you want. Its good practice though to copy the entire cdetect folder into your project. Let's assume the following project tree:

./
├── cdetect
│   ├── autoconf.c
│   ├── cdetect.c
│   └── chost.c
├── readme.md
└── test.c

test.c contains our code for the test project. Obviously, we are missing a configure code. We have two options to work with this:

  • We use the configure.sh/.bat scripts provided
  • We have an entry in our readme.md file to tell the client to first compile our code.
  • We provide a basic makefile to compile the configure code.

In the case of the Phoenix Engine, I have taken the second aporoach and embedded the compiling and configuring deep into the build process.

To now get started, we create a configure.c file first - with the following content:

#include "cdetect/cdetect.c"

int main(int argc, char** argv) {
	config_begin(); // 1
	
	// 2
	if(config_options(argc, argv)) {
		config_header_check("malloc.h") // 3
			|| config_header_check("malloc/malloc.h"); // 4
	}
	
	config_end(); // 5
}

Explanation

  1. We initialize the configuration process here.
  2. We hand cDetect the command line switches that it needs to parse options and things alike.
  3. The first check is for <malloc.h>.
  4. If the first check fails, then we check for <malloc/malloc.h>.
  5. The configuration is over, and we write the result to config.h - which looks like this:
/* Autogenerated by cDetect 0.2.2 -- http://cdetect.sourceforge.net/ */
#ifndef CDETECT_INCLUDE_GUARD_CONFIG_H
#define CDETECT_INCLUDE_GUARD_CONFIG_H

#define CDETECT_HEADER_MALLOC_MALLOC_H 1

#endif /* CDETECT_INCLUDE_GUARD_CONFIG_H */

Now, within test.c, you can look if either CDETECT_HEADER_MALLOC_H or CDETECT_HEADER_MALLOC_MALLOC_H is defined, to include the right one!

The checks

You obviously can do quite a bunch of checks. These are as follows:

  • config_header_check( header )

    • Checks for a given header.
  • config_function_check( function )

    • Check for a given function in the standard libraries. (I.e. no special LDFLAGS)
    • Example: config_function_check("vsprintf");
  • config_function_check_library( function, library )

    • Check for a function given in a function.
    • Example: config_function_check_library("pthread_create", "pthread");
  • config_type_check( type )

    • Lookup a type and define, if it exists.
    • Example: config_type_check("long long");
  • config_type_check_header( type, header )

    • Check if a type exists in a given header.
    • Example: config_type_check_header("uint16_t","inttypes.h");
  • config_tool_check( tool )

    • Check for a tool.
    • Example: config_tool_check("php");
  • config_cpu()

    • Returns a char*, that tells you what architecture this is running on.
  • config_kernel()

    • Determines the kernel.

Defining

  • config_macro_define( macro, value )

    • Defines a custom macro.
    • Example: config_macro_define("PROJECT_VERSION", "0.1");
  • config_tool_define( key, value )

    • Defines the existence of a tool.
    • AND creates a variable that can later be used when building files off templates - see the generation part later.
    • Example 1: config_tool_define("BISON","bison");
    • Example 2: config_tool_define("CFLAGS", "-O3 -g");
  • config_header_define( header )

    • Defines a header, adding the right macro to the resulting config.h
    • Example: config_header_define("d0p/d0p.h");

Caching and generating

  • config_build_register( from, to )

    • When config_end() is called, these files are rendered - from the template, to the result.
    • Example: config_build_register("Makefile.in", "Makefile");
    • !Note: Multiple calls are possible.
  • config_header_register( header )

    • Specify the exact output for the header.
    • Example: config_header_register("php_config.h");
  • config_cache_register( file )

    • Specify the place where the cache should be kept.
    • Example: config_cache_register(".cache");

Manual testing

  • config_compile_source( source, cflags )
    • Compiles a source and returns zero on failure and non-zero on success.
    • An example would be to test compile to check for the exact usage of a function, using the given result to then define a fitting macro.

Options

  • config_option_register( Long name, Short name, Standard, Default, Help )

    • Register an option to the help screen. You can later obtain the value with config_option_get( _name_ ).
    • Standard is applied to the option when it is not supplied by the user.
    • Default is being set to the options value, if the user specifies it without argument. (i.e. --foo)
    • If Standard AND Default are set to 0, then this option does not require an argument.
    • If Standard is not 0, but Default is, then the option's argument is mandatory.
    • If Standart is 0, but Default is not, then the option's argument is optional.
  • config_option_register_group( group )

    • Register a group to the help screen.
    • Example: config_option_register_group("cDetect Extra (cache/output control)");
  • config_work_directory_register( dir )

    • All files generated during the process and such, will be stored there. Make sure the folder exists!

Utilities

  • cdetect_bool_t config_equal( string 1, string 2 )

    • Compares two strings and returns a bool.
    • Returns either CDETECT_TRUE or CDETECT_FALSE.
    • Example: if(config_equal(config_option_get("enable-localjpeg"),"no") == CDETECT_TRUE) {...} else {...}
  • config_report( message )

    • Print a message
  • config_report_bool( message, bool )

    • Emit a "checking for $message... (yes/no)" message.
    • Second parameter is either 0 (false) or 1 (true).
    • Hint: (int)CDETECT_TRUE or (int)CDETECT_FALSE
    • Example: config_report_bool("OpenGL Framework",1);
  • config_report_string( message, string )

    • Instead of a bool, report an actual message.
  • config_abort()

    • Abort the configuration immediately.
  • config_copyright_notice( notice )

    • Obvious. o.o

Utilities from the internals

  • cdetect_bool_t cdetect_tool_exist( tool )

    • Only checks if a tool exists - does not define it, but returns a bool.
    • Example: if(cdetect_tool_exist("pkg-config") == CDETECT_FALSE) { config_report("pkg-config not found but required!") && config_abort() }
  • cdetect_bool_t cdetect_file_exist( filename )

    • Checks if a file is existing. Note, that this does NOT define any macro!
    • Example (actually used in PHP): if((int)cdetect_file_exist("/dev/arandom")) config_macro_define("HAVE_DEV_ARANDOM","1");

Autools compatibility.

In some cases, you have ran into the issue that a library you desire to use is only available in Autotools. What a same, really! But what if you wish to port it to cDetect? You can - and further, you also can get some autotools-like functions.

The autotools layer

Once you include "cdetect/autoconf.c", you will have the following API:

  • ac_register()

    • Register the autoconf layer - that includes changing the macro format, and the output to config.h. Call config_header_register() AFTER this.
  • ac_prefix()

    • Add a good amount of prefix variables to the list of defined variables/tools. If you then generate a makefile that has names like @prefix@ in it, it will be replaced by the respective option.
  • ac_init( name, version, bug report )

    • Begin your header with the typical info you would give Autoconf.
  • ac_path_prog( name, program, default program )

    • Find out if program is given in PATH, and if so define it as tool under name. If not found, the default program will be set as the value instead.
  • ac_prog_shell()

    • Find out the current shell and define it as SHELL.
  • ac_prog_egrep()

    • Determine grep -E or such.
  • ac_prog_ranlib()

    • Find ranlib.
  • ac_prog_awk()

    • Find awk.
  • ac_prog_install()

    • Determine the install program.
  • ac_prog_ln_s()

    • Determine the usage of ln -s and its existence.
  • ac_path_python( expected version )

    • Find python, and make sure it is the expected version.
  • ac_header_stdc()

    • Check for the standard C header files.
  • ac_header_time()

    • Check for the time.h file.
  • ac_header_stdbool()

    • Look for stdbool.h, or if _Bool is defined.
  • ac_header_resolv()

    • Find the resolv.h file and its dependencies.
  • ac_header_dirent()

    • Find the correct dir(...).h file.
  • ac_header_stat()

    • Find the stat.h header.
  • ac_header_mmap_anonymous()

    • Find the map.h file, and pick up if MAP_ANONYMOUS is useable.
  • ac_header_sys_wait()

    • Find the wait.h file
  • ac_type_pid_t() / ac_type_uid_t() / ac_type_off_t() / ac_type_size_t()

    • Find the pid_t/uid_t/off_t/size_t type, and the headers.
  • ac_func_alloca()

    • Find the alloca function.
  • ac_func_rand48()

    • Find the *rand48 function.
  • ac_c_inline()

    • Check for the inline keyword.
  • ac_c_bigendian()

    • Determine endianess.
    • Returns 0 on little endian, 1 on big endian.
  • ac_c_printf_a()

    • Check if printf supports the %a formatting specifier.

Embedding cDetect into your build!

After you work a lot on your project, you see that your file structure has grown, and you have randomly encountered that you needed to use GYP for your building - or Scons, whatever. But you notice that you are left without configuration, really. But you now have a solution.

Imagine you are working on extending the Chromium project with FeatureFoo. But FeatureFoo requires a little configuration. So what you could do within your FunctionFoo.gyp file could look like this:

{
	"targets": [
		{
			"target_name": "libbar_configure",
			"type": "executable",
			"sources": [
				"libbar/configure.c"
			]
		},{
			"target_name": "FeatureFoo",
			"type": "...", #Whatever it is, your choice
			"requires": ["libbar_configure"],
			"actions": [{
				"action_name": "cDetect",
				"command": ["libbar_configure", "--output=libbar/config.h", "--enable-baz"]
			}],
			"sources": [...]
		}
	]
}

As you notice, my GYP-fu is not good... ;). But if you use it, you will notice what I was meaning to do - and this is pretty general:

  • Compile the configure code.
  • Make your actual target depend on the existence of this.
  • Add a pre-build rule to run the program BEFORE building.
  • Build your target, now with a config.h within!

Credits

This here, is just a fork and a contribution to the project that I originaly found on SourceForge, while I was in need for having configuration abilities on the go inside my current build system. The developer has helped me out a lot with understanding how this works and such! You should also check out his other projects as well. He has a nice article about predefined compiler macros, that is very helpful for compile-time configuration!

Find him here: https://sourceforge.net/u/breese/profile/

Notes

This documentation is not complete yet, and everything is stuffed in one single page. Further and better examples will follow. For example, I will take apart my FLTK configure code to showcase many functions that cDetect offers.