This document describes the configuration system used by Compiler Explorer, which is based on .properties files that
follow a hierarchical structure with inheritance and specialized handling for various property types.
Compiler Explorer uses .properties files located in the etc/config/ directory for all its configuration needs. These
files follow a simple key-value format:
key=value
Comments are lines that start with a hash character, and are not processed:
# This is a comment
key=value # This is also a comment
Configuration files follow a specific naming pattern:
CATEGORY.ENVIRONMENT.properties
Where:
CATEGORYrepresents a configuration category (likec++,rust,compiler-explorer,aws)ENVIRONMENTrepresents where the configuration will be used (likedefaults,amazon,local)
For example:
c++.defaults.properties- Default C++ configurationc++.amazon.properties- C++ configuration for the Amazon environment (used in production)c++.local.properties- Local C++ configuration (ignored by git, for personal settings)
The system loads configuration files in a specific order, creating a cascade of settings. The actual hierarchy used for property resolution, from lowest to highest priority, is:
defaults- Base configuration that applies to all environments- Environment-specific settings (such as
dev,beta,staging,amazon) - Platform-specific environment settings (like
dev.linux,staging.darwin) - Platform-specific settings (like
linux,darwin,win32) - Host-specific settings (using the machine's hostname)
local- User-specific configuration that overrides all others (can be disabled with the--no-localflag)
For example, if you're running on a Linux machine named "myserver" in the "staging" environment, a property would be looked up in this order:
etc/config/category.defaults.propertiesetc/config/category.staging.propertiesetc/config/category.staging.linux.propertiesetc/config/category.linux.propertiesetc/config/category.myserver.propertiesetc/config/category.local.properties
This means if the same property is defined in multiple files, the one from the most specific environment will be used.
The hierarchy/cascade only works for top-level properties. Group properties (discussed below) do not inherit across
different environment files. That is, if you define a group property in c++.defaults.properties, but that group is
defined without that property in c++.amazon.properties, the property will not be carried forward to the amazon
environment.
While all properties are stored as strings in the files, the system automatically converts values to appropriate types
using the toProperty function:
-
Strings - The default type for all properties
compiler.clang.name=Clang -
Booleans - Values
true,yes,false,noare converted to boolean valuescompiler.clang.supportsBinary=true -
Numbers - Numeric strings are converted to integers or floats
compiler.clang.timeout=10 -
Special Version Properties - Properties ending with
.versionor.semverare never converted to numbers, even if they look like numbers. This preserves version formatting with leading zeros or other special characters:compiler.gcc123.version=9.0.0 # Remains the string "9.0.0" instead of becoming a number
Many properties in Compiler Explorer represent lists of values. These use specific separators:
-
Colon-separated lists (
:) - Most commonly used for lists of identifiers, compiler names, etc.compilers=gcc:clang:msvc -
Pipe-separated lists (
|) - Used for argument lists, particularly when options might contain colonscompiler.clang.demanglerArgs=-n|-C|--no-verbose
Compilers are configured using properties with the compiler.ID prefix:
compiler.gcc.name=GCC
compiler.gcc.exe=/usr/bin/gcc
compiler.gcc.options=-Wall
A key feature of the configuration system is the ability to define groups of compilers with shared settings using &
syntax:
compilers=&gcc:&clang:specific_compiler
group.gcc.compilers=gcc7:gcc8:gcc9
group.gcc.groupName=GCC
group.gcc.supportsBinary=true
group.clang.compilers=clang9:clang10
group.clang.groupName=Clang
group.clang.intelAsm=-mllvm --x86-asm-syntax=intel
In this example:
- The
compilerslist includes two groups (&gccand&clang) and one individual compiler - Each group defines its own list of compilers
- Properties set at the group level (like
supportsBinaryorintelAsm) are inherited by all compilers in that group
Groups can contain other groups by using the & syntax within a group's compilers list:
group.newer.compilers=&clang:&gcc
group.newer.groupName=Modern Compilers
Properties are inherited from groups to individual compilers. If both a group and a compiler define the same property, the compiler's value takes precedence. This allows for group-wide defaults with compiler-specific overrides. There are some notable exceptions to this rule: some unique-per-compiler properties are not inherited from groups, but we hope to fix this. See this GitHub issue.
Common configuration keys include:
| Key Name | Type | Description |
|---|---|---|
| name | String | Human-readable name displayed to users |
| exe | Path | Path to the executable |
| options | String | Default compiler options |
| compilerType | String | Refers to the handler class in lib/compilers/*.ts |
| intelAsm | String | Flags for Intel assembly syntax |
| supportsX | Boolean | Capability flags for various features |
| versionFlag | String | Flag to pass to compiler to get version |
| demanglerArgs | String | Arguments for the demangler (pipe-separated) |
| objdumperArgs | String | Arguments for the object dumper (pipe-separated) |
| instructionSet | String | Default instruction set for the compiler |
Some properties support variable substitution to make configuration more flexible. The most common variables are:
-
${exePath}: Replaced with the directory path of the compiler executable
ldPath=/opt/compiler-explorer/lib/|${exePath}/../lib/ -
${ceToolsPath}: Replaced with the path to the Compiler Explorer tools directory
demangler=${ceToolsPath}/demangler
Environment variables can be configured using a special format:
envVars=VAR1=value1:VAR2=value2
This is parsed and converted into environment variable key-value pairs that are passed to the compiler when it's executed.
Multiple types of path lists exist in the system, mainly using two separator styles:
-
Colon-separated: Used for general lists (compilers, versions, etc.)
compilers=gcc:clang:msvc -
Pipe-separated: Used for path lists and command arguments
ldPath=/path/to/lib1|/path/to/lib2 demanglerArgs=-n|-C|--no-verbose
Note that path-style properties often support variable substitution as shown above.
You can enable detailed debugging of property resolution by using the --prop-debug flag when starting Compiler
Explorer. This shows every property lookup, including where properties are being overridden and which configuration
source they come from.
Compiler Explorer supports defining remote compilers using a special syntax in the configuration:
compilers=local1:local2:remote@hostname:port
When a compiler ID contains the @ symbol followed by a hostname and port, it is interpreted as a remote compiler. This
allows running compilers on separate machines and accessing them through the main Compiler Explorer instance.
The system has built-in detection for "orphaned" properties - configurations for compilers or groups that are not referenced anywhere. This helps maintain configuration cleanliness by identifying unused configuration entries.
The configuration system is implemented primarily in the following files:
lib/properties.ts- Core implementation of the properties systemlib/properties.interfaces.ts- TypeScript interfaces for the properties systemlib/compiler-finder.ts- Handles compiler discovery and group expansionlib/utils.ts- Contains property value conversion functions
If you need to troubleshoot configuration issues, you can run Compiler Explorer with debug output:
make EXTRA_ARGS='--prop-debug' dev
This will show detailed logs about property resolution, including which properties are being overridden and from which source.