2017-01-30 03:14:53 +08:00
# Copyright 2012-2017 The Meson development team
2013-02-25 04:30:02 +08:00
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
2014-03-18 04:09:28 +08:00
import pickle , os , uuid
2017-02-03 21:27:57 +08:00
from pathlib import PurePath
2017-03-24 21:01:22 +08:00
from collections import OrderedDict
2017-01-30 05:23:35 +08:00
from . mesonlib import MesonException , commonpath
from . mesonlib import default_libdir , default_libexecdir , default_prefix
2017-03-29 03:03:39 +08:00
import ast
2013-02-25 04:44:01 +08:00
2017-04-29 02:38:25 +08:00
version = ' 0.41.0.dev1 '
2017-03-23 17:34:32 +08:00
backendlist = [ ' ninja ' , ' vs ' , ' vs2010 ' , ' vs2015 ' , ' vs2017 ' , ' xcode ' ]
2013-03-02 04:21:02 +08:00
2015-11-03 09:31:56 +08:00
class UserOption :
2015-11-10 06:45:05 +08:00
def __init__ ( self , name , description , choices ) :
2015-11-03 09:31:56 +08:00
super ( ) . __init__ ( )
self . name = name
2015-11-10 06:45:05 +08:00
self . choices = choices
2015-11-03 09:31:56 +08:00
self . description = description
def parse_string ( self , valuestring ) :
return valuestring
2017-03-11 01:37:45 +08:00
# 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 ) :
raise RuntimeError ( ' Derived option class did not override validate_value. ' )
2015-11-03 09:31:56 +08:00
class UserStringOption ( UserOption ) :
2015-11-10 06:45:05 +08:00
def __init__ ( self , name , description , value , choices = None ) :
super ( ) . __init__ ( name , description , choices )
2015-11-03 09:31:56 +08:00
self . set_value ( value )
2015-11-04 06:32:09 +08:00
def validate ( self , value ) :
if not isinstance ( value , str ) :
2016-02-21 19:14:25 +08:00
raise MesonException ( ' Value " %s " for string option " %s " is not a string. ' % ( str ( value ) , self . name ) )
2015-11-04 06:32:09 +08:00
def set_value ( self , newvalue ) :
self . validate ( newvalue )
2015-11-03 09:31:56 +08:00
self . value = newvalue
2017-03-11 01:37:45 +08:00
def validate_value ( self , value ) :
self . validate ( value )
return value
2015-11-03 09:31:56 +08:00
class UserBooleanOption ( UserOption ) :
def __init__ ( self , name , description , value ) :
2017-01-03 02:09:47 +08:00
super ( ) . __init__ ( name , description , [ True , False ] )
2015-11-03 09:31:56 +08:00
self . set_value ( value )
def tobool ( self , thing ) :
if isinstance ( thing , bool ) :
return thing
if thing . lower ( ) == ' true ' :
return True
if thing . lower ( ) == ' false ' :
return False
raise MesonException ( ' Value %s is not boolean (true or false). ' % thing )
def set_value ( self , newvalue ) :
self . value = self . tobool ( newvalue )
def parse_string ( self , valuestring ) :
if valuestring == ' false ' :
return False
if valuestring == ' true ' :
return True
raise MesonException ( ' Value " %s " for boolean option " %s " is not a boolean. ' % ( valuestring , self . name ) )
2016-05-31 01:11:18 +08:00
def __bool__ ( self ) :
return self . value
2017-03-11 01:37:45 +08:00
def validate_value ( self , value ) :
return self . tobool ( value )
2015-11-03 09:31:56 +08:00
class UserComboOption ( UserOption ) :
def __init__ ( self , name , description , choices , value ) :
2015-11-10 06:45:05 +08:00
super ( ) . __init__ ( name , description , choices )
2015-11-03 09:31:56 +08:00
if not isinstance ( self . choices , list ) :
raise MesonException ( ' Combo choices must be an array. ' )
for i in self . choices :
if not isinstance ( i , str ) :
raise MesonException ( ' Combo choice elements must be strings. ' )
self . set_value ( value )
def set_value ( self , newvalue ) :
if newvalue not in self . choices :
optionsstring = ' , ' . join ( [ ' " %s " ' % ( item , ) for item in self . choices ] )
raise MesonException ( ' Value " %s " for combo option " %s " is not one of the choices. Possible choices are: %s . ' % ( newvalue , self . name , optionsstring ) )
self . value = newvalue
2017-03-11 01:37:45 +08:00
def validate_value ( self , value ) :
if value not in self . choices :
raise MesonException ( ' Value %s not one of accepted values. ' % value )
2017-03-13 04:13:26 +08:00
return value
2017-03-11 01:37:45 +08:00
2015-11-03 09:31:56 +08:00
class UserStringArrayOption ( UserOption ) :
2015-11-10 06:45:05 +08:00
def __init__ ( self , name , description , value , * * kwargs ) :
super ( ) . __init__ ( name , description , kwargs . get ( ' choices ' , [ ] ) )
2015-11-03 09:31:56 +08:00
self . set_value ( value )
2017-03-11 01:37:45 +08:00
def validate ( self , value ) :
if isinstance ( value , str ) :
if not value . startswith ( ' [ ' ) :
raise MesonException ( ' Valuestring does not define an array: ' + value )
2017-03-29 03:03:39 +08:00
newvalue = ast . literal_eval ( value )
2017-03-11 01:37:45 +08:00
else :
newvalue = value
2015-11-03 09:31:56 +08:00
if not isinstance ( newvalue , list ) :
2016-07-18 04:57:32 +08:00
raise MesonException ( ' " {0} " should be a string array, but it is not ' . format ( str ( newvalue ) ) )
2015-11-03 09:31:56 +08:00
for i in newvalue :
if not isinstance ( i , str ) :
2016-07-18 04:57:32 +08:00
raise MesonException ( ' String array element " {0} " is not a string. ' . format ( str ( newvalue ) ) )
2017-03-11 01:37:45 +08:00
return newvalue
def set_value ( self , newvalue ) :
self . value = self . validate ( newvalue )
def validate_value ( self , value ) :
self . validate ( value )
return value
2015-11-03 09:31:56 +08:00
2013-04-01 19:08:54 +08:00
# This class contains all data that must persist over multiple
# invocations of Meson. It is roughly the same thing as
# cmakecache.
2017-01-17 21:13:03 +08:00
class CoreData :
2013-06-20 23:07:03 +08:00
2013-02-25 04:30:02 +08:00
def __init__ ( self , options ) :
2014-03-18 06:43:04 +08:00
self . guid = str ( uuid . uuid4 ( ) ) . upper ( )
2014-03-19 05:54:03 +08:00
self . test_guid = str ( uuid . uuid4 ( ) ) . upper ( )
2015-11-05 02:00:04 +08:00
self . regen_guid = str ( uuid . uuid4 ( ) ) . upper ( )
2014-03-18 04:09:28 +08:00
self . target_guids = { }
2013-03-02 04:21:02 +08:00
self . version = version
2015-11-03 09:03:54 +08:00
self . init_builtins ( options )
2013-10-19 01:55:10 +08:00
self . user_options = { }
2015-10-04 08:41:38 +08:00
self . compiler_options = { }
2016-03-17 03:55:03 +08:00
self . base_options = { }
2017-03-24 22:13:21 +08:00
# These external_*args, are set via env vars CFLAGS, LDFLAGS, etc
2017-01-21 14:35:38 +08:00
# but only when not cross-compiling.
2017-03-24 22:13:21 +08:00
self . external_preprocess_args = { } # CPPFLAGS only
self . external_args = { } # CPPFLAGS + CFLAGS
self . external_link_args = { } # CFLAGS + LDFLAGS (with MSVC: only LDFLAGS)
2013-08-24 07:24:29 +08:00
if options . cross_file is not None :
self . cross_file = os . path . join ( os . getcwd ( ) , options . cross_file )
else :
self . cross_file = None
2017-03-25 09:02:26 +08:00
self . wrap_mode = options . wrap_mode
2017-03-24 21:01:22 +08:00
self . compilers = OrderedDict ( )
self . cross_compilers = OrderedDict ( )
2013-02-25 05:27:52 +08:00
self . deps = { }
2015-03-14 01:55:09 +08:00
self . modules = { }
2016-07-03 23:31:46 +08:00
# Only to print a warning if it changes between Meson invocations.
self . pkgconf_envvar = os . environ . get ( ' PKG_CONFIG_PATH ' , ' ' )
2013-02-25 05:11:14 +08:00
2017-01-16 18:38:00 +08:00
def sanitize_prefix ( self , prefix ) :
if not os . path . isabs ( prefix ) :
raise MesonException ( ' prefix value {!r} must be an absolute path '
' ' . format ( prefix ) )
if prefix . endswith ( ' / ' ) or prefix . endswith ( ' \\ ' ) :
# On Windows we need to preserve the trailing slash if the
# string is of type 'C:\' because 'C:' is not an absolute path.
if len ( prefix ) == 3 and prefix [ 1 ] == ' : ' :
pass
else :
prefix = prefix [ : - 1 ]
return prefix
def sanitize_dir_option_value ( self , prefix , option , value ) :
'''
If the option is an installation directory option and the value is an
absolute path , check that it resides within prefix and return the value
as a path relative to the prefix .
This way everyone can do f . ex , get_option ( ' libdir ' ) and be sure to get
the library directory relative to prefix .
'''
if option . endswith ( ' dir ' ) and os . path . isabs ( value ) and \
option not in builtin_dir_noprefix_options :
# Value must be a subdir of the prefix
2017-02-03 21:27:57 +08:00
# commonpath will always return a path in the native format, so we
# must use pathlib.PurePath to do the same conversion before
# comparing.
if commonpath ( [ value , prefix ] ) != str ( PurePath ( prefix ) ) :
2017-01-16 18:38:00 +08:00
m = ' The value of the {!r} option is {!r} which must be a ' \
' subdir of the prefix {!r} . \n Note that if you pass a ' \
' relative path, it is assumed to be a subdir of prefix. '
raise MesonException ( m . format ( option , value , prefix ) )
# Convert path to be relative to prefix
skip = len ( prefix ) + 1
value = value [ skip : ]
return value
2015-11-03 09:03:54 +08:00
def init_builtins ( self , options ) :
2016-02-29 04:20:07 +08:00
self . builtins = { }
2017-01-16 18:38:00 +08:00
# Sanitize prefix
options . prefix = self . sanitize_prefix ( options . prefix )
# Initialize other builtin options
2016-02-29 04:20:07 +08:00
for key in get_builtin_options ( ) :
2017-01-16 18:38:00 +08:00
if hasattr ( options , key ) :
value = getattr ( options , key )
value = self . sanitize_dir_option_value ( options . prefix , key , value )
setattr ( options , key , value )
else :
value = get_builtin_option_default ( key )
args = [ key ] + builtin_options [ key ] [ 1 : - 1 ] + [ value ]
2016-02-29 04:20:07 +08:00
self . builtins [ key ] = builtin_options [ key ] [ 0 ] ( * args )
2015-11-03 09:03:54 +08:00
2014-11-17 00:30:38 +08:00
def get_builtin_option ( self , optname ) :
2016-02-29 04:20:07 +08:00
if optname in self . builtins :
return self . builtins [ optname ] . value
raise RuntimeError ( ' Tried to get unknown builtin option %s . ' % optname )
2014-11-17 00:30:38 +08:00
2015-11-03 09:03:54 +08:00
def set_builtin_option ( self , optname , value ) :
2017-01-16 18:38:00 +08:00
if optname == ' prefix ' :
value = self . sanitize_prefix ( value )
elif optname in self . builtins :
prefix = self . builtins [ ' prefix ' ] . value
value = self . sanitize_dir_option_value ( prefix , optname , value )
2015-11-03 09:03:54 +08:00
else :
2016-02-29 04:20:07 +08:00
raise RuntimeError ( ' Tried to set unknown builtin option %s . ' % optname )
2017-02-01 08:57:22 +08:00
self . builtins [ optname ] . set_value ( value )
2015-11-04 06:32:09 +08:00
2017-03-11 01:37:45 +08:00
def validate_option_value ( self , option_name , override_value ) :
for opts in ( self . builtins , self . base_options , self . compiler_options , self . user_options ) :
if option_name in opts :
opt = opts [ option_name ]
return opt . validate_value ( override_value )
raise MesonException ( ' Tried to validate unknown option %s . ' % option_name )
2013-02-25 04:44:01 +08:00
def load ( filename ) :
2016-10-13 05:09:17 +08:00
load_fail_msg = ' Coredata file {!r} is corrupted. Try with a fresh build tree. ' . format ( filename )
try :
with open ( filename , ' rb ' ) as f :
obj = pickle . load ( f )
except pickle . UnpicklingError :
raise MesonException ( load_fail_msg )
2013-02-25 04:44:01 +08:00
if not isinstance ( obj , CoreData ) :
2016-10-13 05:09:17 +08:00
raise MesonException ( load_fail_msg )
2013-03-02 04:21:02 +08:00
if obj . version != version :
2017-01-01 02:50:15 +08:00
raise MesonException ( ' Build directory has been generated with Meson version %s , which is incompatible with current version %s . \n Please delete this build directory AND create a new one. ' %
2017-01-01 03:19:38 +08:00
( obj . version , version ) )
2013-02-25 04:44:01 +08:00
return obj
def save ( obj , filename ) :
2013-03-02 04:21:02 +08:00
if obj . version != version :
2016-10-13 05:09:17 +08:00
raise MesonException ( ' Fatal version mismatch corruption. ' )
2016-08-25 07:29:11 +08:00
with open ( filename , ' wb ' ) as f :
pickle . dump ( obj , f )
2013-03-10 05:53:02 +08:00
2016-02-29 04:20:07 +08:00
def get_builtin_options ( ) :
return list ( builtin_options . keys ( ) )
def is_builtin_option ( optname ) :
return optname in get_builtin_options ( )
def get_builtin_option_choices ( optname ) :
if is_builtin_option ( optname ) :
if builtin_options [ optname ] [ 0 ] == UserStringOption :
return None
elif builtin_options [ optname ] [ 0 ] == UserBooleanOption :
2017-01-03 02:09:47 +08:00
return [ True , False ]
2016-02-29 04:20:07 +08:00
else :
return builtin_options [ optname ] [ 2 ]
else :
raise RuntimeError ( ' Tried to get the supported values for an unknown builtin option \' %s \' . ' % optname )
def get_builtin_option_description ( optname ) :
if is_builtin_option ( optname ) :
return builtin_options [ optname ] [ 1 ]
else :
raise RuntimeError ( ' Tried to get the description for an unknown builtin option \' %s \' . ' % optname )
def get_builtin_option_default ( optname ) :
if is_builtin_option ( optname ) :
o = builtin_options [ optname ]
if o [ 0 ] == UserComboOption :
return o [ 3 ]
return o [ 2 ]
else :
raise RuntimeError ( ' Tried to get the default value for an unknown builtin option \' %s \' . ' % optname )
builtin_options = {
2017-01-09 18:14:24 +08:00
' buildtype ' : [ UserComboOption , ' Build type to use. ' , [ ' plain ' , ' debug ' , ' debugoptimized ' , ' release ' , ' minsize ' ] , ' debug ' ] ,
' strip ' : [ UserBooleanOption , ' Strip targets on install. ' , False ] ,
2017-03-14 02:13:21 +08:00
' unity ' : [ UserComboOption , ' Unity build. ' , [ ' on ' , ' off ' , ' subprojects ' ] , ' off ' ] ,
2017-01-09 18:14:24 +08:00
' prefix ' : [ UserStringOption , ' Installation prefix. ' , default_prefix ( ) ] ,
' libdir ' : [ UserStringOption , ' Library directory. ' , default_libdir ( ) ] ,
' libexecdir ' : [ UserStringOption , ' Library executable directory. ' , default_libexecdir ( ) ] ,
' bindir ' : [ UserStringOption , ' Executable directory. ' , ' bin ' ] ,
' sbindir ' : [ UserStringOption , ' System executable directory. ' , ' sbin ' ] ,
' includedir ' : [ UserStringOption , ' Header file directory. ' , ' include ' ] ,
' datadir ' : [ UserStringOption , ' Data file directory. ' , ' share ' ] ,
' mandir ' : [ UserStringOption , ' Manual page directory. ' , ' share/man ' ] ,
' infodir ' : [ UserStringOption , ' Info page directory. ' , ' share/info ' ] ,
' localedir ' : [ UserStringOption , ' Locale data directory. ' , ' share/locale ' ] ,
2016-12-18 07:08:45 +08:00
# sysconfdir, localstatedir and sharedstatedir are a bit special. These defaults to ${prefix}/etc,
# ${prefix}/var and ${prefix}/com but nobody uses that. Instead they always set it
# manually to /etc, /var and /var/lib. This default values is thus pointless and not really used
# but we set it to this for consistency with other systems.
2016-11-01 06:11:35 +08:00
#
2016-12-18 07:08:45 +08:00
# Projects installing to sysconfdir, localstatedir or sharedstatedir probably want
2016-12-05 21:27:05 +08:00
# to set the following in project():
2016-11-01 06:11:35 +08:00
#
2016-12-18 07:08:45 +08:00
# default_options : ['sysconfdir=/etc', 'localstatedir=/var', 'sharedstatedir=/var/lib']
2017-01-09 18:14:24 +08:00
' sysconfdir ' : [ UserStringOption , ' Sysconf data directory. ' , ' etc ' ] ,
' localstatedir ' : [ UserStringOption , ' Localstate data directory. ' , ' var ' ] ,
' sharedstatedir ' : [ UserStringOption , ' Architecture-independent data directory. ' , ' com ' ] ,
' werror ' : [ UserBooleanOption , ' Treat warnings as errors. ' , False ] ,
' warning_level ' : [ UserComboOption , ' Compiler warning level to use. ' , [ ' 1 ' , ' 2 ' , ' 3 ' ] , ' 1 ' ] ,
' layout ' : [ UserComboOption , ' Build directory layout. ' , [ ' mirror ' , ' flat ' ] , ' mirror ' ] ,
' default_library ' : [ UserComboOption , ' Default library type. ' , [ ' shared ' , ' static ' ] , ' shared ' ] ,
' backend ' : [ UserComboOption , ' Backend to use. ' , backendlist , ' ninja ' ] ,
' stdsplit ' : [ UserBooleanOption , ' Split stdout and stderr in test logs. ' , True ] ,
' errorlogs ' : [ UserBooleanOption , " Whether to print the logs from failing tests. " , True ] ,
}
2016-02-29 04:20:07 +08:00
2017-01-16 18:38:00 +08:00
# Installation directories that can reside in a path outside of the prefix
builtin_dir_noprefix_options = { ' sysconfdir ' , ' localstatedir ' , ' sharedstatedir ' }
2013-03-10 05:53:02 +08:00
forbidden_target_names = { ' clean ' : None ,
2016-12-20 04:40:54 +08:00
' clean-ctlist ' : None ,
2013-03-25 03:28:59 +08:00
' clean-gcno ' : None ,
' clean-gcda ' : None ,
2013-03-10 05:53:02 +08:00
' coverage-text ' : None ,
' coverage-xml ' : None ,
' coverage-html ' : None ,
' phony ' : None ,
2014-11-24 07:33:26 +08:00
' PHONY ' : None ,
2013-03-10 05:53:02 +08:00
' all ' : None ,
' test ' : None ,
2015-11-26 05:29:06 +08:00
' benchmark ' : None ,
2013-03-10 05:53:02 +08:00
' install ' : None ,
2016-12-29 05:16:19 +08:00
' uninstall ' : None ,
2013-03-23 04:37:34 +08:00
' build.ninja ' : None ,
2016-05-23 00:24:59 +08:00
' scan-build ' : None ,
2016-11-03 05:14:54 +08:00
' reconfigure ' : None ,
2017-01-01 03:25:09 +08:00
}