This tip works with LXR release 1.0 or higher because precise control on the rules application order required a redesign of the 'maps' feature. With older releases, the header layout will be awfully messy and the result is not guaranteed (it depends on key retrieval order which may vary from execution to execution according to Perl documentation). To fully understand this tip, read first how to set include paths, how to define conditional variables and how to dynamically remap file paths.

Standard include links

The Linux kernel has been ported on many computer architectures. However, there exists a single source tree encompassing both all possible "features" and "machines".

Here, the word feature is used to mean the available "services", such as ACPI, I/O, network, memory management, …

To be manageable, the source tree is organized in smaller directories, eventually containing sub-directories to keep things tidy:

The include/ directory itself has a similar organisation with many sub-directories named after the feature. Featureless definitions are stored in the linux/ sub-directory.

A typical source file begins like:

#include <linux/capability.h> #include <linux/file.h> #include <linux/times.h> #include <net/sock.h>

As can be seen, the include/ directory is implicit. Its location is passed to the compiler through an option. We need then to mimic this to enable hyperlink construction under the #include directives. This is possible with an 'incprefix' parameter in lxr.conf:

, 'incprefix' => [ '/include' ]

Architecture links

Architecture-specific parts are stored in the arch/ directory in their own sub-directories (alpha/, arm/, powerpc/, x86/, …). But these parts are not directly #included by the architecture-independent code. Instead, the directive is written #include <asm/...> as in the following example:

#include <linux/mount.h> #include <asm/uaccess.h> #include <asm/div64.h>

The abstract asm/ directory is mapped to a real architecture-directory by the configuration management system through an option specification given to the compiler. All architecture-directories contain an include/asm/ sub-directory where all .h definitions are stored. If we are interested in the arm architecture, we can augment the above 'incprefix' to:

, 'incprefix' => [ '/include' , '/arch/arm/include' ]

But what if we want to compare the x86 version? Reconfiguring lxr.conf for a two-minute reading is not user-friendly.

Fortunately, we can use a very handy LXR feature: 'maps' with variable substitution.

First, we create a variable whose value will be the target architecture (in fact, the name of the architecture sub-directory). Let us call it 'a' for architecture:

