NAME
ExtUtils::CFeatureTest - Test a host for available C language features and libraries
SYNOPSIS
use "./inc";
use ExtUtils::CFeatureTest;
my $ftest= ExtUtils::CFeatureTest->new;
# Test if a header exists. If found, set macro HAVE_STDBOOL_H, and all
# compilation attempts below this will automatically include the header.
$ftest->header('stdbool.h');
# Compile and run the snippet of code, and set HAVE_BOOL if it succeeds.
$ftest->feature(HAVE_BOOL => 'bool x= true; return x? 0 : 1;');
# Compile and run the snippet of code, and try various permutations of
# headers and libs until it works. This one is required, so warn and
# exit if it isn't available.
$ftest->require_feature(HAVE_LIBSSL =>
'unsigned char buf[1]; return RAND_bytes(buf, 1) == 1? 0 : 1;',
{ h => 'openssl/rand.h', pkg_config => ['libressl'] },
{ h => 'openssl/rand.h', pkg_config => ['openssl'] },
{ h => 'openssl/rand.h', -l => [ 'ssl', 'crypto' ] });
# Export all the things we learned into a header to be included by all
# source units in the project.
$ftest->write_config_header('MyModule_config.h');
# Export the compiler flags into ExtUtils::Depends to be used and/or
# installed for other modules to use.
my $dep= ExtUtils::Depends->new('MyModule');
$ftest->export_deps($dep);
DESCRIPTION
This is a module for testing aspects the C compiler and available libraries prior to building an XS distribution. It borrows many ideas from ExtUtils::CChecker and Devel::CheckLib. The main difference is that instead of simply building a list of compiler flags, it builds an entire header file for you that helps remove boilerplate from your other C files.
For example, a traditional approach is to test for a C header like stdint.h, and if found define a preprocessor macro like HAVE_STDINT_H, and then in your source code you write
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
This results in a bulky list of -DHAVE_SOME_FEATURE options to your compiler, and also a lot of boilerplate within each C file.
This module eliminates the middleman by generating a header of its own with everything it learned from feature detection and the workarounds your Makefile.PL have added based on that knowledge. For the above example, if it finds stdint.h it adds the include statement directly to the generated header, saving you the #ifdef boilerplate and avoiding any commandline arguments for the compiler.
INTEGRATION
I strongly recommend copying CFeatureTest into your distribution as inc/ExtUtils/CFeatureTest.pm rather than installing a system-wide copy. This ensures that future changes to CFeatureTest don't break existing distributions. While I don't change APIs frivolously, I'm not committing to full back-compat until it reaches version 1.0.
To this end, CFeatureTest is a single file with no non-core dependencies (since perl 5.9.3 which added ExtUtils::CBuilder) so literally all you need to do is copy one file into your distribution as inc/ExtUtils/CFeatureTest.pm and add use lib "./inc" to the top of your Makefile.PL.
See the Makefile.PL of Crypt::SecretBuffer for a complete example.
CONSTRUCTOR
new
$test= ExtUtils::CFeatureTest->new(%attributes);
ATTRIBUTES
verbose
If true, emit diagnostics, including output from the compiler. The default comes from $ENV{EXTUTILS_CFEATURETEST_VERBOSE}. A future version might make this into an integer of "log levels".
emit_tty
If true, enable fancy colorized output. The default is based on whether STDOUT is a terminal. This also triggers for MSWin32 consoles, but the latest versions of the Windows console do support TTY color codes.
emit_unicode
If true, enable fancy unicode indicators in the output. The default is true if any locale environment variables contain 'utf-8'.
config_header_text
The C source code generated so far by the detection methods, including the "config_macros" and "config_local". The config source code is structured as
/* attribute config_includes */
#include <header1>
#include <header2>
...
/* attribute config_macros */
#define HAS_HEADER1
#define HAS_HEADER2
...
/* attribute config_local */
...
This structure ensures that system headers are included before the pollution from your local macros and other symbols.
config_includes
The C code of #include statements generated so far by the detection methods. Also any code you added with "append_config_includes".
config_include_set
A hashref where each key is a header name which has been added to config_includes. (The hashref is used as a cache, not a declaration that drives code generation)
config_pkg_set
A hashref of the pkg-config library names which have been added. Read-only. (The hashref is used as a cache, not a declaration that drives code generation)
config_macros
A hashref where each key is a C macro name and the value is the definition of the macro. You may modify this hashref to define or remove macros. These will always come after the text of config_includes so as not to pollute global namespace before system headers get included. If you wish to define a macro before the inclusion of system headers (such as _GNU_SOURCE or WIN32_LEAN_AND_MEAN) use "append_config_includes".
config_local
A string of custom C code to append following config_includes and config_macros. This is intended for code you want defined for your entire project but don't want defined until after all system headers are included. See "append_config_local".
cbuilder
An instance of ExtUtils::CBuilder (a core module in modern perls), lazy-built.
last_err
The exception generated by the last call to "compile_and_run", if any.
last_compile_output
The stdout/stderr generated by the last invocation of compiler and/or linker.
last_exec_output
The stdout/stderr generated by the last execution of a built executable.
include_dirs
An arrayref of directories to be passed to the compiler as -Ipath.
extra_compiler_flags
An arrayref of command line arguments to pass to the C compiler.
extra_linker_flags
An arrayref of command line arguments to pass to the C linker.
METHODS
compile_and_run
$bool= $ftest->compile_and_run($code, %options);
# %options:
# include_dirs => [ ... ],
# extra_compiler_flags => [ ... ],
# extra_linker_flags => [ ... ],
Attempt to compile and execute the specified C program text. The compiler will be given all include paths, compiler flags, and linker flags that have been detected so far, in addition to the ones that you pass to this method. $code must be the complete program; the accumulated configuration code in "config_header_text" is not automatically applied.
Returns boolean of whether it succeeded (meaning compile, link, and executable all exited with code 0). The compiler output is stored in attribute "last_compile_output", perl exceptions are stored in attribute "last_err", and output of the text executable is stored in "last_exec_output". Nothing is printed to stdout/stderr.
header
$ftest->header('some_header.h', @test_inc_paths);
Attempt to compile a simple C program that includes the named header. The first compilation attempt will use the existing include path, and if not found, it will try compilation again for each element of @test_inc_paths added to the include path until one succeeds.
If any attempt succeeds, append the #include directive to the "config_includes" attribute and define macro HAVE_SOME_HEADER_H in attribute "config_macros".
This means all future tests will automatically have this header loaded, if it exists.
Returns a boolean of whether it added the header.
require_header
Like "header", but warn+exit if it fails. i.e. the header is mandatory for the build.
feature
$bool= $ftest->feature(MACRO_NAME => $c_code_snippet,
{ # one possible set of options known to work
h => \@header_names,
include_dirs => \@paths,
extra_compiler_flags => \@commandline_options,
extra_linker_flags => \@commandline_options,
pkg_config => \@module_names,
},
{ # another possible set of options known to work
# using convenient aliases for the attributes above
h => $header, -I => $path, -D => $macro, -L => $path, -l => $lib
},
... # as many attempts as you want
);
This attempts to compile and execute $c_code_snippet. It attempts compilation once without any configuration changes, and then attempts again using each of a supplied list of configurations until one succeeds. You can specify the configurations using full attribute names, or with shorthand aliases that resemble the gcc command line flags.
Again, note that any compiler/linker flags are appended to any others that were previously detected (the attributes "include_dirs", "extra_compiler_flags", and "extra_linker_flags") and the generated source code automatically includes the "config_header_text" that CFeatureTest is in the process of building.
Also note that the pkg_config option attempts to load all of the @module_names and proceeds to attempt compilation if any of them were found.
require_feature
Like "feature", but warn+exit if it fails. i.e. the feature is mandatory for the build.
get_pkg_config
$bool= $ftest->get_pkg_config($package_name, \%options_out);
$bool= $ftest->get_pkg_config(\@package_names, \%options_out);
For a named package, retrieve the --cflags and --libs and store the values into %options_out. If the package is not installed or pkg-config executable is missing, this returns false. You can customize the pkg-config executable with $ENV{PKG_CONFIG}.
If you specify an array of names to check, all will be attempted, and success will be determined by whether any of them existed. This is intended for cases where you have one specific package in mind, but it may be available as an alternate name or divided into sub-modules depending on the OS distribution.
append_config_includes
$ftest->append_config_includes(@c_code);
Append custom lines of C code to the "config_includes" attribute. Each element of @c_code will be given a trailing newline if it lacks one.
append_config_local
$ftest->append_config_local(@c_code);
Append custom lines of C code to the "config_local" attribute. Each element of @c_code will be given a trailing newline if it lacks one.
write_config_header
$ftest->write_config_header($filename);
Write the contents of "config_header_text" to a file, also with a standard include-guard of
#ifndef FILENAME_H
#define FILENAME_H
...
#endif
You should choose a filename distinct to your project.
export_deps
$ftest->export_deps($extutils_depends_obj);
Export the include paths, compiler flags, and linker flags required for using this into the ExtUtils::Depends object.
SEE ALSO
AUTHOR
Michael Conrad <mike@nrdvana.net>
COPYRIGHT AND LICENSE
This software is copyright (c) 2025-2026 by Michael Conrad.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.