@ -151,20 +151,20 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend)
tlist . append ( t )
return tlist
class BuildoptionsOp tionHelper:
class Introspec tionHelper:
# mimic an argparse namespace
def __init__ ( self , cross_file ) :
self . cross_file = cross_file
self . native_file = None
self . cmd_line_options = { }
class BuildoptionsInterper ter( astinterpreter . AstInterpreter ) :
class IntrospectionInterpre ter( astinterpreter . AstInterpreter ) :
# Interpreter to detect the options without a build directory
# Most of the code is stolen from interperter.Interpreter
def __init__ ( self , source_root , subdir , backend , cross_file = None , subproject = ' ' , subproject_dir = ' subprojects ' , env = None ) :
super ( ) . __init__ ( source_root , subdir )
options = BuildoptionsOp tionHelper( cross_file )
options = Introspec tionHelper( cross_file )
self . cross_file = cross_file
if env is None :
self . environment = environment . Environment ( source_root , None , options )
@ -176,37 +176,18 @@ class BuildoptionsInterperter(astinterpreter.AstInterpreter):
self . option_file = os . path . join ( self . source_root , self . subdir , ' meson_options.txt ' )
self . backend = backend
self . default_options = { ' backend ' : self . backend }
self . project_data = { }
self . funcs . update ( {
' project ' : self . func_project ,
' add_languages ' : self . func_add_languages
} )
def detect_compilers ( self , lang , need_cross_compiler ) :
comp , cross_comp = self . environment . detect_compilers ( lang , need_cross_compiler )
if comp is None :
return None , None
self . coredata . compilers [ lang ] = comp
# Native compiler always exist so always add its options.
new_options = comp . get_options ( )
if cross_comp is not None :
self . coredata . cross_compilers [ lang ] = cross_comp
new_options . update ( cross_comp . get_options ( ) )
optprefix = lang + ' _ '
for k , o in new_options . items ( ) :
if not k . startswith ( optprefix ) :
raise RuntimeError ( ' Internal error, %s has incorrect prefix. ' % k )
if k in self . environment . cmd_line_options :
o . set_value ( self . environment . cmd_line_options [ k ] )
self . coredata . compiler_options . setdefault ( k , o )
return comp , cross_comp
def flatten_args ( self , args ) :
# Resolve mparser.ArrayNode if needed
flattend_args = [ ]
if isinstance ( args , mparser . ArrayNode ) :
args = [ x . value for x in args . args . arguments ]
for i in args :
if isinstance ( i , mparser . ArrayNode ) :
flattend_args + = [ x . value for x in i . args . arguments ]
@ -216,35 +197,25 @@ class BuildoptionsInterperter(astinterpreter.AstInterpreter):
pass
return flattend_args
def add_languages ( self , args ) :
need_cross_compiler = self . environment . is_cross_build ( )
for lang in sorted ( args , key = compilers . sort_clink ) :
lang = lang . lower ( )
if lang not in self . coredata . compilers :
( comp , _ ) = self . detect_compilers ( lang , need_cross_compiler )
if comp is None :
return
for optname in comp . base_options :
if optname in self . coredata . base_options :
continue
oobj = compilers . base_options [ optname ]
self . coredata . base_options [ optname ] = oobj
def func_project ( self , node , args , kwargs ) :
if len ( args ) < 1 :
raise InvalidArguments ( ' Not enough arguments to project(). Needs at least the project name. ' )
proj_name = args [ 0 ]
proj_vers = kwargs . get ( ' version ' , ' undefined ' )
proj_langs = self . flatten_args ( args [ 1 : ] )
if isinstance ( proj_vers , mparser . ElementaryNode ) :
proj_vers = proj_vers . value
if not isinstance ( proj_vers , str ) :
proj_vers = ' undefined '
self . project_data = { ' descriptive_name ' : proj_name , ' version ' : proj_vers }
if os . path . exists ( self . option_file ) :
oi = optinterpreter . OptionInterpreter ( self . subproject )
oi . process ( self . option_file )
self . coredata . merge_user_options ( oi . options )
def_opts = kwargs . get ( ' default_options ' , [ ] )
if isinstance ( def_opts , mparser . ArrayNode ) :
def_opts = [ x . value for x in def_opts . args . arguments ]
def_opts = self . flatten_args ( kwargs . get ( ' default_options ' , [ ] ) )
self . project_default_options = mesonlib . stringlistify ( def_opts )
self . project_default_options = cdata . create_options_dict ( self . project_default_options )
self . default_options . update ( self . project_default_options )
@ -255,6 +226,7 @@ class BuildoptionsInterperter(astinterpreter.AstInterpreter):
if isinstance ( spdirname , str ) :
self . subproject_dir = spdirname
if not self . is_subproject ( ) :
self . project_data [ ' subprojects ' ] = [ ]
subprojects_dir = os . path . join ( self . source_root , self . subproject_dir )
if os . path . isdir ( subprojects_dir ) :
for i in os . listdir ( subprojects_dir ) :
@ -265,19 +237,26 @@ class BuildoptionsInterperter(astinterpreter.AstInterpreter):
options = { k : v for k , v in self . environment . cmd_line_options . items ( ) if k . startswith ( ' backend_ ' ) }
self . coredata . set_options ( options )
self . add_languages ( proj_langs )
self . func_ add_languages( None , proj_langs , None )
def do_subproject ( self , dirname ) :
subproject_dir_abs = os . path . join ( self . environment . get_source_dir ( ) , self . subproject_dir )
subpr = os . path . join ( subproject_dir_abs , dirname )
try :
subi = BuildoptionsInterper ter( subpr , ' ' , self . backend , cross_file = self . cross_file , subproject = dirname , subproject_dir = self . subproject_dir , env = self . environment )
subi = IntrospectionInterpre ter( subpr , ' ' , self . backend , cross_file = self . cross_file , subproject = dirname , subproject_dir = self . subproject_dir , env = self . environment )
subi . analyze ( )
subi . project_data [ ' name ' ] = dirname
self . project_data [ ' subprojects ' ] + = [ subi . project_data ]
except :
return
def func_add_languages ( self , node , args , kwargs ) :
return self . add_languages ( self . flatten_args ( args ) )
args = self . flatten_args ( args )
need_cross_compiler = self . environment . is_cross_build ( )
for lang in sorted ( args , key = compilers . sort_clink ) :
lang = lang . lower ( )
if lang not in self . coredata . compilers :
self . environment . detect_compilers ( lang , need_cross_compiler )
def is_subproject ( self ) :
return self . subproject != ' '
@ -292,7 +271,7 @@ def list_buildoptions_from_source(sourcedir, backend, indent):
# Make sure that log entries in other parts of meson don't interfere with the JSON output
mlog . disable ( )
backend = backends . get_backend_from_name ( backend , None )
intr = BuildoptionsInterper ter( sourcedir , ' ' , backend . name )
intr = IntrospectionInterpre ter( sourcedir , ' ' , backend . name )
intr . analyze ( )
# Reenable logging just in case
mlog . enable ( )
@ -438,60 +417,22 @@ def list_projinfo(builddata: build.Build):
result [ ' subprojects ' ] = subprojects
return result
class ProjectInfoInterperter ( astinterpreter . AstInterpreter ) :
def __init__ ( self , source_root , subdir ) :
super ( ) . __init__ ( source_root , subdir )
self . funcs . update ( { ' project ' : self . func_project } )
self . project_name = None
self . project_version = None
def func_project ( self , node , args , kwargs ) :
if len ( args ) < 1 :
raise InvalidArguments ( ' Not enough arguments to project(). Needs at least the project name. ' )
self . project_name = args [ 0 ]
self . project_version = kwargs . get ( ' version ' , ' undefined ' )
if isinstance ( self . project_version , mparser . ElementaryNode ) :
self . project_version = self . project_version . value
def set_variable ( self , varname , variable ) :
pass
def analyze ( self ) :
self . load_root_meson_file ( )
self . sanity_check_ast ( )
self . parse_project ( )
self . run ( )
def list_projinfo_from_source ( sourcedir , indent ) :
files = find_buildsystem_files_list ( sourcedir )
files = [ os . path . normpath ( x ) for x in files ]
result = { ' buildsystem_files ' : [ ] }
subprojects = { }
for f in files :
f = f . replace ( ' \\ ' , ' / ' )
if f == ' meson.build ' :
interpreter = ProjectInfoInterperter ( sourcedir , ' ' )
interpreter . analyze ( )
version = None
if interpreter . project_version is str :
version = interpreter . project_version
result . update ( { ' version ' : version , ' descriptive_name ' : interpreter . project_name } )
result [ ' buildsystem_files ' ] . append ( f )
elif f . startswith ( ' subprojects/ ' ) :
subproject_id = f . split ( ' / ' ) [ 1 ]
subproject = subprojects . setdefault ( subproject_id , { ' buildsystem_files ' : [ ] } )
subproject [ ' buildsystem_files ' ] . append ( f )
if f . count ( ' / ' ) == 2 and f . endswith ( ' meson.build ' ) :
interpreter = ProjectInfoInterperter ( os . path . join ( sourcedir , ' subprojects ' , subproject_id ) , ' ' )
interpreter . analyze ( )
subproject . update ( { ' name ' : subproject_id , ' version ' : interpreter . project_version , ' descriptive_name ' : interpreter . project_name } )
else :
result [ ' buildsystem_files ' ] . append ( f )
mlog . disable ( )
intr = IntrospectionInterpreter ( sourcedir , ' ' , ' ninja ' )
intr . analyze ( )
mlog . enable ( )
subprojects = [ obj for name , obj in subprojects . items ( ) ]
result [ ' subprojects ' ] = subprojects
print ( json . dumps ( result , indent = indent ) )
for i in intr . project_data [ ' subprojects ' ] :
basedir = os . path . join ( intr . subproject_dir , i [ ' name ' ] )
i [ ' buildsystem_files ' ] = [ x for x in files if x . startswith ( basedir ) ]
files = [ x for x in files if not x . startswith ( basedir ) ]
intr . project_data [ ' buildsystem_files ' ] = files
print ( json . dumps ( intr . project_data , indent = indent ) )
def run ( options ) :
datadir = ' meson-private '
@ -532,13 +473,8 @@ def run(options):
return 1
results = [ ]
toextract = [ ]
intro_types = get_meson_introspection_types ( )
for i in intro_types . keys ( ) :
if options . all or getattr ( options , i , False ) :
toextract + = [ i ]
# Handle the one option that does not have its own JSON file (meybe deprecate / remove this?)
if options . target_files is not None :
targets_file = os . path . join ( infodir , ' intro-targets.json ' )
@ -547,7 +483,9 @@ def run(options):
results + = [ ( ' target_files ' , list_target_files ( options . target_files , targets , source_dir ) ) ]
# Extract introspection information from JSON
for i in toextract :
for i in intro_types . keys ( ) :
if not options . all and not getattr ( options , i , False ) :
continue
curr = os . path . join ( infodir , ' intro- {} .json ' . format ( i ) )
if not os . path . isfile ( curr ) :
print ( ' Introspection file {} does not exist. ' . format ( curr ) )