@ -21,11 +21,15 @@ project files and don't need this info."""
import json
from . import build , mtest , coredata as cdata
from . import environment
from . import mesonlib
from . import astinterpreter
from . import mparser
from . import mlog
from . import compilers
from . import optinterpreter
from . interpreterbase import InvalidArguments
from . backend import ninjabackend
from . backend import ninjabackend , backends
import sys , os
import pathlib
@ -48,6 +52,8 @@ def add_arguments(parser):
help = ' List external dependencies. ' )
parser . add_argument ( ' --projectinfo ' , action = ' store_true ' , dest = ' projectinfo ' , default = False ,
help = ' Information about projects. ' )
parser . add_argument ( ' --backend ' , choices = cdata . backendlist , dest = ' backend ' , default = ' ninja ' ,
help = ' The backend to use for the --buildoptions introspection. ' )
parser . add_argument ( ' builddir ' , nargs = ' ? ' , default = ' . ' , help = ' The build directory ' )
def determine_installed_path ( target , installdata ) :
@ -129,7 +135,154 @@ def list_target_files(target_name, coredata, builddata):
out . append ( i )
print ( json . dumps ( out ) )
def list_buildoptions ( coredata , builddata ) :
class BuildoptionsOptionHelper :
# mimic an argparse namespace
def __init__ ( self , cross_file ) :
self . cross_file = cross_file
self . native_file = None
self . cmd_line_options = { }
class BuildoptionsInterperter ( 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 = BuildoptionsOptionHelper ( cross_file )
self . cross_file = cross_file
if env is None :
self . environment = environment . Environment ( source_root , None , options )
else :
self . environment = env
self . subproject = subproject
self . subproject_dir = subproject_dir
self . coredata = self . environment . get_coredata ( )
self . option_file = os . path . join ( self . source_root , self . subdir , ' meson_options.txt ' )
self . backend = backend
self . default_options = { ' backend ' : self . backend }
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 = [ ]
for i in args :
if isinstance ( i , mparser . ArrayNode ) :
flattend_args + = [ x . value for x in i . args . arguments ]
elif isinstance ( i , str ) :
flattend_args + = [ i ]
else :
pass
return flattend_args
def add_languages ( self , args ) :
need_cross_compiler = self . environment . is_cross_build ( ) and self . environment . cross_info . need_cross_compiler ( )
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_langs = self . flatten_args ( args [ 1 : ] )
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 ]
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 )
self . coredata . set_default_options ( self . default_options , self . subproject , self . environment . cmd_line_options )
if not self . is_subproject ( ) and ' subproject_dir ' in kwargs :
spdirname = kwargs [ ' subproject_dir ' ]
if isinstance ( spdirname , str ) :
self . subproject_dir = spdirname
if not self . is_subproject ( ) :
subprojects_dir = os . path . join ( self . source_root , self . subproject_dir )
if os . path . isdir ( subprojects_dir ) :
for i in os . listdir ( subprojects_dir ) :
if os . path . isdir ( os . path . join ( subprojects_dir , i ) ) :
self . do_subproject ( i )
self . coredata . init_backend_options ( self . backend )
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 )
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 = BuildoptionsInterperter ( subpr , ' ' , self . backend , cross_file = self . cross_file , subproject = dirname , subproject_dir = self . subproject_dir , env = self . environment )
subi . analyze ( )
except :
return
def func_add_languages ( self , node , args , kwargs ) :
return self . add_languages ( self . flatten_args ( args ) )
def is_subproject ( self ) :
return self . subproject != ' '
def analyze ( self ) :
self . load_root_meson_file ( )
self . sanity_check_ast ( )
self . parse_project ( )
self . run ( )
def list_buildoptions_from_source ( sourcedir , backend ) :
# 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 = BuildoptionsInterperter ( sourcedir , ' ' , backend . name )
intr . analyze ( )
# Reenable logging just in case
mlog . enable ( )
list_buildoptions ( intr . coredata )
def list_buildoptions ( coredata ) :
optlist = [ ]
dir_option_names = [ ' bindir ' ,
@ -301,10 +454,13 @@ def run(options):
if options . builddir is not None :
datadir = os . path . join ( options . builddir , datadir )
if options . builddir . endswith ( ' /meson.build ' ) or options . builddir . endswith ( ' \\ meson.build ' ) or options . builddir == ' meson.build ' :
if options . projectinfo :
sourcedir = ' . ' if options . builddir == ' meson.build ' else options . builddir [ : - 11 ]
if options . projectinfo :
list_projinfo_from_source ( sourcedir )
return 0
if options . buildoptions :
list_buildoptions_from_source ( sourcedir , options . backend )
return 0
if not os . path . isdir ( datadir ) :
print ( ' Current directory is not a build dir. Please specify it or '
' change the working directory to it. ' )
@ -330,7 +486,7 @@ def run(options):
elif options . buildsystem_files :
list_buildsystem_files ( builddata )
elif options . buildoptions :
list_buildoptions ( coredata , builddata )
list_buildoptions ( coredata )
elif options . tests :
list_tests ( testdata )
elif options . benchmarks :