# SPDX-License-Identifier: Apache-2.0
# Copyright 2013-2024 Contributors to the The Meson project
# Copyright © 2024 Intel Corporation
from __future__ import annotations
from collections import OrderedDict
from itertools import chain
from functools import total_ordering
import argparse
from . mesonlib import (
HoldableObject ,
default_prefix ,
default_datadir ,
default_includedir ,
default_infodir ,
default_libdir ,
default_libexecdir ,
default_localedir ,
default_mandir ,
default_sbindir ,
default_sysconfdir ,
MesonException ,
listify_array_value ,
MachineChoice ,
)
from . import mlog
import typing as T
from typing import ItemsView
DEFAULT_YIELDING = False
# Can't bind this near the class method it seems, sadly.
_T = T . TypeVar ( ' _T ' )
backendlist = [ ' ninja ' , ' vs ' , ' vs2010 ' , ' vs2012 ' , ' vs2013 ' , ' vs2015 ' , ' vs2017 ' , ' vs2019 ' , ' vs2022 ' , ' xcode ' , ' none ' ]
genvslitelist = [ ' vs2022 ' ]
buildtypelist = [ ' plain ' , ' debug ' , ' debugoptimized ' , ' release ' , ' minsize ' , ' custom ' ]
# This is copied from coredata. There is no way to share this, because this
# is used in the OptionKey constructor, and the coredata lists are
# OptionKeys...
_BUILTIN_NAMES = {
' prefix ' ,
' bindir ' ,
' datadir ' ,
' includedir ' ,
' infodir ' ,
' libdir ' ,
' licensedir ' ,
' libexecdir ' ,
' localedir ' ,
' localstatedir ' ,
' mandir ' ,
' sbindir ' ,
' sharedstatedir ' ,
' sysconfdir ' ,
' auto_features ' ,
' backend ' ,
' buildtype ' ,
' debug ' ,
' default_library ' ,
' default_both_libraries ' ,
' errorlogs ' ,
' genvslite ' ,
' install_umask ' ,
' layout ' ,
' optimization ' ,
' prefer_static ' ,
' stdsplit ' ,
' strip ' ,
' unity ' ,
' unity_size ' ,
' warning_level ' ,
' werror ' ,
' wrap_mode ' ,
' force_fallback_for ' ,
' pkg_config_path ' ,
' cmake_prefix_path ' ,
' vsenv ' ,
}
@total_ordering
class OptionKey :
""" Represents an option key in the various option dictionaries.
This provides a flexible , powerful way to map option names from their
external form ( things like subproject : build . option ) to something that
internally easier to reason about and produce .
"""
__slots__ = [ ' name ' , ' subproject ' , ' machine ' , ' _hash ' ]
name : str
subproject : str
machine : MachineChoice
_hash : int
def __init__ ( self , name : str , subproject : str = ' ' ,
machine : MachineChoice = MachineChoice . HOST ) :
# the _type option to the constructor is kinda private. We want to be
# able tos ave the state and avoid the lookup function when
# pickling/unpickling, but we need to be able to calculate it when
# constructing a new OptionKey
object . __setattr__ ( self , ' name ' , name )
object . __setattr__ ( self , ' subproject ' , subproject )
object . __setattr__ ( self , ' machine ' , machine )
object . __setattr__ ( self , ' _hash ' , hash ( ( name , subproject , machine ) ) )
def __setattr__ ( self , key : str , value : T . Any ) - > None :
raise AttributeError ( ' OptionKey instances do not support mutation. ' )
def __getstate__ ( self ) - > T . Dict [ str , T . Any ] :
return {
' name ' : self . name ,
' subproject ' : self . subproject ,
' machine ' : self . machine ,
}
def __setstate__ ( self , state : T . Dict [ str , T . Any ] ) - > None :
""" De-serialize the state of a pickle.
This is very clever . __init__ is not a constructor , it ' s an
initializer , therefore it ' s safe to call more than once. We create a
state in the custom __getstate__ method , which is valid to pass
splatted to the initializer .
"""
# Mypy doesn't like this, because it's so clever.
self . __init__ ( * * state ) # type: ignore
def __hash__ ( self ) - > int :
return self . _hash
def _to_tuple ( self ) - > T . Tuple [ str , str , str , MachineChoice , str ] :
return ( self . subproject , self . machine , self . name )
def __eq__ ( self , other : object ) - > bool :
if isinstance ( other , OptionKey ) :
return self . _to_tuple ( ) == other . _to_tuple ( )
return NotImplemented
def __lt__ ( self , other : object ) - > bool :
if isinstance ( other , OptionKey ) :
return self . _to_tuple ( ) < other . _to_tuple ( )
return NotImplemented
def __str__ ( self ) - > str :
out = self . name
if self . machine is MachineChoice . BUILD :
out = f ' build. { out } '
if self . subproject :
out = f ' { self . subproject } : { out } '
return out
def __repr__ ( self ) - > str :
return f ' OptionKey( { self . name !r} , { self . subproject !r} , { self . machine !r} ) '
@classmethod
def from_string ( cls , raw : str ) - > ' OptionKey ' :
""" Parse the raw command line format into a three part tuple.
This takes strings like ` mysubproject : build . myoption ` and Creates an
OptionKey out of them .
"""
try :
subproject , raw2 = raw . split ( ' : ' )
except ValueError :
subproject , raw2 = ' ' , raw
for_machine = MachineChoice . HOST
try :
prefix , raw3 = raw2 . split ( ' . ' )
if prefix == ' build ' :
for_machine = MachineChoice . BUILD
else :
raw3 = raw2
except ValueError :
raw3 = raw2
opt = raw3
assert ' : ' not in opt
assert opt . count ( ' . ' ) < 2
return cls ( opt , subproject , for_machine )
def evolve ( self , name : T . Optional [ str ] = None , subproject : T . Optional [ str ] = None ,
machine : T . Optional [ MachineChoice ] = None ) - > ' OptionKey ' :
""" Create a new copy of this key, but with altered members.
For example :
>> > a = OptionKey ( ' foo ' , ' ' , MachineChoice . Host )
>> > b = OptionKey ( ' foo ' , ' bar ' , MachineChoice . Host )
>> > b == a . evolve ( subproject = ' bar ' )
True
"""
# We have to be a little clever with lang here, because lang is valid
# as None, for non-compiler options
return OptionKey (
name if name is not None else self . name ,
subproject if subproject is not None else self . subproject ,
machine if machine is not None else self . machine ,
)
def as_root ( self ) - > ' OptionKey ' :
""" Convenience method for key.evolve(subproject= ' ' ). """
return self . evolve ( subproject = ' ' )
def as_build ( self ) - > ' OptionKey ' :
""" Convenience method for key.evolve(machine=MachineChoice.BUILD). """
return self . evolve ( machine = MachineChoice . BUILD )
def as_host ( self ) - > ' OptionKey ' :
""" Convenience method for key.evolve(machine=MachineChoice.HOST). """
return self . evolve ( machine = MachineChoice . HOST )
def is_project_hack_for_optionsview ( self ) - > bool :
""" This method will be removed once we can delete OptionsView. """
import sys
sys . exit ( ' FATAL internal error. This should not make it into an actual release. File a bug. ' )
def has_module_prefix ( self ) - > bool :
return ' . ' in self . name
def get_module_prefix ( self ) - > T . Optional [ str ] :
if self . has_module_prefix ( ) :
return self . name . split ( ' . ' , 1 ) [ 0 ]
return None
def without_module_prefix ( self ) - > ' OptionKey ' :
if self . has_module_prefix ( ) :
newname = self . name . split ( ' . ' , 1 ) [ 1 ]
return self . evolve ( newname )
return self
class UserOption ( T . Generic [ _T ] , HoldableObject ) :
def __init__ ( self , name : str , description : str , choices : T . Optional [ T . Union [ str , T . List [ _T ] ] ] ,
yielding : bool ,
deprecated : T . Union [ bool , str , T . Dict [ str , str ] , T . List [ str ] ] = False ) :
super ( ) . __init__ ( )
self . name = name
self . choices = choices
self . description = description
if not isinstance ( yielding , bool ) :
raise MesonException ( ' Value of " yielding " must be a boolean. ' )
self . yielding = yielding
self . deprecated = deprecated
self . readonly = False
def listify ( self , value : T . Any ) - > T . List [ T . Any ] :
return [ value ]
def printable_value ( self ) - > T . Union [ str , int , bool , T . List [ T . Union [ str , int , bool ] ] ] :
assert isinstance ( self . value , ( str , int , bool , list ) )
return self . value
# Check that the input is a valid value and return the
# "cleaned" or "native" version. For example the Boolean
# option could take the string "true" and return True.
def validate_value ( self , value : T . Any ) - > _T :
raise RuntimeError ( ' Derived option class did not override validate_value. ' )
def set_value ( self , newvalue : T . Any ) - > bool :
oldvalue = getattr ( self , ' value ' , None )
self . value = self . validate_value ( newvalue )
return self . value != oldvalue
_U = T . TypeVar ( ' _U ' , bound = UserOption [ _T ] )
class UserStringOption ( UserOption [ str ] ) :
def __init__ ( self , name : str , description : str , value : T . Any , yielding : bool = DEFAULT_YIELDING ,
deprecated : T . Union [ bool , str , T . Dict [ str , str ] , T . List [ str ] ] = False ) :
super ( ) . __init__ ( name , description , None , yielding , deprecated )
self . set_value ( value )
def validate_value ( self , value : T . Any ) - > str :
if not isinstance ( value , str ) :
raise MesonException ( f ' The value of option " { self . name } " is " { value } " , which is not a string. ' )
return value
class UserBooleanOption ( UserOption [ bool ] ) :
def __init__ ( self , name : str , description : str , value : bool , yielding : bool = DEFAULT_YIELDING ,
deprecated : T . Union [ bool , str , T . Dict [ str , str ] , T . List [ str ] ] = False ) :
super ( ) . __init__ ( name , description , [ True , False ] , yielding , deprecated )
self . set_value ( value )
def __bool__ ( self ) - > bool :
return self . value
def validate_value ( self , value : T . Any ) - > bool :
if isinstance ( value , bool ) :
return value
if not isinstance ( value , str ) :
raise MesonException ( f ' Option " { self . name } " value { value } cannot be converted to a boolean ' )
if value . lower ( ) == ' true ' :
return True
if value . lower ( ) == ' false ' :
return False
raise MesonException ( f ' Option " { self . name } " value { value } is not boolean (true or false). ' )
class UserIntegerOption ( UserOption [ int ] ) :
def __init__ ( self , name : str , description : str , value : T . Any , yielding : bool = DEFAULT_YIELDING ,
deprecated : T . Union [ bool , str , T . Dict [ str , str ] , T . List [ str ] ] = False ) :
min_value , max_value , default_value = value
self . min_value = min_value
self . max_value = max_value
c : T . List [ str ] = [ ]
if min_value is not None :
c . append ( ' >= ' + str ( min_value ) )
if max_value is not None :
c . append ( ' <= ' + str ( max_value ) )
choices = ' , ' . join ( c )
super ( ) . __init__ ( name , description , choices , yielding , deprecated )
self . set_value ( default_value )
def validate_value ( self , value : T . Any ) - > int :
if isinstance ( value , str ) :
value = self . toint ( value )
if not isinstance ( value , int ) :
raise MesonException ( f ' Value { value !r} for option " { self . name } " is not an integer. ' )
if self . min_value is not None and value < self . min_value :
raise MesonException ( f ' Value { value } for option " { self . name } " is less than minimum value { self . min_value } . ' )
if self . max_value is not None and value > self . max_value :
raise MesonException ( f ' Value { value } for option " { self . name } " is more than maximum value { self . max_value } . ' )
return value
def toint ( self , valuestring : str ) - > int :
try :
return int ( valuestring )
except ValueError :
raise MesonException ( f ' Value string " { valuestring } " for option " { self . name } " is not convertible to an integer. ' )
class OctalInt ( int ) :
# NinjaBackend.get_user_option_args uses str() to converts it to a command line option
# UserUmaskOption.toint() uses int(str, 8) to convert it to an integer
# So we need to use oct instead of dec here if we do not want values to be misinterpreted.
def __str__ ( self ) - > str :
return oct ( int ( self ) )
class UserUmaskOption ( UserIntegerOption , UserOption [ T . Union [ str , OctalInt ] ] ) :
def __init__ ( self , name : str , description : str , value : T . Any , yielding : bool = DEFAULT_YIELDING ,
deprecated : T . Union [ bool , str , T . Dict [ str , str ] , T . List [ str ] ] = False ) :
super ( ) . __init__ ( name , description , ( 0 , 0o777 , value ) , yielding , deprecated )
self . choices = [ ' preserve ' , ' 0000-0777 ' ]
def printable_value ( self ) - > str :
if self . value == ' preserve ' :
return self . value
return format ( self . value , ' 04o ' )
def validate_value ( self , value : T . Any ) - > T . Union [ str , OctalInt ] :
if value == ' preserve ' :
return ' preserve '
return OctalInt ( super ( ) . validate_value ( value ) )
def toint ( self , valuestring : T . Union [ str , OctalInt ] ) - > int :
try :
return int ( valuestring , 8 )
except ValueError as e :
raise MesonException ( f ' Invalid mode for option " { self . name } " { e } ' )
class UserComboOption ( UserOption [ str ] ) :
def __init__ ( self , name : str , description : str , choices : T . List [ str ] , value : T . Any ,
yielding : bool = DEFAULT_YIELDING ,
deprecated : T . Union [ bool , str , T . Dict [ str , str ] , T . List [ str ] ] = False ) :
super ( ) . __init__ ( name , description , choices , yielding , deprecated )
if not isinstance ( self . choices , list ) :
raise MesonException ( f ' Combo choices for option " { self . name } " must be an array. ' )
for i in self . choices :
if not isinstance ( i , str ) :
raise MesonException ( f ' Combo choice elements for option " { self . name } " must be strings. ' )
self . set_value ( value )
def validate_value ( self , value : T . Any ) - > str :
if value not in self . choices :
if isinstance ( value , bool ) :
_type = ' boolean '
elif isinstance ( value , ( int , float ) ) :
_type = ' number '
else :
_type = ' string '
optionsstring = ' , ' . join ( [ f ' " { item } " ' for item in self . choices ] )
raise MesonException ( ' Value " {} " (of type " {} " ) for option " {} " is not one of the choices. '
' Possible choices are (as string): {} . ' . format (
value , _type , self . name , optionsstring ) )
return value
class UserArrayOption ( UserOption [ T . List [ str ] ] ) :
def __init__ ( self , name : str , description : str , value : T . Union [ str , T . List [ str ] ] ,
split_args : bool = False ,
allow_dups : bool = False , yielding : bool = DEFAULT_YIELDING ,
choices : T . Optional [ T . List [ str ] ] = None ,
deprecated : T . Union [ bool , str , T . Dict [ str , str ] , T . List [ str ] ] = False ) :
super ( ) . __init__ ( name , description , choices if choices is not None else [ ] , yielding , deprecated )
self . split_args = split_args
self . allow_dups = allow_dups
self . set_value ( value )
def listify ( self , value : T . Any ) - > T . List [ T . Any ] :
try :
return listify_array_value ( value , self . split_args )
except MesonException as e :
raise MesonException ( f ' error in option " { self . name } " : { e !s} ' )
def validate_value ( self , value : T . Union [ str , T . List [ str ] ] ) - > T . List [ str ] :
newvalue = self . listify ( value )
if not self . allow_dups and len ( set ( newvalue ) ) != len ( newvalue ) :
msg = ' Duplicated values in array option is deprecated. ' \
' This will become a hard error in meson 2.0. '
mlog . deprecation ( msg )
for i in newvalue :
if not isinstance ( i , str ) :
raise MesonException ( f ' String array element " { newvalue !s} " for option " { self . name } " is not a string. ' )
if self . choices :
bad = [ x for x in newvalue if x not in self . choices ]
if bad :
raise MesonException ( ' Value {} " {} " for option " {} " {} not in allowed choices: " {} " ' . format (
' ' if len ( bad ) == 1 else ' s ' ,
' , ' . join ( bad ) ,
self . name ,
' is ' if len ( bad ) == 1 else ' are ' ,
' , ' . join ( self . choices ) )
)
return newvalue
def extend_value ( self , value : T . Union [ str , T . List [ str ] ] ) - > None :
""" Extend the value with an additional value. """
new = self . validate_value ( value )
self . set_value ( self . value + new )
class UserFeatureOption ( UserComboOption ) :
static_choices = [ ' enabled ' , ' disabled ' , ' auto ' ]
def __init__ ( self , name : str , description : str , value : T . Any , yielding : bool = DEFAULT_YIELDING ,
deprecated : T . Union [ bool , str , T . Dict [ str , str ] , T . List [ str ] ] = False ) :
super ( ) . __init__ ( name , description , self . static_choices , value , yielding , deprecated )
self . name : T . Optional [ str ] = None # TODO: Refactor options to all store their name
def is_enabled ( self ) - > bool :
return self . value == ' enabled '
def is_disabled ( self ) - > bool :
return self . value == ' disabled '
def is_auto ( self ) - > bool :
return self . value == ' auto '
class UserStdOption ( UserComboOption ) :
'''
UserOption specific to c_std and cpp_std options . User can set a list of
STDs in preference order and it selects the first one supported by current
compiler .
For historical reasons , some compilers ( msvc ) allowed setting a GNU std and
silently fell back to C std . This is now deprecated . Projects that support
both GNU and MSVC compilers should set e . g . c_std = gnu11 , c11 .
This is not using self . deprecated mechanism we already have for project
options because we want to print a warning if ALL values are deprecated , not
if SOME values are deprecated .
'''
def __init__ ( self , lang : str , all_stds : T . List [ str ] ) - > None :
self . lang = lang . lower ( )
self . all_stds = [ ' none ' ] + all_stds
# Map a deprecated std to its replacement. e.g. gnu11 -> c11.
self . deprecated_stds : T . Dict [ str , str ] = { }
opt_name = ' cpp_std ' if lang == ' c++ ' else f ' { lang } _std '
super ( ) . __init__ ( opt_name , f ' { lang } language standard to use ' , [ ' none ' ] , ' none ' )
def set_versions ( self , versions : T . List [ str ] , gnu : bool = False , gnu_deprecated : bool = False ) - > None :
assert all ( std in self . all_stds for std in versions )
self . choices + = versions
if gnu :
gnu_stds_map = { f ' gnu { std [ 1 : ] } ' : std for std in versions }
if gnu_deprecated :
self . deprecated_stds . update ( gnu_stds_map )
else :
self . choices + = gnu_stds_map . keys ( )
def validate_value ( self , value : T . Union [ str , T . List [ str ] ] ) - > str :
try :
candidates = listify_array_value ( value )
except MesonException as e :
raise MesonException ( f ' error in option " { self . name } " : { e !s} ' )
unknown = ' , ' . join ( std for std in candidates if std not in self . all_stds )
if unknown :
raise MesonException ( f ' Unknown option " { self . name } " value { unknown } . Possible values are { self . all_stds } . ' )
# Check first if any of the candidates are not deprecated
for std in candidates :
if std in self . choices :
return std
# Fallback to a deprecated std if any
for std in candidates :
newstd = self . deprecated_stds . get ( std )
if newstd is not None :
mlog . deprecation (
f ' None of the values { candidates } are supported by the { self . lang } compiler. \n ' +
f ' However, the deprecated { std } std currently falls back to { newstd } . \n ' +
' This will be an error in meson 2.0. \n ' +
' If the project supports both GNU and MSVC compilers, a value such as \n ' +
' " c_std=gnu11,c11 " specifies that GNU is preferred but it can safely fallback to plain c11. ' )
return newstd
raise MesonException ( f ' None of values { candidates } are supported by the { self . lang . upper ( ) } compiler. ' +
f ' Possible values for option " { self . name } " are { self . choices } ' )
class BuiltinOption ( T . Generic [ _T , _U ] ) :
""" Class for a builtin option type.
There are some cases that are not fully supported yet .
"""
def __init__ ( self , opt_type : T . Type [ _U ] , description : str , default : T . Any , yielding : bool = True , * ,
choices : T . Any = None , readonly : bool = False ) :
self . opt_type = opt_type
self . description = description
self . default = default
self . choices = choices
self . yielding = yielding
self . readonly = readonly
def init_option ( self , name : ' OptionKey ' , value : T . Optional [ T . Any ] , prefix : str ) - > _U :
""" Create an instance of opt_type and return it. """
if value is None :
value = self . prefixed_default ( name , prefix )
keywords = { ' yielding ' : self . yielding , ' value ' : value }
if self . choices :
keywords [ ' choices ' ] = self . choices
o = self . opt_type ( name . name , self . description , * * keywords )
o . readonly = self . readonly
return o
def _argparse_action ( self ) - > T . Optional [ str ] :
# If the type is a boolean, the presence of the argument in --foo form
# is to enable it. Disabling happens by using -Dfoo=false, which is
# parsed under `args.projectoptions` and does not hit this codepath.
if isinstance ( self . default , bool ) :
return ' store_true '
return None
def _argparse_choices ( self ) - > T . Any :
if self . opt_type is UserBooleanOption :
return [ True , False ]
elif self . opt_type is UserFeatureOption :
return UserFeatureOption . static_choices
return self . choices
@staticmethod
def argparse_name_to_arg ( name : str ) - > str :
if name == ' warning_level ' :
return ' --warnlevel '
else :
return ' -- ' + name . replace ( ' _ ' , ' - ' )
def prefixed_default ( self , name : ' OptionKey ' , prefix : str = ' ' ) - > T . Any :
if self . opt_type in [ UserComboOption , UserIntegerOption ] :
return self . default
try :
return BUILTIN_DIR_NOPREFIX_OPTIONS [ name ] [ prefix ]
except KeyError :
pass
return self . default
def add_to_argparse ( self , name : str , parser : argparse . ArgumentParser , help_suffix : str ) - > None :
kwargs = OrderedDict ( )
c = self . _argparse_choices ( )
b = self . _argparse_action ( )
h = self . description
if not b :
h = ' {} (default: {} ). ' . format ( h . rstrip ( ' . ' ) , self . prefixed_default ( name ) )
else :
kwargs [ ' action ' ] = b
if c and not b :
kwargs [ ' choices ' ] = c
kwargs [ ' default ' ] = argparse . SUPPRESS
kwargs [ ' dest ' ] = name
cmdline_name = self . argparse_name_to_arg ( name )
parser . add_argument ( cmdline_name , help = h + help_suffix , * * kwargs )
# Update `docs/markdown/Builtin-options.md` after changing the options below
# Also update mesonlib._BUILTIN_NAMES. See the comment there for why this is required.
# Please also update completion scripts in $MESONSRC/data/shell-completions/
BUILTIN_DIR_OPTIONS : T . Dict [ ' OptionKey ' , ' BuiltinOption ' ] = OrderedDict ( [
( OptionKey ( ' prefix ' ) , BuiltinOption ( UserStringOption , ' Installation prefix ' , default_prefix ( ) ) ) ,
( OptionKey ( ' bindir ' ) , BuiltinOption ( UserStringOption , ' Executable directory ' , ' bin ' ) ) ,
( OptionKey ( ' datadir ' ) , BuiltinOption ( UserStringOption , ' Data file directory ' , default_datadir ( ) ) ) ,
( OptionKey ( ' includedir ' ) , BuiltinOption ( UserStringOption , ' Header file directory ' , default_includedir ( ) ) ) ,
( OptionKey ( ' infodir ' ) , BuiltinOption ( UserStringOption , ' Info page directory ' , default_infodir ( ) ) ) ,
( OptionKey ( ' libdir ' ) , BuiltinOption ( UserStringOption , ' Library directory ' , default_libdir ( ) ) ) ,
( OptionKey ( ' licensedir ' ) , BuiltinOption ( UserStringOption , ' Licenses directory ' , ' ' ) ) ,
( OptionKey ( ' libexecdir ' ) , BuiltinOption ( UserStringOption , ' Library executable directory ' , default_libexecdir ( ) ) ) ,
( OptionKey ( ' localedir ' ) , BuiltinOption ( UserStringOption , ' Locale data directory ' , default_localedir ( ) ) ) ,
( OptionKey ( ' localstatedir ' ) , BuiltinOption ( UserStringOption , ' Localstate data directory ' , ' var ' ) ) ,
( OptionKey ( ' mandir ' ) , BuiltinOption ( UserStringOption , ' Manual page directory ' , default_mandir ( ) ) ) ,
( OptionKey ( ' sbindir ' ) , BuiltinOption ( UserStringOption , ' System executable directory ' , default_sbindir ( ) ) ) ,
( OptionKey ( ' sharedstatedir ' ) , BuiltinOption ( UserStringOption , ' Architecture-independent data directory ' , ' com ' ) ) ,
( OptionKey ( ' sysconfdir ' ) , BuiltinOption ( UserStringOption , ' Sysconf data directory ' , default_sysconfdir ( ) ) ) ,
] )
BUILTIN_CORE_OPTIONS : T . Dict [ ' OptionKey ' , ' BuiltinOption ' ] = OrderedDict ( [
( OptionKey ( ' auto_features ' ) , BuiltinOption ( UserFeatureOption , " Override value of all ' auto ' features " , ' auto ' ) ) ,
( OptionKey ( ' backend ' ) , BuiltinOption ( UserComboOption , ' Backend to use ' , ' ninja ' , choices = backendlist ,
readonly = True ) ) ,
( OptionKey ( ' genvslite ' ) ,
BuiltinOption (
UserComboOption ,
' Setup multiple buildtype-suffixed ninja-backend build directories, '
' and a [builddir]_vs containing a Visual Studio meta-backend with multiple configurations that calls into them ' ,
' vs2022 ' ,
choices = genvslitelist )
) ,
( OptionKey ( ' buildtype ' ) , BuiltinOption ( UserComboOption , ' Build type to use ' , ' debug ' ,
choices = buildtypelist ) ) ,
( OptionKey ( ' debug ' ) , BuiltinOption ( UserBooleanOption , ' Enable debug symbols and other information ' , True ) ) ,
( OptionKey ( ' default_library ' ) , BuiltinOption ( UserComboOption , ' Default library type ' , ' shared ' , choices = [ ' shared ' , ' static ' , ' both ' ] ,
yielding = False ) ) ,
( OptionKey ( ' default_both_libraries ' ) , BuiltinOption ( UserComboOption , ' Default library type for both_libraries ' , ' shared ' , choices = [ ' shared ' , ' static ' , ' auto ' ] ) ) ,
( OptionKey ( ' errorlogs ' ) , BuiltinOption ( UserBooleanOption , " Whether to print the logs from failing tests " , True ) ) ,
( OptionKey ( ' install_umask ' ) , BuiltinOption ( UserUmaskOption , ' Default umask to apply on permissions of installed files ' , ' 022 ' ) ) ,
( OptionKey ( ' layout ' ) , BuiltinOption ( UserComboOption , ' Build directory layout ' , ' mirror ' , choices = [ ' mirror ' , ' flat ' ] ) ) ,
( OptionKey ( ' optimization ' ) , BuiltinOption ( UserComboOption , ' Optimization level ' , ' 0 ' , choices = [ ' plain ' , ' 0 ' , ' g ' , ' 1 ' , ' 2 ' , ' 3 ' , ' s ' ] ) ) ,
( OptionKey ( ' prefer_static ' ) , BuiltinOption ( UserBooleanOption , ' Whether to try static linking before shared linking ' , False ) ) ,
( OptionKey ( ' stdsplit ' ) , BuiltinOption ( UserBooleanOption , ' Split stdout and stderr in test logs ' , True ) ) ,
( OptionKey ( ' strip ' ) , BuiltinOption ( UserBooleanOption , ' Strip targets on install ' , False ) ) ,
( OptionKey ( ' unity ' ) , BuiltinOption ( UserComboOption , ' Unity build ' , ' off ' , choices = [ ' on ' , ' off ' , ' subprojects ' ] ) ) ,
( OptionKey ( ' unity_size ' ) , BuiltinOption ( UserIntegerOption , ' Unity block size ' , ( 2 , None , 4 ) ) ) ,
( OptionKey ( ' warning_level ' ) , BuiltinOption ( UserComboOption , ' Compiler warning level to use ' , ' 1 ' , choices = [ ' 0 ' , ' 1 ' , ' 2 ' , ' 3 ' , ' everything ' ] , yielding = False ) ) ,
( OptionKey ( ' werror ' ) , BuiltinOption ( UserBooleanOption , ' Treat warnings as errors ' , False , yielding = False ) ) ,
( OptionKey ( ' wrap_mode ' ) , BuiltinOption ( UserComboOption , ' Wrap mode ' , ' default ' , choices = [ ' default ' , ' nofallback ' , ' nodownload ' , ' forcefallback ' , ' nopromote ' ] ) ) ,
( OptionKey ( ' force_fallback_for ' ) , BuiltinOption ( UserArrayOption , ' Force fallback for those subprojects ' , [ ] ) ) ,
( OptionKey ( ' vsenv ' ) , BuiltinOption ( UserBooleanOption , ' Activate Visual Studio environment ' , False , readonly = True ) ) ,
# Pkgconfig module
( OptionKey ( ' pkgconfig.relocatable ' ) ,
BuiltinOption ( UserBooleanOption , ' Generate pkgconfig files as relocatable ' , False ) ) ,
# Python module
( OptionKey ( ' python.bytecompile ' ) ,
BuiltinOption ( UserIntegerOption , ' Whether to compile bytecode ' , ( - 1 , 2 , 0 ) ) ) ,
( OptionKey ( ' python.install_env ' ) ,
BuiltinOption ( UserComboOption , ' Which python environment to install to ' , ' prefix ' , choices = [ ' auto ' , ' prefix ' , ' system ' , ' venv ' ] ) ) ,
( OptionKey ( ' python.platlibdir ' ) ,
BuiltinOption ( UserStringOption , ' Directory for site-specific, platform-specific files. ' , ' ' ) ) ,
( OptionKey ( ' python.purelibdir ' ) ,
BuiltinOption ( UserStringOption , ' Directory for site-specific, non-platform-specific files. ' , ' ' ) ) ,
( OptionKey ( ' python.allow_limited_api ' ) ,
BuiltinOption ( UserBooleanOption , ' Whether to allow use of the Python Limited API ' , True ) ) ,
] )
BUILTIN_OPTIONS = OrderedDict ( chain ( BUILTIN_DIR_OPTIONS . items ( ) , BUILTIN_CORE_OPTIONS . items ( ) ) )
BUILTIN_OPTIONS_PER_MACHINE : T . Dict [ ' OptionKey ' , ' BuiltinOption ' ] = OrderedDict ( [
( OptionKey ( ' pkg_config_path ' ) , BuiltinOption ( UserArrayOption , ' List of additional paths for pkg-config to search ' , [ ] ) ) ,
( OptionKey ( ' cmake_prefix_path ' ) , BuiltinOption ( UserArrayOption , ' List of additional prefixes for cmake to search ' , [ ] ) ) ,
] )
# Special prefix-dependent defaults for installation directories that reside in
# a path outside of the prefix in FHS and common usage.
BUILTIN_DIR_NOPREFIX_OPTIONS : T . Dict [ OptionKey , T . Dict [ str , str ] ] = {
OptionKey ( ' sysconfdir ' ) : { ' /usr ' : ' /etc ' } ,
OptionKey ( ' localstatedir ' ) : { ' /usr ' : ' /var ' , ' /usr/local ' : ' /var/local ' } ,
OptionKey ( ' sharedstatedir ' ) : { ' /usr ' : ' /var/lib ' , ' /usr/local ' : ' /var/local/lib ' } ,
OptionKey ( ' python.platlibdir ' ) : { } ,
OptionKey ( ' python.purelibdir ' ) : { } ,
}
class OptionStore :
def __init__ ( self ) :
self . d : T . Dict [ ' OptionKey ' , ' UserOption[T.Any] ' ] = { }
self . project_options = set ( )
self . all_languages = set ( )
self . module_options = set ( )
from . compilers import all_languages
for lang in all_languages :
self . all_languages . add ( lang )
def __len__ ( self ) :
return len ( self . d )
def ensure_key ( self , key : T . Union [ OptionKey , str ] ) - > OptionKey :
if isinstance ( key , str ) :
return OptionKey ( key )
return key
def get_value_object ( self , key : T . Union [ OptionKey , str ] ) - > ' UserOption[T.Any] ' :
return self . d [ self . ensure_key ( key ) ]
def get_value ( self , key : T . Union [ OptionKey , str ] ) - > ' T.Any ' :
return self . get_value_object ( key ) . value
def add_system_option ( self , key : T . Union [ OptionKey , str ] , valobj : ' UserOption[T.Any] ' ) :
key = self . ensure_key ( key )
if ' . ' in key . name :
raise MesonException ( f ' Internal error: non-module option has a period in its name { key . name } . ' )
self . add_system_option_internal ( key , valobj )
def add_system_option_internal ( self , key : T . Union [ OptionKey , str ] , valobj : ' UserOption[T.Any] ' ) :
key = self . ensure_key ( key )
assert isinstance ( valobj , UserOption )
self . d [ key ] = valobj
def add_compiler_option ( self , language : str , key : T . Union [ OptionKey , str ] , valobj : ' UserOption[T.Any] ' ) :
key = self . ensure_key ( key )
if not key . name . startswith ( language + ' _ ' ) :
raise MesonException ( f ' Internal error: all compiler option names must start with language prefix. ( { key . name } vs { language } _) ' )
self . add_system_option ( key , valobj )
def add_project_option ( self , key : T . Union [ OptionKey , str ] , valobj : ' UserOption[T.Any] ' ) :
key = self . ensure_key ( key )
self . d [ key ] = valobj
self . project_options . add ( key )
def add_module_option ( self , modulename : str , key : T . Union [ OptionKey , str ] , valobj : ' UserOption[T.Any] ' ) :
key = self . ensure_key ( key )
if key . name . startswith ( ' build. ' ) :
raise MesonException ( ' FATAL internal error: somebody goofed option handling. ' )
if not key . name . startswith ( modulename + ' . ' ) :
raise MesonException ( ' Internal error: module option name {key.name} does not start with module prefix {modulename} . ' )
self . add_system_option_internal ( key , valobj )
self . module_options . add ( key )
def set_value ( self , key : T . Union [ OptionKey , str ] , new_value : ' T.Any ' ) - > bool :
key = self . ensure_key ( key )
return self . d [ key ] . set_value ( new_value )
# FIXME, this should be removed.or renamed to "change_type_of_existing_object" or something like that
def set_value_object ( self , key : T . Union [ OptionKey , str ] , new_object : ' UserOption[T.Any] ' ) - > bool :
key = self . ensure_key ( key )
self . d [ key ] = new_object
def remove ( self , key ) :
del self . d [ key ]
def __contains__ ( self , key ) :
key = self . ensure_key ( key )
return key in self . d
def __repr__ ( self ) :
return repr ( self . d )
def keys ( self ) :
return self . d . keys ( )
def values ( self ) :
return self . d . values ( )
def items ( self ) - > ItemsView [ ' OptionKey ' , ' UserOption[T.Any] ' ] :
return self . d . items ( )
# FIXME: this method must be deleted and users moved to use "add_xxx_option"s instead.
def update ( self , * args , * * kwargs ) :
return self . d . update ( * args , * * kwargs )
def setdefault ( self , k , o ) :
return self . d . setdefault ( k , o )
def get ( self , * args , * * kwargs ) - > UserOption :
return self . d . get ( * args , * * kwargs )
def is_project_option ( self , key : OptionKey ) - > bool :
""" Convenience method to check if this is a project option. """
return key in self . project_options
def is_reserved_name ( self , key : OptionKey ) - > bool :
if key . name in _BUILTIN_NAMES :
return True
if ' _ ' not in key . name :
return False
prefix = key . name . split ( ' _ ' ) [ 0 ]
# Pylint seems to think that it is faster to build a set object
# and all related work just to test whether a string has one of two
# values. It is not, thank you very much.
if prefix in ( ' b ' , ' backend ' ) : # pylint: disable=R6201
return True
if prefix in self . all_languages :
return True
return False
def is_builtin_option ( self , key : OptionKey ) - > bool :
""" Convenience method to check if this is a builtin option. """
return key . name in _BUILTIN_NAMES or self . is_module_option ( key )
def is_base_option ( self , key : OptionKey ) - > bool :
""" Convenience method to check if this is a base option. """
return key . name . startswith ( ' b_ ' )
def is_backend_option ( self , key : OptionKey ) - > bool :
""" Convenience method to check if this is a backend option. """
return key . name . startswith ( ' backend_ ' )
def is_compiler_option ( self , key : OptionKey ) - > bool :
""" Convenience method to check if this is a compiler option. """
# FIXME, duplicate of is_reserved_name above. Should maybe store a cache instead.
if ' _ ' not in key . name :
return False
prefix = key . name . split ( ' _ ' ) [ 0 ]
if prefix in self . all_languages :
return True
return False
def is_module_option ( self , key : OptionKey ) - > bool :
return key in self . module_options