, 'variables' => { 'v' => { … } # Version variable (mandatory) , 'a' => { 'name' => 'Architecture' , 'range' => [ qw(alpha arm powerpc x86) ] , 'default' => 'arm' } } At this stage, the 'range' content must be manually typed. We incur the risk of omitting an architecture or entering a non-existing one. Further below, a tip is given to automatically collect an up-to-date list.

When LXR is launched, this variable appears in the header area. Just select the target architecture and click the Change button to set the variable.

Next, the architecture 'incprefix' rule must be rewritten to reference a virtual architecture:

, 'incprefix' => [ '/include' , '/arch/%=ARCH=%/include' ] The name %=ARCH=% has been chosen in the hope it will never be a real directory name. If it is ever encountered, the name must be changed, otherwise strange things might happen with the next parameter.

Finally, a path rewrite rule is applied just before the test for file existence with the variable value as parameter:

, 'maps' => [ '^/arch/%=ARCH=%/' => '/arch/$a/' ]

Sub-architecture links

The above configuration parameters are satisfactory for the "classical" computer architectures, but clearly do not meet all requirements for the embedded architectures, such as arm. These present variants in processors, boards, peripheral chips, … which challenge an effective LXR configuration.

The known architectures with variants in the 3.x kernel are arm, avr32, blackfin, cris, mn10300 and um. Unfortunately, there is no automatic identification method.

Let us consider the arm case as an example. The Linux-arm implementation must cope with machine and platform variants. Two new variables are then created:

, 'variables' => { 'v' => { … } # Version variable (mandatory) , 'a' => { 'name' => 'Architecture' , 'range' => [ qw(alpha arm powerpc x86) ] , 'default' => 'arm' } , 'arm_mach' => { 'name' => 'ARM machine' , 'when' => '"$a" eq "arm"' , 'range' => [ qw(at91 bcmring clps711x davinci) ] # and many more } , 'arm_plat' => { 'name' => 'ARM platform' , 'when' => '"$a" eq "arm"' , 'range' => [ qw(iop mxc nomadik) ] # and some more too } } The 'when' clause is a 1.0 feature preventing variable display outside a defined context. Here, the variables appear on screen only when variable 'a' is equal to arm.

The machine-specific files are referenced with #include <mach/…> and they live in the arch/arm/mach-xxx/include/mach/ directory where xxx is one of the values defined in 'arm_mach''s 'range' (respectively 'arm_plat' for platform-specific files).

Leaving aside 'incprefix' for the moment, the following rules would do the job at first thought:

, 'maps' => [ '^/arch/arm/%=LEVEL2=%/' => '/arch/arm/mach-${arm_mach}/' , '^/arch/arm/%=LEVEL2=%/' => '/arch/arm/plat-${arm_plat}/' ]

THIS IS WRONG!

Because the effect of 'maps' rules is cumulative: after application of the first rule, %=LEVEL2=% has been replaced and, consequently, the second rule never matches any more since %=LEVEL2=% has disappeared.

This implies we must force two different include paths. The correct configuration is

, 'incprefix' => [ '/include' , '/arch/%=ARCH=%/include' , '/arch/%=ARCH=%/%=LVL2A=%/include' , '/arch/%=ARCH=%/%=LVL2B=%/include' ] , 'maps' => [ '^/arch/%=ARCH=%/' => '/arch/$a/' , '^/arch/arm/%=LVL2A=%/' => '/arch/arm/mach-${arm_mach}/' , '^/arch/arm/%=LVL2B=%/' => '/arch/arm/plat-${arm_plat}/' ]
CAVEAT! Not all machine/platform combinations are valid. If some #include directives are not hyperlinked, check if variables 'arm_mach' and 'arm_plat' describe an existing configuration.

Similar rules can be designed for the other architectures.

Automatic architecture enumeration

Starting with release 1.0, LXR ships with a script which collects the architecture names and their variants. Launch it from the LXR root directory as:

$ cd /LXR/root/directory $ ./scripts/kernel-grab-vars.sh /kernel/source/tree

Possible options are --help, --verbose, --erase and --suffix=s.

The script output is a collection of *_list.txt files in the custom.d/ directory suitable for automatic reading into the master configuration.

Putting it all together

The following parameters are recommended for Linux kernel browsing:

, 'variables' => { 'v' => { 'name' => 'Version' , 'range' => [ readfile('custom.d/version_list.txt') ] } , 'a' => { 'name' => 'Architecture' , 'range' => [ readfile('custom.d/arch_list.txt') ] , 'default' => 'x86' } , 'arm_mach' => { 'name' => 'ARM machine' , 'when' => '"$a" eq "arm"' , 'range' => [ readfile('custom.d/arm_mach_list.txt') ] } , 'arm_plat' => { 'name' => 'ARM platform' , 'when' => '"$a" eq "arm"' , 'range' => [ readfile('custom.d/arm_plat_list.txt') ] } , 'avr32_mach' => { 'name' => 'AVR32 machine' , 'when' => '"$a" eq "avr32"' , 'range' => [ readfile('custom.d/avr32_mach_list.txt') ] } , 'blackfin_mach' => { 'name' => 'Blackfin machine' , 'when' => '"$a" eq "blackfin"' , 'range' => [ readfile('custom.d/blackfin_mach_list.txt') ] } , 'cris_arch' => { 'name' => 'CRIS architecture' , 'when' => '"$a" eq "cris"' , 'range' => [ readfile('custom.d/cris_arch_list.txt') ] } , 'mn10300_proc' => { 'name' => 'MN10300 processor' , 'when' => '"$a" eq "mn10300"' , 'range' => [ readfile('custom.d/mn10300_proc_list.txt') ] } , 'mn10300_unit' => { 'name' => 'MN10300 unit' , 'when' => '"$a" eq "mn10300"' , 'range' => [ readfile('custom.d/mn10300_unit_list.txt') ] } , 'um_sys' => { 'name' => 'UM system' , 'when' => '"$a" eq "um"' } } , 'incprefix' => [ '/include' , '/arch/%=ARCH=%/include' , '/arch/%=ARCH=%/%=LVL2A=%/include' , '/arch/%=ARCH=%/%=LVL2B=%/include' ] , 'maps' => [ '^/arch/%=ARCH=%/' => '/arch/$a/' , '^/arch/arm/%=LVL2A=%/' => '/arch/arm/mach-${arm_mach}/' , '^/arch/arm/%=LVL2B=%/' => '/arch/arm/plat-${arm_plat}/' , '^/arch/avr32/%=LVL2A=%/' => '/arch/avr32/mach-${avr32_mach}/' , '^/arch/blackfin/%=LVL2A=%/' => '/arch/blackfin/mach-${blackfin_mach}/' , '^/arch/cris/%=LVL2A=%/' => '/arch/cris/arch-${cris_arch}/' , '^/arch/mn10300/%=LVL2A=%/' => '/arch/mn10300/proc-${mn10300_proc}/' , '^/arch/mn10300/%=LVL2B=%/' => '/arch/mn10300/unit-${mn10300_unit}/' , '^/arch/um/%=LVL2A=%/' => '/arch/um/arch-${um_sys}/' ] CAVEAT! To work around a Debian issue, use OS-absolute file paths in the readfile argument. This is taken care of by the configuration wizard, starting with release 2.0.3.

This can be automatically generated by the initial configuration script:

$ cd /LXR/root/directory $ ./scripts/configure-lxr.pl --add --conf-out=lxr.conf lxrkernel.conf

Use option --add if you already configured LXR for another tree. If you intend to use LXR against the kernel source only, the complete configuration process is done with:

$ ./scripts/configure-lxr.pl --conf-out=lxr.conf lxrkernel.conf

If you gave a --suffix=s to script kernel-grab-vars.sh, you must first manually change the default suffix to your custom suffix in this file.

Remember to run script initdb.sh to create the associated database:

$ ./custom.d/initdb.sh

Copy the configuration file to its final destination:

$ cp custom.d/lxr.conf lxr.conf

Everything is ready for genxref.