123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128 |
- # Copyright 2012-2018 The Meson development team
- # 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.
- from . import mparser
- from . import environment
- from . import coredata
- from . import dependencies
- from . import mlog
- from . import build
- from . import optinterpreter
- from . import compilers
- from .wrap import wrap, WrapMode
- from . import mesonlib
- from .mesonlib import FileMode, MachineChoice, Popen_safe, listify, extract_as_list, has_path_sep
- from .dependencies import ExternalProgram
- from .dependencies import InternalDependency, Dependency, NotFoundDependency, DependencyException
- from .interpreterbase import InterpreterBase
- from .interpreterbase import check_stringlist, flatten, noPosargs, noKwargs, stringArgs, permittedKwargs, noArgsFlattening
- from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest
- from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler, disablerIfNotFound
- from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs
- from .interpreterbase import ObjectHolder
- from .modules import ModuleReturnValue
- import os, shutil, uuid
- import re, shlex
- import subprocess
- from collections import namedtuple
- from itertools import chain
- from pathlib import PurePath
- import functools
- import importlib
- permitted_method_kwargs = {
- 'partial_dependency': {'compile_args', 'link_args', 'links', 'includes',
- 'sources'},
- }
- def stringifyUserArguments(args):
- if isinstance(args, list):
- return '[%s]' % ', '.join([stringifyUserArguments(x) for x in args])
- elif isinstance(args, dict):
- return '{%s}' % ', '.join(['%s : %s' % (stringifyUserArguments(k), stringifyUserArguments(v)) for k, v in args.items()])
- elif isinstance(args, int):
- return str(args)
- elif isinstance(args, str):
- return "'%s'" % args
- raise InvalidArguments('Function accepts only strings, integers, lists and lists thereof.')
- class FeatureOptionHolder(InterpreterObject, ObjectHolder):
- def __init__(self, env, option):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, option)
- if option.is_auto():
- self.held_object = env.coredata.builtins['auto_features']
- self.name = option.name
- self.methods.update({'enabled': self.enabled_method,
- 'disabled': self.disabled_method,
- 'auto': self.auto_method,
- })
- @noPosargs
- @permittedKwargs({})
- def enabled_method(self, args, kwargs):
- return self.held_object.is_enabled()
- @noPosargs
- @permittedKwargs({})
- def disabled_method(self, args, kwargs):
- return self.held_object.is_disabled()
- @noPosargs
- @permittedKwargs({})
- def auto_method(self, args, kwargs):
- return self.held_object.is_auto()
- def extract_required_kwarg(kwargs, subproject, feature_check=None, default=True):
- val = kwargs.get('required', default)
- disabled = False
- required = False
- feature = None
- if isinstance(val, FeatureOptionHolder):
- if not feature_check:
- feature_check = FeatureNew('User option "feature"', '0.47.0')
- feature_check.use(subproject)
- option = val.held_object
- feature = val.name
- if option.is_disabled():
- disabled = True
- elif option.is_enabled():
- required = True
- elif isinstance(val, bool):
- required = val
- else:
- raise InterpreterException('required keyword argument must be boolean or a feature option')
- # Keep boolean value in kwargs to simplify other places where this kwarg is
- # checked.
- kwargs['required'] = required
- return disabled, required, feature
- class TryRunResultHolder(InterpreterObject):
- def __init__(self, res):
- super().__init__()
- self.res = res
- self.methods.update({'returncode': self.returncode_method,
- 'compiled': self.compiled_method,
- 'stdout': self.stdout_method,
- 'stderr': self.stderr_method,
- })
- @noPosargs
- @permittedKwargs({})
- def returncode_method(self, args, kwargs):
- return self.res.returncode
- @noPosargs
- @permittedKwargs({})
- def compiled_method(self, args, kwargs):
- return self.res.compiled
- @noPosargs
- @permittedKwargs({})
- def stdout_method(self, args, kwargs):
- return self.res.stdout
- @noPosargs
- @permittedKwargs({})
- def stderr_method(self, args, kwargs):
- return self.res.stderr
- class RunProcess(InterpreterObject):
- def __init__(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False, capture=True):
- super().__init__()
- if not isinstance(cmd, ExternalProgram):
- raise AssertionError('BUG: RunProcess must be passed an ExternalProgram')
- self.capture = capture
- pc, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check)
- self.returncode = pc.returncode
- self.methods.update({'returncode': self.returncode_method,
- 'stdout': self.stdout_method,
- 'stderr': self.stderr_method,
- })
- def run_command(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check=False):
- command_array = cmd.get_command() + args
- menv = {'MESON_SOURCE_ROOT': source_dir,
- 'MESON_BUILD_ROOT': build_dir,
- 'MESON_SUBDIR': subdir,
- 'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in mesonintrospect]),
- }
- if in_builddir:
- cwd = os.path.join(build_dir, subdir)
- else:
- cwd = os.path.join(source_dir, subdir)
- child_env = os.environ.copy()
- child_env.update(menv)
- child_env = env.get_env(child_env)
- stdout = subprocess.PIPE if self.capture else subprocess.DEVNULL
- mlog.debug('Running command:', ' '.join(command_array))
- try:
- p, o, e = Popen_safe(command_array, stdout=stdout, env=child_env, cwd=cwd)
- if self.capture:
- mlog.debug('--- stdout ---')
- mlog.debug(o)
- else:
- o = ''
- mlog.debug('--- stdout disabled ---')
- mlog.debug('--- stderr ---')
- mlog.debug(e)
- mlog.debug('')
- if check and p.returncode != 0:
- raise InterpreterException('Command "{}" failed with status {}.'.format(' '.join(command_array), p.returncode))
- return p, o, e
- except FileNotFoundError:
- raise InterpreterException('Could not execute command "%s".' % ' '.join(command_array))
- @noPosargs
- @permittedKwargs({})
- def returncode_method(self, args, kwargs):
- return self.returncode
- @noPosargs
- @permittedKwargs({})
- def stdout_method(self, args, kwargs):
- return self.stdout
- @noPosargs
- @permittedKwargs({})
- def stderr_method(self, args, kwargs):
- return self.stderr
- class ConfigureFileHolder(InterpreterObject, ObjectHolder):
- def __init__(self, subdir, sourcename, targetname, configuration_data):
- InterpreterObject.__init__(self)
- obj = build.ConfigureFile(subdir, sourcename, targetname, configuration_data)
- ObjectHolder.__init__(self, obj)
- class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder):
- def __init__(self):
- MutableInterpreterObject.__init__(self)
- ObjectHolder.__init__(self, build.EnvironmentVariables())
- self.methods.update({'set': self.set_method,
- 'append': self.append_method,
- 'prepend': self.prepend_method,
- })
- def __repr__(self):
- repr_str = "<{0}: {1}>"
- return repr_str.format(self.__class__.__name__, self.held_object.envvars)
- def add_var(self, method, args, kwargs):
- if not isinstance(kwargs.get("separator", ""), str):
- raise InterpreterException("EnvironmentVariablesHolder methods 'separator'"
- " argument needs to be a string.")
- if len(args) < 2:
- raise InterpreterException("EnvironmentVariablesHolder methods require at least"
- "2 arguments, first is the name of the variable and"
- " following one are values")
- self.held_object.envvars.append((method, args[0], args[1:], kwargs))
- @stringArgs
- @permittedKwargs({'separator'})
- def set_method(self, args, kwargs):
- self.add_var(self.held_object.set, args, kwargs)
- @stringArgs
- @permittedKwargs({'separator'})
- def append_method(self, args, kwargs):
- self.add_var(self.held_object.append, args, kwargs)
- @stringArgs
- @permittedKwargs({'separator'})
- def prepend_method(self, args, kwargs):
- self.add_var(self.held_object.prepend, args, kwargs)
- class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder):
- def __init__(self, pv):
- MutableInterpreterObject.__init__(self)
- self.used = False # These objects become immutable after use in configure_file.
- ObjectHolder.__init__(self, build.ConfigurationData(), pv)
- self.methods.update({'set': self.set_method,
- 'set10': self.set10_method,
- 'set_quoted': self.set_quoted_method,
- 'has': self.has_method,
- 'get': self.get_method,
- 'get_unquoted': self.get_unquoted_method,
- 'merge_from': self.merge_from_method,
- })
- def is_used(self):
- return self.used
- def mark_used(self):
- self.used = True
- def validate_args(self, args, kwargs):
- if len(args) == 1 and isinstance(args[0], list) and len(args[0]) == 2:
- mlog.deprecation('Passing a list as the single argument to '
- 'configuration_data.set is deprecated. This will '
- 'become a hard error in the future.',
- location=self.current_node)
- args = args[0]
- if len(args) != 2:
- raise InterpreterException("Configuration set requires 2 arguments.")
- if self.used:
- raise InterpreterException("Can not set values on configuration object that has been used.")
- name = args[0]
- val = args[1]
- if not isinstance(val, (int, str)):
- msg = 'Setting a configuration data value to {!r} is invalid, ' \
- 'and will fail at configure_file(). If you are using it ' \
- 'just to store some values, please use a dict instead.'
- mlog.deprecation(msg.format(val), location=self.current_node)
- desc = kwargs.get('description', None)
- if not isinstance(name, str):
- raise InterpreterException("First argument to set must be a string.")
- if desc is not None and not isinstance(desc, str):
- raise InterpreterException('Description must be a string.')
- return name, val, desc
- @noArgsFlattening
- def set_method(self, args, kwargs):
- (name, val, desc) = self.validate_args(args, kwargs)
- self.held_object.values[name] = (val, desc)
- def set_quoted_method(self, args, kwargs):
- (name, val, desc) = self.validate_args(args, kwargs)
- if not isinstance(val, str):
- raise InterpreterException("Second argument to set_quoted must be a string.")
- escaped_val = '\\"'.join(val.split('"'))
- self.held_object.values[name] = ('"' + escaped_val + '"', desc)
- def set10_method(self, args, kwargs):
- (name, val, desc) = self.validate_args(args, kwargs)
- if val:
- self.held_object.values[name] = (1, desc)
- else:
- self.held_object.values[name] = (0, desc)
- def has_method(self, args, kwargs):
- return args[0] in self.held_object.values
- @FeatureNew('configuration_data.get()', '0.38.0')
- @noArgsFlattening
- def get_method(self, args, kwargs):
- if len(args) < 1 or len(args) > 2:
- raise InterpreterException('Get method takes one or two arguments.')
- name = args[0]
- if name in self.held_object:
- return self.held_object.get(name)[0]
- if len(args) > 1:
- return args[1]
- raise InterpreterException('Entry %s not in configuration data.' % name)
- @FeatureNew('configuration_data.get_unquoted()', '0.44.0')
- def get_unquoted_method(self, args, kwargs):
- if len(args) < 1 or len(args) > 2:
- raise InterpreterException('Get method takes one or two arguments.')
- name = args[0]
- if name in self.held_object:
- val = self.held_object.get(name)[0]
- elif len(args) > 1:
- val = args[1]
- else:
- raise InterpreterException('Entry %s not in configuration data.' % name)
- if val[0] == '"' and val[-1] == '"':
- return val[1:-1]
- return val
- def get(self, name):
- return self.held_object.values[name] # (val, desc)
- def keys(self):
- return self.held_object.values.keys()
- def merge_from_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Merge_from takes one positional argument.')
- from_object = args[0]
- if not isinstance(from_object, ConfigurationDataHolder):
- raise InterpreterException('Merge_from argument must be a configuration data object.')
- from_object = from_object.held_object
- for k, v in from_object.values.items():
- self.held_object.values[k] = v
- # Interpreter objects can not be pickled so we must have
- # these wrappers.
- class DependencyHolder(InterpreterObject, ObjectHolder):
- def __init__(self, dep, pv):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, dep, pv)
- self.methods.update({'found': self.found_method,
- 'type_name': self.type_name_method,
- 'version': self.version_method,
- 'name': self.name_method,
- 'get_pkgconfig_variable': self.pkgconfig_method,
- 'get_configtool_variable': self.configtool_method,
- 'partial_dependency': self.partial_dependency_method,
- })
- def found(self):
- return self.found_method([], {})
- @noPosargs
- @permittedKwargs({})
- def type_name_method(self, args, kwargs):
- return self.held_object.type_name
- @noPosargs
- @permittedKwargs({})
- def found_method(self, args, kwargs):
- if self.held_object.type_name == 'internal':
- return True
- return self.held_object.found()
- @noPosargs
- @permittedKwargs({})
- def version_method(self, args, kwargs):
- return self.held_object.get_version()
- @noPosargs
- @permittedKwargs({})
- def name_method(self, args, kwargs):
- return self.held_object.get_name()
- @permittedKwargs({'define_variable', 'default'})
- def pkgconfig_method(self, args, kwargs):
- args = listify(args)
- if len(args) != 1:
- raise InterpreterException('get_pkgconfig_variable takes exactly one argument.')
- varname = args[0]
- if not isinstance(varname, str):
- raise InterpreterException('Variable name must be a string.')
- return self.held_object.get_pkgconfig_variable(varname, kwargs)
- @FeatureNew('dep.get_configtool_variable', '0.44.0')
- @permittedKwargs({})
- def configtool_method(self, args, kwargs):
- args = listify(args)
- if len(args) != 1:
- raise InterpreterException('get_configtool_variable takes exactly one argument.')
- varname = args[0]
- if not isinstance(varname, str):
- raise InterpreterException('Variable name must be a string.')
- return self.held_object.get_configtool_variable(varname)
- @FeatureNew('dep.partial_dependency', '0.46.0')
- @noPosargs
- @permittedKwargs(permitted_method_kwargs['partial_dependency'])
- def partial_dependency_method(self, args, kwargs):
- pdep = self.held_object.get_partial_dependency(**kwargs)
- return DependencyHolder(pdep, self.subproject)
- class InternalDependencyHolder(InterpreterObject, ObjectHolder):
- def __init__(self, dep, pv):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, dep, pv)
- self.methods.update({'found': self.found_method,
- 'version': self.version_method,
- 'partial_dependency': self.partial_dependency_method,
- })
- @noPosargs
- @permittedKwargs({})
- def found_method(self, args, kwargs):
- return True
- @noPosargs
- @permittedKwargs({})
- def version_method(self, args, kwargs):
- return self.held_object.get_version()
- @FeatureNew('dep.partial_dependency', '0.46.0')
- @noPosargs
- @permittedKwargs(permitted_method_kwargs['partial_dependency'])
- def partial_dependency_method(self, args, kwargs):
- pdep = self.held_object.get_partial_dependency(**kwargs)
- return DependencyHolder(pdep, self.subproject)
- class ExternalProgramHolder(InterpreterObject, ObjectHolder):
- def __init__(self, ep):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, ep)
- self.methods.update({'found': self.found_method,
- 'path': self.path_method})
- @noPosargs
- @permittedKwargs({})
- def found_method(self, args, kwargs):
- return self.found()
- @noPosargs
- @permittedKwargs({})
- def path_method(self, args, kwargs):
- return self.held_object.get_path()
- def found(self):
- return isinstance(self.held_object, build.Executable) or self.held_object.found()
- def get_command(self):
- return self.held_object.get_command()
- def get_name(self):
- return self.held_object.get_name()
- class ExternalLibraryHolder(InterpreterObject, ObjectHolder):
- def __init__(self, el, pv):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, el, pv)
- self.methods.update({'found': self.found_method,
- 'type_name': self.type_name_method,
- 'partial_dependency': self.partial_dependency_method,
- })
- def found(self):
- return self.held_object.found()
- @noPosargs
- @permittedKwargs({})
- def type_name_method(self, args, kwargs):
- return self.held_object.type_name
- @noPosargs
- @permittedKwargs({})
- def found_method(self, args, kwargs):
- return self.found()
- def get_name(self):
- return self.held_object.name
- def get_compile_args(self):
- return self.held_object.get_compile_args()
- def get_link_args(self):
- return self.held_object.get_link_args()
- def get_exe_args(self):
- return self.held_object.get_exe_args()
- @FeatureNew('dep.partial_dependency', '0.46.0')
- @noPosargs
- @permittedKwargs(permitted_method_kwargs['partial_dependency'])
- def partial_dependency_method(self, args, kwargs):
- pdep = self.held_object.get_partial_dependency(**kwargs)
- return DependencyHolder(pdep, self.subproject)
- class GeneratorHolder(InterpreterObject, ObjectHolder):
- @FeatureNewKwargs('generator', '0.43.0', ['capture'])
- def __init__(self, interp, args, kwargs):
- self.interpreter = interp
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, build.Generator(args, kwargs), interp.subproject)
- self.methods.update({'process': self.process_method})
- @FeatureNewKwargs('generator.process', '0.45.0', ['preserve_path_from'])
- @permittedKwargs({'extra_args', 'preserve_path_from'})
- def process_method(self, args, kwargs):
- extras = mesonlib.stringlistify(kwargs.get('extra_args', []))
- if 'preserve_path_from' in kwargs:
- preserve_path_from = kwargs['preserve_path_from']
- if not isinstance(preserve_path_from, str):
- raise InvalidArguments('Preserve_path_from must be a string.')
- preserve_path_from = os.path.normpath(preserve_path_from)
- if not os.path.isabs(preserve_path_from):
- # This is a bit of a hack. Fix properly before merging.
- raise InvalidArguments('Preserve_path_from must be an absolute path for now. Sorry.')
- else:
- preserve_path_from = None
- gl = self.held_object.process_files('Generator', args, self.interpreter,
- preserve_path_from, extra_args=extras)
- return GeneratedListHolder(gl)
- class GeneratedListHolder(InterpreterObject, ObjectHolder):
- def __init__(self, arg1, extra_args=[]):
- InterpreterObject.__init__(self)
- if isinstance(arg1, GeneratorHolder):
- ObjectHolder.__init__(self, build.GeneratedList(arg1.held_object, extra_args))
- else:
- ObjectHolder.__init__(self, arg1)
- def __repr__(self):
- r = '<{}: {!r}>'
- return r.format(self.__class__.__name__, self.held_object.get_outputs())
- def add_file(self, a):
- self.held_object.add_file(a)
- # A machine that's statically known from the cross file
- class MachineHolder(InterpreterObject, ObjectHolder):
- def __init__(self, machine_info):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, machine_info)
- self.methods.update({'system': self.system_method,
- 'cpu': self.cpu_method,
- 'cpu_family': self.cpu_family_method,
- 'endian': self.endian_method,
- })
- @noPosargs
- @permittedKwargs({})
- def cpu_family_method(self, args, kwargs):
- return self.held_object.cpu_family
- @noPosargs
- @permittedKwargs({})
- def cpu_method(self, args, kwargs):
- return self.held_object.cpu
- @noPosargs
- @permittedKwargs({})
- def system_method(self, args, kwargs):
- return self.held_object.system
- @noPosargs
- @permittedKwargs({})
- def endian_method(self, args, kwargs):
- return self.held_object.endian
- class IncludeDirsHolder(InterpreterObject, ObjectHolder):
- def __init__(self, idobj):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, idobj)
- class Headers(InterpreterObject):
- def __init__(self, sources, kwargs):
- InterpreterObject.__init__(self)
- self.sources = sources
- self.install_subdir = kwargs.get('subdir', '')
- self.custom_install_dir = kwargs.get('install_dir', None)
- self.custom_install_mode = kwargs.get('install_mode', None)
- if self.custom_install_dir is not None:
- if not isinstance(self.custom_install_dir, str):
- raise InterpreterException('Custom_install_dir must be a string.')
- def set_install_subdir(self, subdir):
- self.install_subdir = subdir
- def get_install_subdir(self):
- return self.install_subdir
- def get_sources(self):
- return self.sources
- def get_custom_install_dir(self):
- return self.custom_install_dir
- def get_custom_install_mode(self):
- return self.custom_install_mode
- class DataHolder(InterpreterObject, ObjectHolder):
- def __init__(self, data):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, data)
- def get_source_subdir(self):
- return self.held_object.source_subdir
- def get_sources(self):
- return self.held_object.sources
- def get_install_dir(self):
- return self.held_object.install_dir
- class InstallDir(InterpreterObject):
- def __init__(self, src_subdir, inst_subdir, install_dir, install_mode, exclude, strip_directory):
- InterpreterObject.__init__(self)
- self.source_subdir = src_subdir
- self.installable_subdir = inst_subdir
- self.install_dir = install_dir
- self.install_mode = install_mode
- self.exclude = exclude
- self.strip_directory = strip_directory
- class Man(InterpreterObject):
- def __init__(self, sources, kwargs):
- InterpreterObject.__init__(self)
- self.sources = sources
- self.validate_sources()
- self.custom_install_dir = kwargs.get('install_dir', None)
- self.custom_install_mode = kwargs.get('install_mode', None)
- if self.custom_install_dir is not None and not isinstance(self.custom_install_dir, str):
- raise InterpreterException('Custom_install_dir must be a string.')
- def validate_sources(self):
- for s in self.sources:
- try:
- num = int(s.split('.')[-1])
- except (IndexError, ValueError):
- num = 0
- if num < 1 or num > 8:
- raise InvalidArguments('Man file must have a file extension of a number between 1 and 8')
- def get_custom_install_dir(self):
- return self.custom_install_dir
- def get_custom_install_mode(self):
- return self.custom_install_mode
- def get_sources(self):
- return self.sources
- class GeneratedObjectsHolder(InterpreterObject, ObjectHolder):
- def __init__(self, held_object):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, held_object)
- class TargetHolder(InterpreterObject, ObjectHolder):
- def __init__(self, target, interp):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, target, interp.subproject)
- self.interpreter = interp
- class BuildTargetHolder(TargetHolder):
- def __init__(self, target, interp):
- super().__init__(target, interp)
- self.methods.update({'extract_objects': self.extract_objects_method,
- 'extract_all_objects': self.extract_all_objects_method,
- 'get_id': self.get_id_method,
- 'outdir': self.outdir_method,
- 'full_path': self.full_path_method,
- 'private_dir_include': self.private_dir_include_method,
- })
- def __repr__(self):
- r = '<{} {}: {}>'
- h = self.held_object
- return r.format(self.__class__.__name__, h.get_id(), h.filename)
- def is_cross(self):
- return self.held_object.is_cross()
- @noPosargs
- @permittedKwargs({})
- def private_dir_include_method(self, args, kwargs):
- return IncludeDirsHolder(build.IncludeDirs('', [], False,
- [self.interpreter.backend.get_target_private_dir(self.held_object)]))
- @noPosargs
- @permittedKwargs({})
- def full_path_method(self, args, kwargs):
- return self.interpreter.backend.get_target_filename_abs(self.held_object)
- @noPosargs
- @permittedKwargs({})
- def outdir_method(self, args, kwargs):
- return self.interpreter.backend.get_target_dir(self.held_object)
- @permittedKwargs({})
- def extract_objects_method(self, args, kwargs):
- gobjs = self.held_object.extract_objects(args)
- return GeneratedObjectsHolder(gobjs)
- @FeatureNewKwargs('extract_all_objects', '0.46.0', ['recursive'])
- @noPosargs
- @permittedKwargs({'recursive'})
- def extract_all_objects_method(self, args, kwargs):
- recursive = kwargs.get('recursive', False)
- gobjs = self.held_object.extract_all_objects(recursive)
- if gobjs.objlist and 'recursive' not in kwargs:
- mlog.warning('extract_all_objects called without setting recursive '
- 'keyword argument. Meson currently defaults to '
- 'non-recursive to maintain backward compatibility but '
- 'the default will be changed in the future.',
- location=self.current_node)
- return GeneratedObjectsHolder(gobjs)
- @noPosargs
- @permittedKwargs({})
- def get_id_method(self, args, kwargs):
- return self.held_object.get_id()
- class ExecutableHolder(BuildTargetHolder):
- def __init__(self, target, interp):
- super().__init__(target, interp)
- class StaticLibraryHolder(BuildTargetHolder):
- def __init__(self, target, interp):
- super().__init__(target, interp)
- class SharedLibraryHolder(BuildTargetHolder):
- def __init__(self, target, interp):
- super().__init__(target, interp)
- # Set to True only when called from self.func_shared_lib().
- target.shared_library_only = False
- class BothLibrariesHolder(BuildTargetHolder):
- def __init__(self, shared_holder, static_holder, interp):
- # FIXME: This build target always represents the shared library, but
- # that should be configurable.
- super().__init__(shared_holder.held_object, interp)
- self.shared_holder = shared_holder
- self.static_holder = static_holder
- self.methods.update({'get_shared_lib': self.get_shared_lib_method,
- 'get_static_lib': self.get_static_lib_method,
- })
- def __repr__(self):
- r = '<{} {}: {}, {}: {}>'
- h1 = self.shared_holder.held_object
- h2 = self.static_holder.held_object
- return r.format(self.__class__.__name__, h1.get_id(), h1.filename, h2.get_id(), h2.filename)
- @noPosargs
- @permittedKwargs({})
- def get_shared_lib_method(self, args, kwargs):
- return self.shared_holder
- @noPosargs
- @permittedKwargs({})
- def get_static_lib_method(self, args, kwargs):
- return self.static_holder
- class SharedModuleHolder(BuildTargetHolder):
- def __init__(self, target, interp):
- super().__init__(target, interp)
- class JarHolder(BuildTargetHolder):
- def __init__(self, target, interp):
- super().__init__(target, interp)
- class CustomTargetIndexHolder(InterpreterObject, ObjectHolder):
- def __init__(self, object_to_hold):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, object_to_hold)
- class CustomTargetHolder(TargetHolder):
- def __init__(self, target, interp):
- super().__init__(target, interp)
- self.methods.update({'full_path': self.full_path_method,
- })
- def __repr__(self):
- r = '<{} {}: {}>'
- h = self.held_object
- return r.format(self.__class__.__name__, h.get_id(), h.command)
- @noPosargs
- @permittedKwargs({})
- def full_path_method(self, args, kwargs):
- return self.interpreter.backend.get_target_filename_abs(self.held_object)
- def __getitem__(self, index):
- return CustomTargetIndexHolder(self.held_object[index])
- def __setitem__(self, index, value):
- raise InterpreterException('Cannot set a member of a CustomTarget')
- def __delitem__(self, index):
- raise InterpreterException('Cannot delete a member of a CustomTarget')
- def outdir_include(self):
- return IncludeDirsHolder(build.IncludeDirs('', [], False,
- [os.path.join('@BUILD_ROOT@', self.interpreter.backend.get_target_dir(self.held_object))]))
- class RunTargetHolder(InterpreterObject, ObjectHolder):
- def __init__(self, name, command, args, dependencies, subdir, subproject):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, build.RunTarget(name, command, args, dependencies, subdir, subproject))
- def __repr__(self):
- r = '<{} {}: {}>'
- h = self.held_object
- return r.format(self.__class__.__name__, h.get_id(), h.command)
- class Test(InterpreterObject):
- def __init__(self, name, project, suite, exe, depends, is_parallel,
- cmd_args, env, should_fail, timeout, workdir):
- InterpreterObject.__init__(self)
- self.name = name
- self.suite = suite
- self.project_name = project
- self.exe = exe
- self.depends = depends
- self.is_parallel = is_parallel
- self.cmd_args = cmd_args
- self.env = env
- self.should_fail = should_fail
- self.timeout = timeout
- self.workdir = workdir
- def get_exe(self):
- return self.exe
- def get_name(self):
- return self.name
- class SubprojectHolder(InterpreterObject, ObjectHolder):
- def __init__(self, subinterpreter, subproject_dir, name):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, subinterpreter)
- self.name = name
- self.subproject_dir = subproject_dir
- self.methods.update({'get_variable': self.get_variable_method,
- 'found': self.found_method,
- })
- @noPosargs
- @permittedKwargs({})
- def found_method(self, args, kwargs):
- return self.found()
- def found(self):
- return self.held_object is not None
- @permittedKwargs({})
- def get_variable_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Get_variable takes one argument.')
- if not self.found():
- raise InterpreterException('Subproject "%s/%s" disabled can\'t get_variable on it.' % (
- self.subproject_dir, self.name))
- varname = args[0]
- if not isinstance(varname, str):
- raise InterpreterException('Get_variable takes a string argument.')
- if varname not in self.held_object.variables:
- raise InvalidArguments('Requested variable "{0}" not found.'.format(varname))
- return self.held_object.variables[varname]
- header_permitted_kwargs = set([
- 'required',
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- ])
- find_library_permitted_kwargs = set([
- 'has_headers',
- 'required',
- 'dirs',
- ])
- find_library_permitted_kwargs |= set(['header_' + k for k in header_permitted_kwargs])
- class CompilerHolder(InterpreterObject):
- def __init__(self, compiler, env, subproject):
- InterpreterObject.__init__(self)
- self.compiler = compiler
- self.environment = env
- self.subproject = subproject
- self.methods.update({'compiles': self.compiles_method,
- 'links': self.links_method,
- 'get_id': self.get_id_method,
- 'compute_int': self.compute_int_method,
- 'sizeof': self.sizeof_method,
- 'get_define': self.get_define_method,
- 'check_header': self.check_header_method,
- 'has_header': self.has_header_method,
- 'has_header_symbol': self.has_header_symbol_method,
- 'run': self.run_method,
- 'has_function': self.has_function_method,
- 'has_member': self.has_member_method,
- 'has_members': self.has_members_method,
- 'has_type': self.has_type_method,
- 'alignment': self.alignment_method,
- 'version': self.version_method,
- 'cmd_array': self.cmd_array_method,
- 'find_library': self.find_library_method,
- 'has_argument': self.has_argument_method,
- 'has_function_attribute': self.has_func_attribute_method,
- 'get_supported_function_attributes': self.get_supported_function_attributes_method,
- 'has_multi_arguments': self.has_multi_arguments_method,
- 'get_supported_arguments': self.get_supported_arguments_method,
- 'first_supported_argument': self.first_supported_argument_method,
- 'has_link_argument': self.has_link_argument_method,
- 'has_multi_link_arguments': self.has_multi_link_arguments_method,
- 'get_supported_link_arguments': self.get_supported_link_arguments_method,
- 'first_supported_link_argument': self.first_supported_link_argument_method,
- 'unittest_args': self.unittest_args_method,
- 'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method,
- 'get_argument_syntax': self.get_argument_syntax_method,
- })
- def _dep_msg(self, deps, endl):
- msg_single = 'with dependency {}'
- msg_many = 'with dependencies {}'
- if not deps:
- return endl
- if endl is None:
- endl = ''
- tpl = msg_many if len(deps) > 1 else msg_single
- names = []
- for d in deps:
- if isinstance(d, dependencies.ExternalLibrary):
- name = '-l' + d.name
- else:
- name = d.name
- names.append(name)
- return tpl.format(', '.join(names)) + endl
- @noPosargs
- @permittedKwargs({})
- def version_method(self, args, kwargs):
- return self.compiler.version
- @noPosargs
- @permittedKwargs({})
- def cmd_array_method(self, args, kwargs):
- return self.compiler.exelist
- def determine_args(self, kwargs, mode='link'):
- nobuiltins = kwargs.get('no_builtin_args', False)
- if not isinstance(nobuiltins, bool):
- raise InterpreterException('Type of no_builtin_args not a boolean.')
- args = []
- incdirs = extract_as_list(kwargs, 'include_directories')
- for i in incdirs:
- if not isinstance(i, IncludeDirsHolder):
- raise InterpreterException('Include directories argument must be an include_directories object.')
- for idir in i.held_object.get_incdirs():
- idir = os.path.join(self.environment.get_source_dir(),
- i.held_object.get_curdir(), idir)
- args += self.compiler.get_include_args(idir, False)
- native = kwargs.get('native', None)
- if native:
- for_machine = MachineChoice.BUILD
- else:
- for_machine = MachineChoice.HOST
- if not nobuiltins:
- opts = self.environment.coredata.compiler_options[for_machine]
- args += self.compiler.get_option_compile_args(opts)
- if mode == 'link':
- args += self.compiler.get_option_link_args(opts)
- args += mesonlib.stringlistify(kwargs.get('args', []))
- return args
- def determine_dependencies(self, kwargs, endl=':'):
- deps = kwargs.get('dependencies', None)
- if deps is not None:
- deps = listify(deps)
- final_deps = []
- for d in deps:
- try:
- d = d.held_object
- except Exception:
- pass
- if isinstance(d, InternalDependency) or not isinstance(d, Dependency):
- raise InterpreterException('Dependencies must be external dependencies')
- final_deps.append(d)
- deps = final_deps
- return deps, self._dep_msg(deps, endl)
- @permittedKwargs({
- 'prefix',
- 'args',
- 'dependencies',
- })
- def alignment_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Alignment method takes exactly one positional argument.')
- check_stringlist(args)
- typename = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of alignment must be a string.')
- extra_args = mesonlib.stringlistify(kwargs.get('args', []))
- deps, msg = self.determine_dependencies(kwargs)
- result = self.compiler.alignment(typename, prefix, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- mlog.log('Checking for alignment of', mlog.bold(typename, True), msg, result)
- return result
- @permittedKwargs({
- 'name',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def run_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Run method takes exactly one positional argument.')
- code = args[0]
- if isinstance(code, mesonlib.File):
- code = mesonlib.File.from_absolute_file(
- code.rel_to_builddir(self.environment.source_dir))
- elif not isinstance(code, str):
- raise InvalidArguments('Argument must be string or file.')
- testname = kwargs.get('name', '')
- if not isinstance(testname, str):
- raise InterpreterException('Testname argument must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs, endl=None)
- result = self.compiler.run(code, self.environment, extra_args=extra_args,
- dependencies=deps)
- if len(testname) > 0:
- if not result.compiled:
- h = mlog.red('DID NOT COMPILE')
- elif result.returncode == 0:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO (%d)' % result.returncode)
- mlog.log('Checking if', mlog.bold(testname, True), msg, 'runs:', h)
- return TryRunResultHolder(result)
- @noPosargs
- @permittedKwargs({})
- def get_id_method(self, args, kwargs):
- return self.compiler.get_id()
- @noPosargs
- @permittedKwargs({})
- def symbols_have_underscore_prefix_method(self, args, kwargs):
- '''
- Check if the compiler prefixes _ (underscore) to global C symbols
- See: https://en.wikipedia.org/wiki/Name_mangling#C
- '''
- return self.compiler.symbols_have_underscore_prefix(self.environment)
- @noPosargs
- @permittedKwargs({})
- def unittest_args_method(self, args, kwargs):
- '''
- This function is deprecated and should not be used.
- It can be removed in a future version of Meson.
- '''
- if not hasattr(self.compiler, 'get_feature_args'):
- raise InterpreterException('This {} compiler has no feature arguments.'.format(self.compiler.get_display_language()))
- build_to_src = os.path.relpath(self.environment.get_source_dir(), self.environment.get_build_dir())
- return self.compiler.get_feature_args({'unittest': 'true'}, build_to_src)
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def has_member_method(self, args, kwargs):
- if len(args) != 2:
- raise InterpreterException('Has_member takes exactly two arguments.')
- check_stringlist(args)
- typename = args[0]
- membername = args[1]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_member must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- had = self.compiler.has_members(typename, [membername], prefix,
- self.environment,
- extra_args=extra_args,
- dependencies=deps)
- if had:
- hadtxt = mlog.green('YES')
- else:
- hadtxt = mlog.red('NO')
- mlog.log('Checking whether type', mlog.bold(typename, True),
- 'has member', mlog.bold(membername, True), msg, hadtxt)
- return had
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def has_members_method(self, args, kwargs):
- if len(args) < 2:
- raise InterpreterException('Has_members needs at least two arguments.')
- check_stringlist(args)
- typename = args[0]
- membernames = args[1:]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_members must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- had = self.compiler.has_members(typename, membernames, prefix,
- self.environment,
- extra_args=extra_args,
- dependencies=deps)
- if had:
- hadtxt = mlog.green('YES')
- else:
- hadtxt = mlog.red('NO')
- members = mlog.bold(', '.join(['"{}"'.format(m) for m in membernames]))
- mlog.log('Checking whether type', mlog.bold(typename, True),
- 'has members', members, msg, hadtxt)
- return had
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def has_function_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Has_function takes exactly one argument.')
- check_stringlist(args)
- funcname = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_function must be a string.')
- extra_args = self.determine_args(kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- had = self.compiler.has_function(funcname, prefix, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- if had:
- hadtxt = mlog.green('YES')
- else:
- hadtxt = mlog.red('NO')
- mlog.log('Checking for function', mlog.bold(funcname, True), msg, hadtxt)
- return had
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def has_type_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Has_type takes exactly one argument.')
- check_stringlist(args)
- typename = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_type must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- had = self.compiler.has_type(typename, prefix, self.environment,
- extra_args=extra_args, dependencies=deps)
- if had:
- hadtxt = mlog.green('YES')
- else:
- hadtxt = mlog.red('NO')
- mlog.log('Checking for type', mlog.bold(typename, True), msg, hadtxt)
- return had
- @FeatureNew('compiler.compute_int', '0.40.0')
- @permittedKwargs({
- 'prefix',
- 'low',
- 'high',
- 'guess',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def compute_int_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Compute_int takes exactly one argument.')
- check_stringlist(args)
- expression = args[0]
- prefix = kwargs.get('prefix', '')
- low = kwargs.get('low', None)
- high = kwargs.get('high', None)
- guess = kwargs.get('guess', None)
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of compute_int must be a string.')
- if low is not None and not isinstance(low, int):
- raise InterpreterException('Low argument of compute_int must be an int.')
- if high is not None and not isinstance(high, int):
- raise InterpreterException('High argument of compute_int must be an int.')
- if guess is not None and not isinstance(guess, int):
- raise InterpreterException('Guess argument of compute_int must be an int.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- res = self.compiler.compute_int(expression, low, high, guess, prefix,
- self.environment, extra_args=extra_args,
- dependencies=deps)
- mlog.log('Computing int of', mlog.bold(expression, True), msg, res)
- return res
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def sizeof_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Sizeof takes exactly one argument.')
- check_stringlist(args)
- element = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of sizeof must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- esize = self.compiler.sizeof(element, prefix, self.environment,
- extra_args=extra_args, dependencies=deps)
- mlog.log('Checking for size of', mlog.bold(element, True), msg, esize)
- return esize
- @FeatureNew('compiler.get_define', '0.40.0')
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def get_define_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('get_define() takes exactly one argument.')
- check_stringlist(args)
- element = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of get_define() must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- value = self.compiler.get_define(element, prefix, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- mlog.log('Fetching value of define', mlog.bold(element, True), msg, value)
- return value
- @permittedKwargs({
- 'name',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def compiles_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('compiles method takes exactly one argument.')
- code = args[0]
- if isinstance(code, mesonlib.File):
- code = mesonlib.File.from_absolute_file(
- code.rel_to_builddir(self.environment.source_dir))
- elif not isinstance(code, str):
- raise InvalidArguments('Argument must be string or file.')
- testname = kwargs.get('name', '')
- if not isinstance(testname, str):
- raise InterpreterException('Testname argument must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs, endl=None)
- result = self.compiler.compiles(code, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- if len(testname) > 0:
- if result:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- mlog.log('Checking if', mlog.bold(testname, True), msg, 'compiles:', h)
- return result
- @permittedKwargs({
- 'name',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def links_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('links method takes exactly one argument.')
- code = args[0]
- if isinstance(code, mesonlib.File):
- code = mesonlib.File.from_absolute_file(
- code.rel_to_builddir(self.environment.source_dir))
- elif not isinstance(code, str):
- raise InvalidArguments('Argument must be string or file.')
- testname = kwargs.get('name', '')
- if not isinstance(testname, str):
- raise InterpreterException('Testname argument must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs, endl=None)
- result = self.compiler.links(code, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- if len(testname) > 0:
- if result:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- mlog.log('Checking if', mlog.bold(testname, True), msg, 'links:', h)
- return result
- @FeatureNew('compiler.check_header', '0.47.0')
- @FeatureNewKwargs('compiler.check_header', '0.50.0', ['required'])
- @permittedKwargs(header_permitted_kwargs)
- def check_header_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('check_header method takes exactly one argument.')
- check_stringlist(args)
- hname = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_header must be a string.')
- disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
- if disabled:
- mlog.log('Check usable header', mlog.bold(hname, True), 'skipped: feature', mlog.bold(feature), 'disabled')
- return False
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- haz = self.compiler.check_header(hname, prefix, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- if required and not haz:
- raise InterpreterException('{} header {!r} not usable'.format(self.compiler.get_display_language(), hname))
- elif haz:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- mlog.log('Check usable header', mlog.bold(hname, True), msg, h)
- return haz
- @FeatureNewKwargs('compiler.has_header', '0.50.0', ['required'])
- @permittedKwargs(header_permitted_kwargs)
- def has_header_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('has_header method takes exactly one argument.')
- check_stringlist(args)
- hname = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_header must be a string.')
- disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
- if disabled:
- mlog.log('Has header', mlog.bold(hname, True), 'skipped: feature', mlog.bold(feature), 'disabled')
- return False
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- haz = self.compiler.has_header(hname, prefix, self.environment,
- extra_args=extra_args, dependencies=deps)
- if required and not haz:
- raise InterpreterException('{} header {!r} not found'.format(self.compiler.get_display_language(), hname))
- elif haz:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- mlog.log('Has header', mlog.bold(hname, True), msg, h)
- return haz
- @FeatureNewKwargs('compiler.has_header_symbol', '0.50.0', ['required'])
- @permittedKwargs(header_permitted_kwargs)
- def has_header_symbol_method(self, args, kwargs):
- if len(args) != 2:
- raise InterpreterException('has_header_symbol method takes exactly two arguments.')
- check_stringlist(args)
- hname = args[0]
- symbol = args[1]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_header_symbol must be a string.')
- disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
- if disabled:
- mlog.log('Header <{0}> has symbol'.format(hname), mlog.bold(symbol, True), 'skipped: feature', mlog.bold(feature), 'disabled')
- return False
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- haz = self.compiler.has_header_symbol(hname, symbol, prefix, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- if required and not haz:
- raise InterpreterException('{} symbol {} not found in header {}'.format(self.compiler.get_display_language(), symbol, hname))
- elif haz:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- mlog.log('Header <{0}> has symbol'.format(hname), mlog.bold(symbol, True), msg, h)
- return haz
- def notfound_library(self, libname):
- lib = dependencies.ExternalLibrary(libname, None,
- self.environment,
- self.compiler.language,
- silent=True)
- return ExternalLibraryHolder(lib, self.subproject)
- @FeatureNewKwargs('compiler.find_library', '0.50.0', ['has_headers'])
- @FeatureNewKwargs('compiler.find_library', '0.49.0', ['disabler'])
- @disablerIfNotFound
- @permittedKwargs(find_library_permitted_kwargs)
- def find_library_method(self, args, kwargs):
- # TODO add dependencies support?
- if len(args) != 1:
- raise InterpreterException('find_library method takes one argument.')
- libname = args[0]
- if not isinstance(libname, str):
- raise InterpreterException('Library name not a string.')
- disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
- if disabled:
- mlog.log('Library', mlog.bold(libname), 'skipped: feature', mlog.bold(feature), 'disabled')
- return self.notfound_library(libname)
- has_header_kwargs = {k[7:]: v for k, v in kwargs.items() if k.startswith('header_')}
- has_header_kwargs['required'] = required
- headers = mesonlib.stringlistify(kwargs.get('has_headers', []))
- for h in headers:
- if not self.has_header_method([h], has_header_kwargs):
- return self.notfound_library(libname)
- search_dirs = mesonlib.stringlistify(kwargs.get('dirs', []))
- for i in search_dirs:
- if not os.path.isabs(i):
- raise InvalidCode('Search directory %s is not an absolute path.' % i)
- linkargs = self.compiler.find_library(libname, self.environment, search_dirs)
- if required and not linkargs:
- raise InterpreterException('{} library {!r} not found'.format(self.compiler.get_display_language(), libname))
- lib = dependencies.ExternalLibrary(libname, linkargs, self.environment,
- self.compiler.language)
- return ExternalLibraryHolder(lib, self.subproject)
- @permittedKwargs({})
- def has_argument_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- if len(args) != 1:
- raise InterpreterException('has_argument takes exactly one argument.')
- return self.has_multi_arguments_method(args, kwargs)
- @permittedKwargs({})
- def has_multi_arguments_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- result = self.compiler.has_multi_arguments(args, self.environment)
- if result:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- mlog.log(
- 'Compiler for {} supports arguments {}:'.format(
- self.compiler.get_display_language(), ' '.join(args)),
- h)
- return result
- @FeatureNew('compiler.get_supported_arguments', '0.43.0')
- @permittedKwargs({})
- def get_supported_arguments_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- supported_args = []
- for arg in args:
- if self.has_argument_method(arg, kwargs):
- supported_args.append(arg)
- return supported_args
- @permittedKwargs({})
- def first_supported_argument_method(self, args, kwargs):
- for i in mesonlib.stringlistify(args):
- if self.has_argument_method(i, kwargs):
- mlog.log('First supported argument:', mlog.bold(i))
- return [i]
- mlog.log('First supported argument:', mlog.red('None'))
- return []
- @FeatureNew('compiler.has_link_argument', '0.46.0')
- @permittedKwargs({})
- def has_link_argument_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- if len(args) != 1:
- raise InterpreterException('has_link_argument takes exactly one argument.')
- return self.has_multi_link_arguments_method(args, kwargs)
- @FeatureNew('compiler.has_multi_link_argument', '0.46.0')
- @permittedKwargs({})
- def has_multi_link_arguments_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- result = self.compiler.has_multi_link_arguments(args, self.environment)
- if result:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- mlog.log(
- 'Compiler for {} supports link arguments {}:'.format(
- self.compiler.get_display_language(), ' '.join(args)),
- h)
- return result
- @FeatureNew('compiler.get_supported_link_arguments_method', '0.46.0')
- @permittedKwargs({})
- def get_supported_link_arguments_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- supported_args = []
- for arg in args:
- if self.has_link_argument_method(arg, kwargs):
- supported_args.append(arg)
- return supported_args
- @FeatureNew('compiler.first_supported_link_argument_method', '0.46.0')
- @permittedKwargs({})
- def first_supported_link_argument_method(self, args, kwargs):
- for i in mesonlib.stringlistify(args):
- if self.has_link_argument_method(i, kwargs):
- mlog.log('First supported link argument:', mlog.bold(i))
- return [i]
- mlog.log('First supported link argument:', mlog.red('None'))
- return []
- @FeatureNew('compiler.has_function_attribute', '0.48.0')
- @permittedKwargs({})
- def has_func_attribute_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- if len(args) != 1:
- raise InterpreterException('has_func_attribute takes exactly one argument.')
- result = self.compiler.has_func_attribute(args[0], self.environment)
- h = mlog.green('YES') if result else mlog.red('NO')
- mlog.log('Compiler for {} supports function attribute {}:'.format(self.compiler.get_display_language(), args[0]), h)
- return result
- @FeatureNew('compiler.get_supported_function_attributes', '0.48.0')
- @permittedKwargs({})
- def get_supported_function_attributes_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- return [a for a in args if self.has_func_attribute_method(a, kwargs)]
- @FeatureNew('compiler.get_argument_syntax_method', '0.49.0')
- @noPosargs
- @noKwargs
- def get_argument_syntax_method(self, args, kwargs):
- return self.compiler.get_argument_syntax()
- ModuleState = namedtuple('ModuleState', [
- 'build_to_src', 'subproject', 'subdir', 'current_lineno', 'environment',
- 'project_name', 'project_version', 'backend', 'compilers', 'targets',
- 'data', 'headers', 'man', 'global_args', 'project_args', 'build_machine',
- 'host_machine', 'target_machine', 'current_node'])
- class ModuleHolder(InterpreterObject, ObjectHolder):
- def __init__(self, modname, module, interpreter):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, module)
- self.modname = modname
- self.interpreter = interpreter
- def method_call(self, method_name, args, kwargs):
- try:
- fn = getattr(self.held_object, method_name)
- except AttributeError:
- raise InvalidArguments('Module %s does not have method %s.' % (self.modname, method_name))
- if method_name.startswith('_'):
- raise InvalidArguments('Function {!r} in module {!r} is private.'.format(method_name, self.modname))
- if not getattr(fn, 'no-args-flattening', False):
- args = flatten(args)
- # This is not 100% reliable but we can't use hash()
- # because the Build object contains dicts and lists.
- num_targets = len(self.interpreter.build.targets)
- state = ModuleState(
- build_to_src=mesonlib.relpath(self.interpreter.environment.get_source_dir(),
- self.interpreter.environment.get_build_dir()),
- subproject=self.interpreter.subproject,
- subdir=self.interpreter.subdir,
- current_lineno=self.interpreter.current_lineno,
- environment=self.interpreter.environment,
- project_name=self.interpreter.build.project_name,
- project_version=self.interpreter.build.dep_manifest[self.interpreter.active_projectname],
- # The backend object is under-used right now, but we will need it:
- # https://github.com/mesonbuild/meson/issues/1419
- backend=self.interpreter.backend,
- compilers=self.interpreter.build.compilers,
- targets=self.interpreter.build.targets,
- data=self.interpreter.build.data,
- headers=self.interpreter.build.get_headers(),
- man=self.interpreter.build.get_man(),
- global_args=self.interpreter.build.global_args,
- project_args=self.interpreter.build.projects_args.get(self.interpreter.subproject, {}),
- build_machine=self.interpreter.builtin['build_machine'].held_object,
- host_machine=self.interpreter.builtin['host_machine'].held_object,
- target_machine=self.interpreter.builtin['target_machine'].held_object,
- current_node=self.current_node
- )
- if self.held_object.is_snippet(method_name):
- value = fn(self.interpreter, state, args, kwargs)
- return self.interpreter.holderify(value)
- else:
- value = fn(state, args, kwargs)
- if num_targets != len(self.interpreter.build.targets):
- raise InterpreterException('Extension module altered internal state illegally.')
- return self.interpreter.module_method_callback(value)
- class MesonMain(InterpreterObject):
- def __init__(self, build, interpreter):
- InterpreterObject.__init__(self)
- self.build = build
- self.interpreter = interpreter
- self._found_source_scripts = {}
- self.methods.update({'get_compiler': self.get_compiler_method,
- 'is_cross_build': self.is_cross_build_method,
- 'has_exe_wrapper': self.has_exe_wrapper_method,
- 'is_unity': self.is_unity_method,
- 'is_subproject': self.is_subproject_method,
- 'current_source_dir': self.current_source_dir_method,
- 'current_build_dir': self.current_build_dir_method,
- 'source_root': self.source_root_method,
- 'build_root': self.build_root_method,
- 'add_install_script': self.add_install_script_method,
- 'add_postconf_script': self.add_postconf_script_method,
- 'add_dist_script': self.add_dist_script_method,
- 'install_dependency_manifest': self.install_dependency_manifest_method,
- 'override_find_program': self.override_find_program_method,
- 'project_version': self.project_version_method,
- 'project_license': self.project_license_method,
- 'version': self.version_method,
- 'project_name': self.project_name_method,
- 'get_cross_property': self.get_cross_property_method,
- 'backend': self.backend_method,
- })
- def _find_source_script(self, name, args):
- # Prefer scripts in the current source directory
- search_dir = os.path.join(self.interpreter.environment.source_dir,
- self.interpreter.subdir)
- key = (name, search_dir)
- if key in self._found_source_scripts:
- found = self._found_source_scripts[key]
- else:
- found = dependencies.ExternalProgram(name, search_dir=search_dir)
- if found.found():
- self._found_source_scripts[key] = found
- else:
- m = 'Script or command {!r} not found or not executable'
- raise InterpreterException(m.format(name))
- return build.RunScript(found.get_command(), args)
- @permittedKwargs({})
- def add_install_script_method(self, args, kwargs):
- if len(args) < 1:
- raise InterpreterException('add_install_script takes one or more arguments')
- check_stringlist(args, 'add_install_script args must be strings')
- script = self._find_source_script(args[0], args[1:])
- self.build.install_scripts.append(script)
- @permittedKwargs({})
- def add_postconf_script_method(self, args, kwargs):
- if len(args) < 1:
- raise InterpreterException('add_postconf_script takes one or more arguments')
- check_stringlist(args, 'add_postconf_script arguments must be strings')
- script = self._find_source_script(args[0], args[1:])
- self.build.postconf_scripts.append(script)
- @permittedKwargs({})
- def add_dist_script_method(self, args, kwargs):
- if len(args) < 1:
- raise InterpreterException('add_dist_script takes one or more arguments')
- if len(args) > 1:
- FeatureNew('Calling "add_dist_script" with multiple arguments', '0.49.0').use(self.interpreter.subproject)
- check_stringlist(args, 'add_dist_script argument must be a string')
- if self.interpreter.subproject != '':
- raise InterpreterException('add_dist_script may not be used in a subproject.')
- script = self._find_source_script(args[0], args[1:])
- self.build.dist_scripts.append(script)
- @noPosargs
- @permittedKwargs({})
- def current_source_dir_method(self, args, kwargs):
- src = self.interpreter.environment.source_dir
- sub = self.interpreter.subdir
- if sub == '':
- return src
- return os.path.join(src, sub)
- @noPosargs
- @permittedKwargs({})
- def current_build_dir_method(self, args, kwargs):
- src = self.interpreter.environment.build_dir
- sub = self.interpreter.subdir
- if sub == '':
- return src
- return os.path.join(src, sub)
- @noPosargs
- @permittedKwargs({})
- def backend_method(self, args, kwargs):
- return self.interpreter.backend.name
- @noPosargs
- @permittedKwargs({})
- def source_root_method(self, args, kwargs):
- return self.interpreter.environment.source_dir
- @noPosargs
- @permittedKwargs({})
- def build_root_method(self, args, kwargs):
- return self.interpreter.environment.build_dir
- @noPosargs
- @permittedKwargs({})
- def has_exe_wrapper_method(self, args, kwargs):
- if self.is_cross_build_method(None, None) and \
- self.build.environment.need_exe_wrapper():
- if self.build.environment.exe_wrapper is None:
- return False
- # We return True when exe_wrap is defined, when it's not needed, and
- # when we're compiling natively. The last two are semantically confusing.
- # Need to revisit this.
- return True
- @noPosargs
- @permittedKwargs({})
- def is_cross_build_method(self, args, kwargs):
- return self.build.environment.is_cross_build()
- @permittedKwargs({'native'})
- def get_compiler_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('get_compiler_method must have one and only one argument.')
- cname = args[0]
- native = kwargs.get('native', None)
- if native is None:
- if self.build.environment.is_cross_build():
- native = False
- else:
- native = True
- if not isinstance(native, bool):
- raise InterpreterException('Type of "native" must be a boolean.')
- if native:
- clist = self.build.compilers
- else:
- clist = self.build.cross_compilers
- if cname in clist:
- return CompilerHolder(clist[cname], self.build.environment, self.interpreter.subproject)
- raise InterpreterException('Tried to access compiler for unspecified language "%s".' % cname)
- @noPosargs
- @permittedKwargs({})
- def is_unity_method(self, args, kwargs):
- optval = self.interpreter.environment.coredata.get_builtin_option('unity')
- if optval == 'on' or (optval == 'subprojects' and self.interpreter.is_subproject()):
- return True
- return False
- @noPosargs
- @permittedKwargs({})
- def is_subproject_method(self, args, kwargs):
- return self.interpreter.is_subproject()
- @permittedKwargs({})
- def install_dependency_manifest_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Must specify manifest install file name')
- if not isinstance(args[0], str):
- raise InterpreterException('Argument must be a string.')
- self.build.dep_manifest_name = args[0]
- @FeatureNew('meson.override_find_program', '0.46.0')
- @permittedKwargs({})
- def override_find_program_method(self, args, kwargs):
- if len(args) != 2:
- raise InterpreterException('Override needs two arguments')
- name = args[0]
- exe = args[1]
- if not isinstance(name, str):
- raise InterpreterException('First argument must be a string')
- if hasattr(exe, 'held_object'):
- exe = exe.held_object
- if isinstance(exe, mesonlib.File):
- abspath = exe.absolute_path(self.interpreter.environment.source_dir,
- self.interpreter.environment.build_dir)
- if not os.path.exists(abspath):
- raise InterpreterException('Tried to override %s with a file that does not exist.' % name)
- exe = dependencies.ExternalProgram(abspath)
- if not isinstance(exe, (dependencies.ExternalProgram, build.Executable)):
- raise InterpreterException('Second argument must be an external program or executable.')
- self.interpreter.add_find_program_override(name, exe)
- @noPosargs
- @permittedKwargs({})
- def project_version_method(self, args, kwargs):
- return self.build.dep_manifest[self.interpreter.active_projectname]['version']
- @FeatureNew('meson.project_license()', '0.45.0')
- @noPosargs
- @permittedKwargs({})
- def project_license_method(self, args, kwargs):
- return self.build.dep_manifest[self.interpreter.active_projectname]['license']
- @noPosargs
- @permittedKwargs({})
- def version_method(self, args, kwargs):
- return coredata.version
- @noPosargs
- @permittedKwargs({})
- def project_name_method(self, args, kwargs):
- return self.interpreter.active_projectname
- @noArgsFlattening
- @permittedKwargs({})
- def get_cross_property_method(self, args, kwargs):
- if len(args) < 1 or len(args) > 2:
- raise InterpreterException('Must have one or two arguments.')
- propname = args[0]
- if not isinstance(propname, str):
- raise InterpreterException('Property name must be string.')
- try:
- props = self.interpreter.environment.properties.host
- return props[propname]
- except Exception:
- if len(args) == 2:
- return args[1]
- raise InterpreterException('Unknown cross property: %s.' % propname)
- known_library_kwargs = (
- build.known_shlib_kwargs |
- build.known_stlib_kwargs
- )
- known_build_target_kwargs = (
- known_library_kwargs |
- build.known_exe_kwargs |
- build.known_jar_kwargs |
- {'target_type'}
- )
- permitted_kwargs = {'add_global_arguments': {'language', 'native'},
- 'add_global_link_arguments': {'language', 'native'},
- 'add_languages': {'required'},
- 'add_project_link_arguments': {'language', 'native'},
- 'add_project_arguments': {'language', 'native'},
- 'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'},
- 'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'},
- 'build_target': known_build_target_kwargs,
- 'configure_file': {'input',
- 'output',
- 'configuration',
- 'command',
- 'copy',
- 'install_dir',
- 'install_mode',
- 'capture',
- 'install',
- 'format',
- 'output_format',
- 'encoding'},
- 'custom_target': {'input',
- 'output',
- 'command',
- 'install',
- 'install_dir',
- 'install_mode',
- 'build_always',
- 'capture',
- 'depends',
- 'depend_files',
- 'depfile',
- 'build_by_default',
- 'build_always_stale',
- 'console'},
- 'dependency': {'default_options',
- 'fallback',
- 'language',
- 'main',
- 'method',
- 'modules',
- 'cmake_module_path',
- 'optional_modules',
- 'native',
- 'not_found_message',
- 'required',
- 'static',
- 'version',
- 'private_headers',
- 'cmake_args',
- },
- 'declare_dependency': {'include_directories',
- 'link_with',
- 'sources',
- 'dependencies',
- 'compile_args',
- 'link_args',
- 'link_whole',
- 'version',
- },
- 'executable': build.known_exe_kwargs,
- 'find_program': {'required', 'native'},
- 'generator': {'arguments', 'output', 'depfile', 'capture', 'preserve_path_from'},
- 'include_directories': {'is_system'},
- 'install_data': {'install_dir', 'install_mode', 'rename', 'sources'},
- 'install_headers': {'install_dir', 'install_mode', 'subdir'},
- 'install_man': {'install_dir', 'install_mode'},
- 'install_subdir': {'exclude_files', 'exclude_directories', 'install_dir', 'install_mode', 'strip_directory'},
- 'jar': build.known_jar_kwargs,
- 'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'},
- 'run_command': {'check', 'capture', 'env'},
- 'run_target': {'command', 'depends'},
- 'shared_library': build.known_shlib_kwargs,
- 'shared_module': build.known_shmod_kwargs,
- 'static_library': build.known_stlib_kwargs,
- 'both_libraries': known_library_kwargs,
- 'library': known_library_kwargs,
- 'subdir': {'if_found'},
- 'subproject': {'version', 'default_options', 'required'},
- 'test': {'args', 'depends', 'env', 'is_parallel', 'should_fail', 'timeout', 'workdir', 'suite'},
- 'vcs_tag': {'input', 'output', 'fallback', 'command', 'replace_string'},
- }
- class Interpreter(InterpreterBase):
- def __init__(self, build, backend=None, subproject='', subdir='', subproject_dir='subprojects',
- modules = None, default_project_options=None, mock=False):
- super().__init__(build.environment.get_source_dir(), subdir)
- self.an_unpicklable_object = mesonlib.an_unpicklable_object
- self.build = build
- self.environment = build.environment
- self.coredata = self.environment.get_coredata()
- self.backend = backend
- self.subproject = subproject
- if modules is None:
- self.modules = {}
- else:
- self.modules = modules
- # Subproject directory is usually the name of the subproject, but can
- # be different for dependencies provided by wrap files.
- self.subproject_directory_name = subdir.split(os.path.sep)[-1]
- self.subproject_dir = subproject_dir
- self.option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt')
- if not mock:
- self.load_root_meson_file()
- self.sanity_check_ast()
- self.builtin.update({'meson': MesonMain(build, self)})
- self.generators = []
- self.visited_subdirs = {}
- self.project_args_frozen = False
- self.global_args_frozen = False # implies self.project_args_frozen
- self.subprojects = {}
- self.subproject_stack = []
- self.configure_file_outputs = {}
- # Passed from the outside, only used in subprojects.
- if default_project_options:
- self.default_project_options = default_project_options.copy()
- else:
- self.default_project_options = {}
- self.project_default_options = {}
- self.build_func_dict()
- # build_def_files needs to be defined before parse_project is called
- self.build_def_files = [os.path.join(self.subdir, environment.build_filename)]
- if not mock:
- self.parse_project()
- # Initialize machine descriptions. We can do a better job now because we
- # have the compilers needed to gain more knowledge, so wipe out old
- # inferrence and start over.
- self.build.environment.machines.miss_defaulting()
- self.build.environment.machines.detect_build(self.coredata.compilers)
- self.build.environment.machines.default_missing()
- assert self.build.environment.machines.build.cpu is not None
- assert self.build.environment.machines.host.cpu is not None
- assert self.build.environment.machines.target.cpu is not None
- self.builtin['build_machine'] = \
- MachineHolder(self.build.environment.machines.build)
- self.builtin['host_machine'] = \
- MachineHolder(self.build.environment.machines.host)
- self.builtin['target_machine'] = \
- MachineHolder(self.build.environment.machines.target)
- def get_non_matching_default_options(self):
- env = self.environment
- for def_opt_name, def_opt_value in self.project_default_options.items():
- for option_type in env.coredata.get_all_options():
- for cur_opt_name, cur_opt_value in option_type.items():
- if def_opt_name == cur_opt_name:
- def_opt_value = env.coredata.validate_option_value(def_opt_name, def_opt_value)
- if def_opt_value != cur_opt_value.value:
- yield (def_opt_name, def_opt_value, cur_opt_value)
- def build_func_dict(self):
- self.funcs.update({'add_global_arguments': self.func_add_global_arguments,
- 'add_project_arguments': self.func_add_project_arguments,
- 'add_global_link_arguments': self.func_add_global_link_arguments,
- 'add_project_link_arguments': self.func_add_project_link_arguments,
- 'add_test_setup': self.func_add_test_setup,
- 'add_languages': self.func_add_languages,
- 'assert': self.func_assert,
- 'benchmark': self.func_benchmark,
- 'build_target': self.func_build_target,
- 'configuration_data': self.func_configuration_data,
- 'configure_file': self.func_configure_file,
- 'custom_target': self.func_custom_target,
- 'declare_dependency': self.func_declare_dependency,
- 'dependency': self.func_dependency,
- 'disabler': self.func_disabler,
- 'environment': self.func_environment,
- 'error': self.func_error,
- 'executable': self.func_executable,
- 'generator': self.func_generator,
- 'gettext': self.func_gettext,
- 'get_option': self.func_get_option,
- 'get_variable': self.func_get_variable,
- 'files': self.func_files,
- 'find_library': self.func_find_library,
- 'find_program': self.func_find_program,
- 'include_directories': self.func_include_directories,
- 'import': self.func_import,
- 'install_data': self.func_install_data,
- 'install_headers': self.func_install_headers,
- 'install_man': self.func_install_man,
- 'install_subdir': self.func_install_subdir,
- 'is_variable': self.func_is_variable,
- 'jar': self.func_jar,
- 'join_paths': self.func_join_paths,
- 'library': self.func_library,
- 'message': self.func_message,
- 'warning': self.func_warning,
- 'option': self.func_option,
- 'project': self.func_project,
- 'run_target': self.func_run_target,
- 'run_command': self.func_run_command,
- 'set_variable': self.func_set_variable,
- 'subdir': self.func_subdir,
- 'subdir_done': self.func_subdir_done,
- 'subproject': self.func_subproject,
- 'shared_library': self.func_shared_lib,
- 'shared_module': self.func_shared_module,
- 'static_library': self.func_static_lib,
- 'both_libraries': self.func_both_lib,
- 'test': self.func_test,
- 'vcs_tag': self.func_vcs_tag
- })
- if 'MESON_UNIT_TEST' in os.environ:
- self.funcs.update({'exception': self.func_exception})
- def holderify(self, item):
- if isinstance(item, list):
- return [self.holderify(x) for x in item]
- if isinstance(item, build.CustomTarget):
- return CustomTargetHolder(item, self)
- elif isinstance(item, (int, str, bool)) or item is None:
- return item
- elif isinstance(item, build.Executable):
- return ExecutableHolder(item, self)
- elif isinstance(item, build.GeneratedList):
- return GeneratedListHolder(item)
- elif isinstance(item, build.RunTarget):
- raise RuntimeError('This is not a pipe.')
- elif isinstance(item, build.RunScript):
- raise RuntimeError('Do not do this.')
- elif isinstance(item, build.Data):
- return DataHolder(item)
- elif isinstance(item, dependencies.InternalDependency):
- return InternalDependencyHolder(item, self.subproject)
- elif isinstance(item, dependencies.ExternalDependency):
- return DependencyHolder(item, self.subproject)
- elif isinstance(item, dependencies.ExternalProgram):
- return ExternalProgramHolder(item)
- elif hasattr(item, 'held_object'):
- return item
- else:
- raise InterpreterException('Module returned a value of unknown type.')
- def process_new_values(self, invalues):
- invalues = listify(invalues)
- for v in invalues:
- if isinstance(v, (RunTargetHolder, CustomTargetHolder, BuildTargetHolder)):
- v = v.held_object
- if isinstance(v, (build.BuildTarget, build.CustomTarget, build.RunTarget)):
- self.add_target(v.name, v)
- elif isinstance(v, list):
- self.module_method_callback(v)
- elif isinstance(v, build.GeneratedList):
- pass
- elif isinstance(v, build.RunScript):
- self.build.install_scripts.append(v)
- elif isinstance(v, build.Data):
- self.build.data.append(v)
- elif isinstance(v, dependencies.ExternalProgram):
- return ExternalProgramHolder(v)
- elif isinstance(v, dependencies.InternalDependency):
- # FIXME: This is special cased and not ideal:
- # The first source is our new VapiTarget, the rest are deps
- self.process_new_values(v.sources[0])
- elif hasattr(v, 'held_object'):
- pass
- elif isinstance(v, (int, str, bool)):
- pass
- else:
- raise InterpreterException('Module returned a value of unknown type.')
- def module_method_callback(self, return_object):
- if not isinstance(return_object, ModuleReturnValue):
- raise InterpreterException('Bug in module, it returned an invalid object')
- invalues = return_object.new_objects
- self.process_new_values(invalues)
- return self.holderify(return_object.return_value)
- def get_build_def_files(self):
- return self.build_def_files
- def get_variables(self):
- return self.variables
- def check_cross_stdlibs(self):
- if self.build.environment.is_cross_build():
- props = self.build.environment.properties.host
- for l, c in self.build.cross_compilers.items():
- try:
- di = mesonlib.stringlistify(props.get_stdlib(l))
- if len(di) != 2:
- raise InterpreterException('Stdlib definition for %s should have exactly two elements.'
- % l)
- projname, depname = di
- subproj = self.do_subproject(projname, {})
- self.build.cross_stdlibs[l] = subproj.get_variable_method([depname], {})
- except KeyError:
- pass
- except InvalidArguments:
- pass
- @stringArgs
- @noKwargs
- def func_import(self, node, args, kwargs):
- if len(args) != 1:
- raise InvalidCode('Import takes one argument.')
- modname = args[0]
- if modname.startswith('unstable-'):
- plainname = modname.split('-', 1)[1]
- mlog.warning('Module %s has no backwards or forwards compatibility and might not exist in future releases.' % modname, location=node)
- modname = 'unstable_' + plainname
- if modname not in self.modules:
- try:
- module = importlib.import_module('mesonbuild.modules.' + modname)
- except ImportError:
- raise InvalidArguments('Module "%s" does not exist' % (modname, ))
- self.modules[modname] = module.initialize(self)
- return ModuleHolder(modname, self.modules[modname], self)
- @stringArgs
- @noKwargs
- def func_files(self, node, args, kwargs):
- return [mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, fname) for fname in args]
- @FeatureNewKwargs('declare_dependency', '0.46.0', ['link_whole'])
- @permittedKwargs(permitted_kwargs['declare_dependency'])
- @noPosargs
- def func_declare_dependency(self, node, args, kwargs):
- version = kwargs.get('version', self.project_version)
- if not isinstance(version, str):
- raise InterpreterException('Version must be a string.')
- incs = self.extract_incdirs(kwargs)
- libs = extract_as_list(kwargs, 'link_with', unholder=True)
- libs_whole = extract_as_list(kwargs, 'link_whole', unholder=True)
- sources = extract_as_list(kwargs, 'sources')
- sources = listify(self.source_strings_to_files(sources), unholder=True)
- deps = extract_as_list(kwargs, 'dependencies', unholder=True)
- compile_args = mesonlib.stringlistify(kwargs.get('compile_args', []))
- link_args = mesonlib.stringlistify(kwargs.get('link_args', []))
- final_deps = []
- for d in deps:
- try:
- d = d.held_object
- except Exception:
- pass
- if not isinstance(d, (dependencies.Dependency, dependencies.ExternalLibrary, dependencies.InternalDependency)):
- raise InterpreterException('Dependencies must be external deps')
- final_deps.append(d)
- for l in libs:
- if isinstance(l, dependencies.Dependency):
- raise InterpreterException('''Entries in "link_with" may only be self-built targets,
- external dependencies (including libraries) must go to "dependencies".''')
- dep = dependencies.InternalDependency(version, incs, compile_args,
- link_args, libs, libs_whole, sources, final_deps)
- return DependencyHolder(dep, self.subproject)
- @noKwargs
- def func_assert(self, node, args, kwargs):
- if len(args) != 2:
- raise InterpreterException('Assert takes exactly two arguments')
- value, message = args
- if not isinstance(value, bool):
- raise InterpreterException('Assert value not bool.')
- if not isinstance(message, str):
- raise InterpreterException('Assert message not a string.')
- if not value:
- raise InterpreterException('Assert failed: ' + message)
- def validate_arguments(self, args, argcount, arg_types):
- if argcount is not None:
- if argcount != len(args):
- raise InvalidArguments('Expected %d arguments, got %d.' %
- (argcount, len(args)))
- for i in range(min(len(args), len(arg_types))):
- wanted = arg_types[i]
- actual = args[i]
- if wanted is not None:
- if not isinstance(actual, wanted):
- raise InvalidArguments('Incorrect argument type.')
- @FeatureNewKwargs('run_command', '0.50.0', ['env'])
- @FeatureNewKwargs('run_command', '0.47.0', ['check', 'capture'])
- @permittedKwargs(permitted_kwargs['run_command'])
- def func_run_command(self, node, args, kwargs):
- return self.run_command_impl(node, args, kwargs)
- def run_command_impl(self, node, args, kwargs, in_builddir=False):
- if len(args) < 1:
- raise InterpreterException('Not enough arguments')
- cmd = args[0]
- cargs = args[1:]
- capture = kwargs.get('capture', True)
- srcdir = self.environment.get_source_dir()
- builddir = self.environment.get_build_dir()
- check = kwargs.get('check', False)
- if not isinstance(check, bool):
- raise InterpreterException('Check must be boolean.')
- env = self.unpack_env_kwarg(kwargs)
- m = 'must be a string, or the output of find_program(), files() '\
- 'or configure_file(), or a compiler object; not {!r}'
- if isinstance(cmd, ExternalProgramHolder):
- cmd = cmd.held_object
- if isinstance(cmd, build.Executable):
- progname = node.args.arguments[0].value
- msg = 'Program {!r} was overridden with the compiled executable {!r}'\
- ' and therefore cannot be used during configuration'
- raise InterpreterException(msg.format(progname, cmd.description()))
- elif isinstance(cmd, CompilerHolder):
- cmd = cmd.compiler.get_exelist()[0]
- prog = ExternalProgram(cmd, silent=True)
- if not prog.found():
- raise InterpreterException('Program {!r} not found '
- 'or not executable'.format(cmd))
- cmd = prog
- else:
- if isinstance(cmd, mesonlib.File):
- cmd = cmd.absolute_path(srcdir, builddir)
- elif not isinstance(cmd, str):
- raise InterpreterException('First argument ' + m.format(cmd))
- # Prefer scripts in the current source directory
- search_dir = os.path.join(srcdir, self.subdir)
- prog = ExternalProgram(cmd, silent=True, search_dir=search_dir)
- if not prog.found():
- raise InterpreterException('Program or command {!r} not found '
- 'or not executable'.format(cmd))
- cmd = prog
- cmd_path = mesonlib.relpath(cmd.get_path(), start=srcdir)
- if not cmd_path.startswith('..') and cmd_path not in self.build_def_files:
- self.build_def_files.append(cmd_path)
- expanded_args = []
- for a in listify(cargs):
- if isinstance(a, str):
- expanded_args.append(a)
- elif isinstance(a, mesonlib.File):
- expanded_args.append(a.absolute_path(srcdir, builddir))
- elif isinstance(a, ExternalProgramHolder):
- expanded_args.append(a.held_object.get_path())
- else:
- raise InterpreterException('Arguments ' + m.format(a))
- for a in expanded_args:
- if not os.path.isabs(a):
- a = os.path.join(builddir if in_builddir else srcdir, self.subdir, a)
- if os.path.isfile(a):
- a = mesonlib.relpath(a, start=srcdir)
- if not a.startswith('..'):
- if a not in self.build_def_files:
- self.build_def_files.append(a)
- return RunProcess(cmd, expanded_args, env, srcdir, builddir, self.subdir,
- self.environment.get_build_command() + ['introspect'],
- in_builddir=in_builddir, check=check, capture=capture)
- @stringArgs
- def func_gettext(self, nodes, args, kwargs):
- raise InterpreterException('Gettext() function has been moved to module i18n. Import it and use i18n.gettext() instead')
- def func_option(self, nodes, args, kwargs):
- raise InterpreterException('Tried to call option() in build description file. All options must be in the option file.')
- @FeatureNewKwargs('subproject', '0.38.0', ['default_options'])
- @permittedKwargs(permitted_kwargs['subproject'])
- @stringArgs
- def func_subproject(self, nodes, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Subproject takes exactly one argument')
- dirname = args[0]
- return self.do_subproject(dirname, kwargs)
- def disabled_subproject(self, dirname):
- self.subprojects[dirname] = SubprojectHolder(None, self.subproject_dir, dirname)
- return self.subprojects[dirname]
- def do_subproject(self, dirname, kwargs):
- disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
- if disabled:
- mlog.log('Subproject', mlog.bold(dirname), ':', 'skipped: feature', mlog.bold(feature), 'disabled')
- return self.disabled_subproject(dirname)
- default_options = mesonlib.stringlistify(kwargs.get('default_options', []))
- default_options = coredata.create_options_dict(default_options)
- if dirname == '':
- raise InterpreterException('Subproject dir name must not be empty.')
- if dirname[0] == '.':
- raise InterpreterException('Subproject dir name must not start with a period.')
- if '..' in dirname:
- raise InterpreterException('Subproject name must not contain a ".." path segment.')
- if os.path.isabs(dirname):
- raise InterpreterException('Subproject name must not be an absolute path.')
- if has_path_sep(dirname):
- mlog.warning('Subproject name has a path separator. This may cause unexpected behaviour.',
- location=self.current_node)
- if dirname in self.subproject_stack:
- fullstack = self.subproject_stack + [dirname]
- incpath = ' => '.join(fullstack)
- raise InvalidCode('Recursive include of subprojects: %s.' % incpath)
- if dirname in self.subprojects:
- subproject = self.subprojects[dirname]
- if required and not subproject.found():
- raise InterpreterException('Subproject "%s/%s" required but not found.' % (
- self.subproject_dir, dirname))
- return subproject
- subproject_dir_abs = os.path.join(self.environment.get_source_dir(), self.subproject_dir)
- r = wrap.Resolver(subproject_dir_abs, self.coredata.get_builtin_option('wrap_mode'))
- try:
- resolved = r.resolve(dirname)
- except wrap.WrapException as e:
- subprojdir = os.path.join(self.subproject_dir, r.directory)
- if isinstance(e, wrap.WrapNotFoundException):
- # if the reason subproject execution failed was because
- # the directory doesn't exist, try to give some helpful
- # advice if it's a nested subproject that needs
- # promotion...
- self.print_nested_info(dirname)
- if not required:
- mlog.log(e)
- mlog.log('Subproject ', mlog.bold(subprojdir), 'is buildable:', mlog.red('NO'), '(disabling)')
- return self.disabled_subproject(dirname)
- raise e
- subdir = os.path.join(self.subproject_dir, resolved)
- os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True)
- self.global_args_frozen = True
- mlog.log()
- with mlog.nested():
- mlog.log('Executing subproject', mlog.bold(dirname), '\n')
- try:
- with mlog.nested():
- new_build = self.build.copy()
- subi = Interpreter(new_build, self.backend, dirname, subdir, self.subproject_dir,
- self.modules, default_options)
- subi.subprojects = self.subprojects
- subi.subproject_stack = self.subproject_stack + [dirname]
- current_active = self.active_projectname
- subi.run()
- mlog.log('Subproject', mlog.bold(dirname), 'finished.')
- # Invalid code is always an error
- except InvalidCode:
- raise
- except Exception as e:
- if not required:
- with mlog.nested():
- # Suppress the 'ERROR:' prefix because this exception is not
- # fatal and VS CI treat any logs with "ERROR:" as fatal.
- mlog.exception(e, prefix=None)
- mlog.log('\nSubproject', mlog.bold(dirname), 'is buildable:', mlog.red('NO'), '(disabling)')
- return self.disabled_subproject(dirname)
- raise e
- mlog.log()
- if 'version' in kwargs:
- pv = subi.project_version
- wanted = kwargs['version']
- if pv == 'undefined' or not mesonlib.version_compare_many(pv, wanted)[0]:
- raise InterpreterException('Subproject %s version is %s but %s required.' % (dirname, pv, wanted))
- self.active_projectname = current_active
- self.subprojects.update(subi.subprojects)
- self.subprojects[dirname] = SubprojectHolder(subi, self.subproject_dir, dirname)
- self.build_def_files += subi.build_def_files
- self.build.merge(subi.build)
- self.build.subprojects[dirname] = subi.project_version
- return self.subprojects[dirname]
- def get_option_internal(self, optname):
- # Some base options are not defined in some environments, return the
- # default value from compilers.base_options in that case.
- for d in chain(
- [self.coredata.base_options, compilers.base_options, self.coredata.builtins],
- self.coredata.get_all_compiler_options()):
- try:
- return d[optname]
- except KeyError:
- pass
- raw_optname = optname
- if self.is_subproject():
- optname = self.subproject + ':' + optname
- try:
- opt = self.coredata.user_options[optname]
- if opt.yielding and ':' in optname and raw_optname in self.coredata.user_options:
- popt = self.coredata.user_options[raw_optname]
- if type(opt) is type(popt):
- opt = popt
- else:
- # Get class name, then option type as a string
- opt_type = opt.__class__.__name__[4:][:-6].lower()
- popt_type = popt.__class__.__name__[4:][:-6].lower()
- # This is not a hard error to avoid dependency hell, the workaround
- # when this happens is to simply set the subproject's option directly.
- mlog.warning('Option {0!r} of type {1!r} in subproject {2!r} cannot yield '
- 'to parent option of type {3!r}, ignoring parent value. '
- 'Use -D{2}:{0}=value to set the value for this option manually'
- '.'.format(raw_optname, opt_type, self.subproject, popt_type),
- location=self.current_node)
- return opt
- except KeyError:
- pass
- raise InterpreterException('Tried to access unknown option "%s".' % optname)
- @stringArgs
- @noKwargs
- def func_get_option(self, nodes, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Argument required for get_option.')
- optname = args[0]
- if ':' in optname:
- raise InterpreterException('Having a colon in option name is forbidden, '
- 'projects are not allowed to directly access '
- 'options of other subprojects.')
- opt = self.get_option_internal(optname)
- if isinstance(opt, coredata.UserFeatureOption):
- return FeatureOptionHolder(self.environment, opt)
- elif isinstance(opt, coredata.UserOption):
- return opt.value
- return opt
- @noKwargs
- def func_configuration_data(self, node, args, kwargs):
- if len(args) > 1:
- raise InterpreterException('configuration_data takes only one optional positional arguments')
- elif len(args) == 1:
- initial_values = args[0]
- if not isinstance(initial_values, dict):
- raise InterpreterException('configuration_data first argument must be a dictionary')
- else:
- initial_values = {}
- cdata = ConfigurationDataHolder(self.subproject)
- for k, v in initial_values.items():
- cdata.set_method([k, v], {})
- return cdata
- def set_backend(self):
- # The backend is already set when parsing subprojects
- if self.backend is not None:
- return
- backend = self.coredata.get_builtin_option('backend')
- from .backend import backends
- self.backend = backends.get_backend_from_name(backend, self.build)
- if self.backend is None:
- raise InterpreterException('Unknown backend "%s".' % backend)
- if backend != self.backend.name:
- if self.backend.name.startswith('vs'):
- mlog.log('Auto detected Visual Studio backend:', mlog.bold(self.backend.name))
- self.coredata.set_builtin_option('backend', self.backend.name)
- # Only init backend options on first invocation otherwise it would
- # override values previously set from command line.
- if self.environment.first_invocation:
- self.coredata.init_backend_options(backend)
- options = {k: v for k, v in self.environment.cmd_line_options.items() if k.startswith('backend_')}
- self.coredata.set_options(options)
- @stringArgs
- @permittedKwargs(permitted_kwargs['project'])
- 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_langs = args[1:]
- if ':' in proj_name:
- raise InvalidArguments("Project name {!r} must not contain ':'".format(proj_name))
- if os.path.exists(self.option_file):
- oi = optinterpreter.OptionInterpreter(self.subproject)
- oi.process(self.option_file)
- self.coredata.merge_user_options(oi.options)
- # Do not set default_options on reconfigure otherwise it would override
- # values previously set from command line. That means that changing
- # default_options in a project will trigger a reconfigure but won't
- # have any effect.
- self.project_default_options = mesonlib.stringlistify(kwargs.get('default_options', []))
- self.project_default_options = coredata.create_options_dict(self.project_default_options)
- if self.environment.first_invocation:
- default_options = self.project_default_options
- default_options.update(self.default_project_options)
- else:
- default_options = {}
- self.coredata.set_default_options(default_options, self.subproject, self.environment.cmd_line_options)
- self.set_backend()
- if not self.is_subproject():
- self.build.project_name = proj_name
- self.active_projectname = proj_name
- self.project_version = kwargs.get('version', 'undefined')
- if self.build.project_version is None:
- self.build.project_version = self.project_version
- proj_license = mesonlib.stringlistify(kwargs.get('license', 'unknown'))
- self.build.dep_manifest[proj_name] = {'version': self.project_version,
- 'license': proj_license}
- if self.subproject in self.build.projects:
- raise InvalidCode('Second call to project().')
- if not self.is_subproject() and 'subproject_dir' in kwargs:
- spdirname = kwargs['subproject_dir']
- if not isinstance(spdirname, str):
- raise InterpreterException('Subproject_dir must be a string')
- if os.path.isabs(spdirname):
- raise InterpreterException('Subproject_dir must not be an absolute path.')
- if spdirname.startswith('.'):
- raise InterpreterException('Subproject_dir must not begin with a period.')
- if '..' in spdirname:
- raise InterpreterException('Subproject_dir must not contain a ".." segment.')
- self.subproject_dir = spdirname
- self.build.subproject_dir = self.subproject_dir
- mesonlib.project_meson_versions[self.subproject] = ''
- if 'meson_version' in kwargs:
- cv = coredata.version
- pv = kwargs['meson_version']
- mesonlib.project_meson_versions[self.subproject] = pv
- if not mesonlib.version_compare(cv, pv):
- raise InterpreterException('Meson version is %s but project requires %s.' % (cv, pv))
- self.build.projects[self.subproject] = proj_name
- mlog.log('Project name:', mlog.bold(proj_name))
- mlog.log('Project version:', mlog.bold(self.project_version))
- self.add_languages(proj_langs, True)
- langs = self.coredata.compilers.keys()
- if 'vala' in langs:
- if 'c' not in langs:
- raise InterpreterException('Compiling Vala requires C. Add C to your project languages and rerun Meson.')
- if not self.is_subproject():
- self.check_cross_stdlibs()
- @permittedKwargs(permitted_kwargs['add_languages'])
- @stringArgs
- def func_add_languages(self, node, args, kwargs):
- disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
- if disabled:
- for lang in sorted(args, key=compilers.sort_clink):
- mlog.log('Compiler for language', mlog.bold(lang), 'skipped: feature', mlog.bold(feature), 'disabled')
- return False
- return self.add_languages(args, required)
- def get_message_string_arg(self, node):
- # reduce arguments again to avoid flattening posargs
- (posargs, _) = self.reduce_arguments(node.args)
- if len(posargs) != 1:
- raise InvalidArguments('Expected 1 argument, got %d' % len(posargs))
- arg = posargs[0]
- if isinstance(arg, list):
- argstr = stringifyUserArguments(arg)
- elif isinstance(arg, dict):
- argstr = stringifyUserArguments(arg)
- elif isinstance(arg, str):
- argstr = arg
- elif isinstance(arg, int):
- argstr = str(arg)
- else:
- raise InvalidArguments('Function accepts only strings, integers, lists and lists thereof.')
- return argstr
- @noKwargs
- def func_message(self, node, args, kwargs):
- argstr = self.get_message_string_arg(node)
- self.message_impl(argstr)
- def message_impl(self, argstr):
- mlog.log(mlog.bold('Message:'), argstr)
- @FeatureNew('warning', '0.44.0')
- @noKwargs
- def func_warning(self, node, args, kwargs):
- argstr = self.get_message_string_arg(node)
- mlog.warning(argstr, location=node)
- @noKwargs
- def func_error(self, node, args, kwargs):
- self.validate_arguments(args, 1, [str])
- raise InterpreterException('Problem encountered: ' + args[0])
- @noKwargs
- def func_exception(self, node, args, kwargs):
- self.validate_arguments(args, 0, [])
- raise Exception()
- def add_languages(self, args, required):
- success = True
- need_cross_compiler = self.environment.is_cross_build()
- for lang in sorted(args, key=compilers.sort_clink):
- lang = lang.lower()
- if lang in self.coredata.compilers:
- comp = self.coredata.compilers[lang]
- cross_comp = self.coredata.cross_compilers.get(lang, None)
- else:
- try:
- (comp, cross_comp) = self.environment.detect_compilers(lang, need_cross_compiler)
- self.environment.check_compilers(lang, comp, cross_comp)
- except Exception:
- if not required:
- mlog.log('Compiler for language', mlog.bold(lang), 'not found.')
- success = False
- continue
- else:
- raise
- if comp.full_version is not None:
- version_string = '(%s %s "%s")' % (comp.id, comp.version, comp.full_version)
- else:
- version_string = '(%s %s)' % (comp.id, comp.version)
- mlog.log('Native', comp.get_display_language(), 'compiler:',
- mlog.bold(' '.join(comp.get_exelist())), version_string)
- self.build.ensure_static_linker(comp)
- if need_cross_compiler:
- version_string = '(%s %s)' % (cross_comp.id, cross_comp.version)
- mlog.log('Cross', cross_comp.get_display_language(), 'compiler:',
- mlog.bold(' '.join(cross_comp.get_exelist())), version_string)
- self.build.ensure_static_cross_linker(comp)
- return success
- def program_from_file_for(self, for_machine, prognames, silent):
- bins = self.environment.binaries[for_machine]
- for p in prognames:
- if hasattr(p, 'held_object'):
- p = p.held_object
- if isinstance(p, mesonlib.File):
- continue # Always points to a local (i.e. self generated) file.
- if not isinstance(p, str):
- raise InterpreterException('Executable name must be a string')
- prog = ExternalProgram.from_bin_list(bins, p)
- if prog.found():
- return ExternalProgramHolder(prog)
- return None
- def program_from_system(self, args, silent=False):
- # Search for scripts relative to current subdir.
- # Do not cache found programs because find_program('foobar')
- # might give different results when run from different source dirs.
- source_dir = os.path.join(self.environment.get_source_dir(), self.subdir)
- for exename in args:
- if isinstance(exename, mesonlib.File):
- if exename.is_built:
- search_dir = os.path.join(self.environment.get_build_dir(),
- exename.subdir)
- else:
- search_dir = os.path.join(self.environment.get_source_dir(),
- exename.subdir)
- exename = exename.fname
- elif isinstance(exename, str):
- search_dir = source_dir
- else:
- raise InvalidArguments('find_program only accepts strings and '
- 'files, not {!r}'.format(exename))
- extprog = dependencies.ExternalProgram(exename, search_dir=search_dir,
- silent=silent)
- progobj = ExternalProgramHolder(extprog)
- if progobj.found():
- return progobj
- def program_from_overrides(self, command_names, silent=False):
- for name in command_names:
- if not isinstance(name, str):
- continue
- if name in self.build.find_overrides:
- exe = self.build.find_overrides[name]
- if not silent:
- mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'),
- '(overridden: %s)' % exe.description())
- return ExternalProgramHolder(exe)
- return None
- def store_name_lookups(self, command_names):
- for name in command_names:
- if isinstance(name, str):
- self.build.searched_programs.add(name)
- def add_find_program_override(self, name, exe):
- if name in self.build.searched_programs:
- raise InterpreterException('Tried to override finding of executable "%s" which has already been found.'
- % name)
- if name in self.build.find_overrides:
- raise InterpreterException('Tried to override executable "%s" which has already been overridden.'
- % name)
- self.build.find_overrides[name] = exe
- def find_program_impl(self, args, native=False, required=True, silent=True):
- if not isinstance(args, list):
- args = [args]
- progobj = self.program_from_overrides(args, silent=silent)
- if progobj is None:
- for_machine = MachineChoice.BUILD if native else MachineChoice.HOST
- progobj = self.program_from_file_for(for_machine, args, silent=silent)
- if progobj is None:
- progobj = self.program_from_system(args, silent=silent)
- if required and (progobj is None or not progobj.found()):
- raise InvalidArguments('Program(s) {!r} not found or not executable'.format(args))
- if progobj is None:
- return ExternalProgramHolder(dependencies.NonExistingExternalProgram())
- # Only store successful lookups
- self.store_name_lookups(args)
- return progobj
- @FeatureNewKwargs('find_program', '0.49.0', ['disabler'])
- @disablerIfNotFound
- @permittedKwargs(permitted_kwargs['find_program'])
- def func_find_program(self, node, args, kwargs):
- if not args:
- raise InterpreterException('No program name specified.')
- disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
- if disabled:
- mlog.log('Program', mlog.bold(' '.join(args)), 'skipped: feature', mlog.bold(feature), 'disabled')
- return ExternalProgramHolder(dependencies.NonExistingExternalProgram())
- if not isinstance(required, bool):
- raise InvalidArguments('"required" argument must be a boolean.')
- use_native = kwargs.get('native', False)
- if not isinstance(use_native, bool):
- raise InvalidArguments('Argument to "native" must be a boolean.')
- return self.find_program_impl(args, native=use_native, required=required, silent=False)
- def func_find_library(self, node, args, kwargs):
- raise InvalidCode('find_library() is removed, use meson.get_compiler(\'name\').find_library() instead.\n'
- 'Look here for documentation: http://mesonbuild.com/Reference-manual.html#compiler-object\n'
- 'Look here for example: http://mesonbuild.com/howtox.html#add-math-library-lm-portably\n'
- )
- def _find_cached_dep(self, name, kwargs):
- # Check if we want this as a cross-dep or a native-dep
- # FIXME: Not all dependencies support such a distinction right now,
- # and we repeat this check inside dependencies that do. We need to
- # consolidate this somehow.
- is_cross = self.environment.is_cross_build()
- if 'native' in kwargs and is_cross:
- want_cross = not kwargs['native']
- else:
- want_cross = is_cross
- identifier = dependencies.get_dep_identifier(name, kwargs, want_cross)
- cached_dep = self.coredata.deps.get(identifier)
- if cached_dep:
- if not cached_dep.found():
- mlog.log('Dependency', mlog.bold(name),
- 'found:', mlog.red('NO'), '(cached)')
- return identifier, cached_dep
- # Verify the cached dep version match
- wanted = kwargs.get('version', [])
- found = cached_dep.get_version()
- if not wanted or mesonlib.version_compare_many(found, wanted)[0]:
- mlog.log('Dependency', mlog.bold(name),
- 'found:', mlog.green('YES'), '(cached)')
- return identifier, cached_dep
- return identifier, None
- @staticmethod
- def check_subproject_version(wanted, found):
- if wanted == 'undefined':
- return True
- if found == 'undefined' or not mesonlib.version_compare_many(found, wanted)[0]:
- return False
- return True
- def notfound_dependency(self):
- return DependencyHolder(NotFoundDependency(self.environment), self.subproject)
- def get_subproject_dep(self, display_name, dirname, varname, kwargs):
- dep = self.notfound_dependency()
- try:
- subproject = self.subprojects[dirname]
- if subproject.found():
- dep = self.subprojects[dirname].get_variable_method([varname], {})
- except InvalidArguments:
- pass
- if not isinstance(dep, DependencyHolder):
- raise InvalidCode('Fetched variable {!r} in the subproject {!r} is '
- 'not a dependency object.'.format(varname, dirname))
- required = kwargs.get('required', True)
- wanted = kwargs.get('version', 'undefined')
- subproj_path = os.path.join(self.subproject_dir, dirname)
- if not dep.found():
- if required:
- raise DependencyException('Could not find dependency {} in subproject {}'
- ''.format(varname, dirname))
- # If the dependency is not required, don't raise an exception
- mlog.log('Dependency', mlog.bold(display_name), 'from subproject',
- mlog.bold(subproj_path), 'found:', mlog.red('NO'))
- return dep
- found = dep.held_object.get_version()
- if not self.check_subproject_version(wanted, found):
- if required:
- raise DependencyException('Version {} of subproject dependency {} already '
- 'cached, requested incompatible version {} for '
- 'dep {}'.format(found, dirname, wanted, display_name))
- mlog.log('Subproject', mlog.bold(subproj_path), 'dependency',
- mlog.bold(display_name), 'version is', mlog.bold(found),
- 'but', mlog.bold(wanted), 'is required.')
- return self.notfound_dependency()
- mlog.log('Dependency', mlog.bold(display_name), 'from subproject',
- mlog.bold(subproj_path), 'found:', mlog.green('YES'))
- return dep
- def _handle_featurenew_dependencies(self, name):
- 'Do a feature check on dependencies used by this subproject'
- if name == 'mpi':
- FeatureNew('MPI Dependency', '0.42.0').use(self.subproject)
- elif name == 'pcap':
- FeatureNew('Pcap Dependency', '0.42.0').use(self.subproject)
- elif name == 'vulkan':
- FeatureNew('Vulkan Dependency', '0.42.0').use(self.subproject)
- elif name == 'libwmf':
- FeatureNew('LibWMF Dependency', '0.44.0').use(self.subproject)
- elif name == 'openmp':
- FeatureNew('OpenMP Dependency', '0.46.0').use(self.subproject)
- @FeatureNewKwargs('dependency', '0.50.0', ['not_found_message', 'cmake_module_path', 'cmake_args'])
- @FeatureNewKwargs('dependency', '0.49.0', ['disabler'])
- @FeatureNewKwargs('dependency', '0.40.0', ['method'])
- @FeatureNewKwargs('dependency', '0.38.0', ['default_options'])
- @disablerIfNotFound
- @permittedKwargs(permitted_kwargs['dependency'])
- def func_dependency(self, node, args, kwargs):
- self.validate_arguments(args, 1, [str])
- name = args[0]
- display_name = name if name else '(anonymous)'
- not_found_message = kwargs.get('not_found_message', '')
- if not isinstance(not_found_message, str):
- raise InvalidArguments('The not_found_message must be a string.')
- try:
- d = self.dependency_impl(name, display_name, kwargs)
- except Exception:
- if not_found_message:
- self.message_impl(not_found_message)
- raise
- if not d.found() and not_found_message:
- self.message_impl(not_found_message)
- return d
- def dependency_impl(self, name, display_name, kwargs):
- disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
- if disabled:
- mlog.log('Dependency', mlog.bold(display_name), 'skipped: feature', mlog.bold(feature), 'disabled')
- return self.notfound_dependency()
- has_fallback = 'fallback' in kwargs
- if 'default_options' in kwargs and not has_fallback:
- mlog.warning('The "default_options" keyworg argument does nothing without a "fallback" keyword argument.',
- location=self.current_node)
- # writing just "dependency('')" is an error, because it can only fail
- if name == '' and required and not has_fallback:
- raise InvalidArguments('Dependency is both required and not-found')
- if '<' in name or '>' in name or '=' in name:
- raise InvalidArguments('Characters <, > and = are forbidden in dependency names. To specify'
- 'version\n requirements use the \'version\' keyword argument instead.')
- identifier, cached_dep = self._find_cached_dep(name, kwargs)
- if cached_dep:
- if required and not cached_dep.found():
- m = 'Dependency {!r} was already checked and was not found'
- raise DependencyException(m.format(display_name))
- return DependencyHolder(cached_dep, self.subproject)
- # If the dependency has already been configured, possibly by
- # a higher level project, try to use it first.
- if has_fallback:
- dirname, varname = self.get_subproject_infos(kwargs)
- if dirname in self.subprojects:
- return self.get_subproject_dep(name, dirname, varname, kwargs)
- wrap_mode = self.coredata.get_builtin_option('wrap_mode')
- forcefallback = wrap_mode == WrapMode.forcefallback and has_fallback
- if name != '' and not forcefallback:
- self._handle_featurenew_dependencies(name)
- kwargs['required'] = required and not has_fallback
- dep = dependencies.find_external_dependency(name, self.environment, kwargs)
- kwargs['required'] = required
- # Only store found-deps in the cache
- # Never add fallback deps to self.coredata.deps since we
- # cannot cache them. They must always be evaluated else
- # we won't actually read all the build files.
- if dep.found():
- self.coredata.deps[identifier] = dep
- return DependencyHolder(dep, self.subproject)
- if has_fallback:
- return self.dependency_fallback(display_name, kwargs)
- return self.notfound_dependency()
- @FeatureNew('disabler', '0.44.0')
- @noKwargs
- @noPosargs
- def func_disabler(self, node, args, kwargs):
- return Disabler()
- def print_nested_info(self, dependency_name):
- message = ['Dependency', mlog.bold(dependency_name), 'not found but it is available in a sub-subproject.\n' +
- 'To use it in the current project, promote it by going in the project source\n'
- 'root and issuing']
- sprojs = mesonlib.detect_subprojects('subprojects', self.source_root)
- if dependency_name not in sprojs:
- return
- found = sprojs[dependency_name]
- if len(found) > 1:
- message.append('one of the following commands:')
- else:
- message.append('the following command:')
- command_templ = '\nmeson wrap promote {}'
- for l in found:
- message.append(mlog.bold(command_templ.format(l[len(self.source_root) + 1:])))
- mlog.warning(*message, location=self.current_node)
- def get_subproject_infos(self, kwargs):
- fbinfo = kwargs['fallback']
- check_stringlist(fbinfo)
- if len(fbinfo) != 2:
- raise InterpreterException('Fallback info must have exactly two items.')
- return fbinfo
- def dependency_fallback(self, display_name, kwargs):
- if self.coredata.get_builtin_option('wrap_mode') == WrapMode.nofallback:
- mlog.log('Not looking for a fallback subproject for the dependency',
- mlog.bold(display_name), 'because:\nUse of fallback'
- 'dependencies is disabled.')
- return self.notfound_dependency()
- elif self.coredata.get_builtin_option('wrap_mode') == WrapMode.forcefallback:
- mlog.log('Looking for a fallback subproject for the dependency',
- mlog.bold(display_name), 'because:\nUse of fallback dependencies is forced.')
- else:
- mlog.log('Looking for a fallback subproject for the dependency',
- mlog.bold(display_name))
- dirname, varname = self.get_subproject_infos(kwargs)
- sp_kwargs = {
- 'default_options': kwargs.get('default_options', []),
- 'required': kwargs.get('required', True),
- }
- self.do_subproject(dirname, sp_kwargs)
- return self.get_subproject_dep(display_name, dirname, varname, kwargs)
- @FeatureNewKwargs('executable', '0.42.0', ['implib'])
- @permittedKwargs(permitted_kwargs['executable'])
- def func_executable(self, node, args, kwargs):
- return self.build_target(node, args, kwargs, ExecutableHolder)
- @permittedKwargs(permitted_kwargs['static_library'])
- def func_static_lib(self, node, args, kwargs):
- return self.build_target(node, args, kwargs, StaticLibraryHolder)
- @permittedKwargs(permitted_kwargs['shared_library'])
- def func_shared_lib(self, node, args, kwargs):
- holder = self.build_target(node, args, kwargs, SharedLibraryHolder)
- holder.held_object.shared_library_only = True
- return holder
- @permittedKwargs(permitted_kwargs['both_libraries'])
- def func_both_lib(self, node, args, kwargs):
- return self.build_both_libraries(node, args, kwargs)
- @FeatureNew('shared_module', '0.37.0')
- @permittedKwargs(permitted_kwargs['shared_module'])
- def func_shared_module(self, node, args, kwargs):
- return self.build_target(node, args, kwargs, SharedModuleHolder)
- @permittedKwargs(permitted_kwargs['library'])
- def func_library(self, node, args, kwargs):
- return self.build_library(node, args, kwargs)
- @permittedKwargs(permitted_kwargs['jar'])
- def func_jar(self, node, args, kwargs):
- return self.build_target(node, args, kwargs, JarHolder)
- @FeatureNewKwargs('build_target', '0.40.0', ['link_whole', 'override_options'])
- @permittedKwargs(permitted_kwargs['build_target'])
- def func_build_target(self, node, args, kwargs):
- if 'target_type' not in kwargs:
- raise InterpreterException('Missing target_type keyword argument')
- target_type = kwargs.pop('target_type')
- if target_type == 'executable':
- return self.build_target(node, args, kwargs, ExecutableHolder)
- elif target_type == 'shared_library':
- return self.build_target(node, args, kwargs, SharedLibraryHolder)
- elif target_type == 'static_library':
- return self.build_target(node, args, kwargs, StaticLibraryHolder)
- elif target_type == 'both_libraries':
- return self.build_both_libraries(node, args, kwargs)
- elif target_type == 'library':
- return self.build_library(node, args, kwargs)
- elif target_type == 'jar':
- return self.build_target(node, args, kwargs, JarHolder)
- else:
- raise InterpreterException('Unknown target_type.')
- @permittedKwargs(permitted_kwargs['vcs_tag'])
- def func_vcs_tag(self, node, args, kwargs):
- if 'input' not in kwargs or 'output' not in kwargs:
- raise InterpreterException('Keyword arguments input and output must exist')
- if 'fallback' not in kwargs:
- FeatureNew('Optional fallback in vcs_tag', '0.41.0').use(self.subproject)
- fallback = kwargs.pop('fallback', self.project_version)
- if not isinstance(fallback, str):
- raise InterpreterException('Keyword argument fallback must be a string.')
- replace_string = kwargs.pop('replace_string', '@VCS_TAG@')
- regex_selector = '(.*)' # default regex selector for custom command: use complete output
- vcs_cmd = kwargs.get('command', None)
- if vcs_cmd and not isinstance(vcs_cmd, list):
- vcs_cmd = [vcs_cmd]
- source_dir = os.path.normpath(os.path.join(self.environment.get_source_dir(), self.subdir))
- if vcs_cmd:
- # Is the command an executable in path or maybe a script in the source tree?
- vcs_cmd[0] = shutil.which(vcs_cmd[0]) or os.path.join(source_dir, vcs_cmd[0])
- else:
- vcs = mesonlib.detect_vcs(source_dir)
- if vcs:
- mlog.log('Found %s repository at %s' % (vcs['name'], vcs['wc_dir']))
- vcs_cmd = vcs['get_rev'].split()
- regex_selector = vcs['rev_regex']
- else:
- vcs_cmd = [' '] # executing this cmd will fail in vcstagger.py and force to use the fallback string
- # vcstagger.py parameters: infile, outfile, fallback, source_dir, replace_string, regex_selector, command...
- kwargs['command'] = self.environment.get_build_command() + \
- ['--internal',
- 'vcstagger',
- '@INPUT0@',
- '@OUTPUT0@',
- fallback,
- source_dir,
- replace_string,
- regex_selector] + vcs_cmd
- kwargs.setdefault('build_by_default', True)
- kwargs.setdefault('build_always_stale', True)
- return self._func_custom_target_impl(node, [kwargs['output']], kwargs)
- @FeatureNew('subdir_done', '0.46.0')
- @stringArgs
- def func_subdir_done(self, node, args, kwargs):
- if len(kwargs) > 0:
- raise InterpreterException('exit does not take named arguments')
- if len(args) > 0:
- raise InterpreterException('exit does not take any arguments')
- raise SubdirDoneRequest()
- @stringArgs
- @FeatureNewKwargs('custom_target', '0.48.0', ['console'])
- @FeatureNewKwargs('custom_target', '0.47.0', ['install_mode', 'build_always_stale'])
- @FeatureNewKwargs('custom_target', '0.40.0', ['build_by_default'])
- @permittedKwargs(permitted_kwargs['custom_target'])
- def func_custom_target(self, node, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('custom_target: Only one positional argument is allowed, and it must be a string name')
- if 'depfile' in kwargs and ('@BASENAME@' in kwargs['depfile'] or '@PLAINNAME@' in kwargs['depfile']):
- FeatureNew('substitutions in custom_target depfile', '0.47.0').use(self.subproject)
- return self._func_custom_target_impl(node, args, kwargs)
- def _func_custom_target_impl(self, node, args, kwargs):
- 'Implementation-only, without FeatureNew checks, for internal use'
- name = args[0]
- kwargs['install_mode'] = self._get_kwarg_install_mode(kwargs)
- if 'input' in kwargs:
- try:
- kwargs['input'] = self.source_strings_to_files(extract_as_list(kwargs, 'input'))
- except mesonlib.MesonException:
- mlog.warning('''Custom target input \'%s\' can\'t be converted to File object(s).
- This will become a hard error in the future.''' % kwargs['input'], location=self.current_node)
- tg = CustomTargetHolder(build.CustomTarget(name, self.subdir, self.subproject, kwargs), self)
- self.add_target(name, tg.held_object)
- return tg
- @permittedKwargs(permitted_kwargs['run_target'])
- def func_run_target(self, node, args, kwargs):
- if len(args) > 1:
- raise InvalidCode('Run_target takes only one positional argument: the target name.')
- elif len(args) == 1:
- if 'command' not in kwargs:
- raise InterpreterException('Missing "command" keyword argument')
- all_args = extract_as_list(kwargs, 'command')
- deps = extract_as_list(kwargs, 'depends', unholder=True)
- else:
- raise InterpreterException('Run_target needs at least one positional argument.')
- cleaned_args = []
- for i in listify(all_args, unholder=True):
- if not isinstance(i, (str, build.BuildTarget, build.CustomTarget, dependencies.ExternalProgram, mesonlib.File)):
- mlog.debug('Wrong type:', str(i))
- raise InterpreterException('Invalid argument to run_target.')
- if isinstance(i, dependencies.ExternalProgram) and not i.found():
- raise InterpreterException('Tried to use non-existing executable {!r}'.format(i.name))
- cleaned_args.append(i)
- name = args[0]
- if not isinstance(name, str):
- raise InterpreterException('First argument must be a string.')
- cleaned_deps = []
- for d in deps:
- if not isinstance(d, (build.BuildTarget, build.CustomTarget)):
- raise InterpreterException('Depends items must be build targets.')
- cleaned_deps.append(d)
- command = cleaned_args[0]
- cmd_args = cleaned_args[1:]
- tg = RunTargetHolder(name, command, cmd_args, cleaned_deps, self.subdir, self.subproject)
- self.add_target(name, tg.held_object)
- return tg
- @permittedKwargs(permitted_kwargs['generator'])
- def func_generator(self, node, args, kwargs):
- gen = GeneratorHolder(self, args, kwargs)
- self.generators.append(gen)
- return gen
- @permittedKwargs(permitted_kwargs['benchmark'])
- def func_benchmark(self, node, args, kwargs):
- self.add_test(node, args, kwargs, False)
- @FeatureNewKwargs('test', '0.46.0', ['depends'])
- @permittedKwargs(permitted_kwargs['test'])
- def func_test(self, node, args, kwargs):
- self.add_test(node, args, kwargs, True)
- def unpack_env_kwarg(self, kwargs):
- envlist = kwargs.get('env', EnvironmentVariablesHolder())
- if isinstance(envlist, EnvironmentVariablesHolder):
- env = envlist.held_object
- else:
- envlist = listify(envlist)
- # Convert from array to environment object
- env = EnvironmentVariablesHolder()
- for e in envlist:
- if '=' not in e:
- raise InterpreterException('Env var definition must be of type key=val.')
- (k, val) = e.split('=', 1)
- k = k.strip()
- val = val.strip()
- if ' ' in k:
- raise InterpreterException('Env var key must not have spaces in it.')
- env.set_method([k, val], {})
- env = env.held_object
- return env
- def add_test(self, node, args, kwargs, is_base_test):
- if len(args) != 2:
- raise InterpreterException('Incorrect number of arguments')
- if not isinstance(args[0], str):
- raise InterpreterException('First argument of test must be a string.')
- exe = args[1]
- if not isinstance(exe, (ExecutableHolder, JarHolder, ExternalProgramHolder)):
- if isinstance(exe, mesonlib.File):
- exe = self.func_find_program(node, args[1], {})
- else:
- raise InterpreterException('Second argument must be executable.')
- par = kwargs.get('is_parallel', True)
- if not isinstance(par, bool):
- raise InterpreterException('Keyword argument is_parallel must be a boolean.')
- cmd_args = extract_as_list(kwargs, 'args', unholder=True)
- for i in cmd_args:
- if not isinstance(i, (str, mesonlib.File, build.Target)):
- raise InterpreterException('Command line arguments must be strings, files or targets.')
- env = self.unpack_env_kwarg(kwargs)
- should_fail = kwargs.get('should_fail', False)
- if not isinstance(should_fail, bool):
- raise InterpreterException('Keyword argument should_fail must be a boolean.')
- timeout = kwargs.get('timeout', 30)
- if 'workdir' in kwargs:
- workdir = kwargs['workdir']
- if not isinstance(workdir, str):
- raise InterpreterException('Workdir keyword argument must be a string.')
- if not os.path.isabs(workdir):
- raise InterpreterException('Workdir keyword argument must be an absolute path.')
- else:
- workdir = None
- if not isinstance(timeout, int):
- raise InterpreterException('Timeout must be an integer.')
- suite = []
- prj = self.subproject if self.is_subproject() else self.build.project_name
- for s in mesonlib.stringlistify(kwargs.get('suite', '')):
- if len(s) > 0:
- s = ':' + s
- suite.append(prj.replace(' ', '_').replace(':', '_') + s)
- depends = extract_as_list(kwargs, 'depends', unholder=True)
- for dep in depends:
- if not isinstance(dep, (build.CustomTarget, build.BuildTarget)):
- raise InterpreterException('Depends items must be build targets.')
- t = Test(args[0], prj, suite, exe.held_object, depends, par, cmd_args,
- env, should_fail, timeout, workdir)
- if is_base_test:
- self.build.tests.append(t)
- mlog.debug('Adding test', mlog.bold(args[0], True))
- else:
- self.build.benchmarks.append(t)
- mlog.debug('Adding benchmark', mlog.bold(args[0], True))
- @FeatureNewKwargs('install_headers', '0.47.0', ['install_mode'])
- @permittedKwargs(permitted_kwargs['install_headers'])
- def func_install_headers(self, node, args, kwargs):
- source_files = self.source_strings_to_files(args)
- kwargs['install_mode'] = self._get_kwarg_install_mode(kwargs)
- h = Headers(source_files, kwargs)
- self.build.headers.append(h)
- return h
- @FeatureNewKwargs('install_man', '0.47.0', ['install_mode'])
- @permittedKwargs(permitted_kwargs['install_man'])
- def func_install_man(self, node, args, kwargs):
- fargs = self.source_strings_to_files(args)
- kwargs['install_mode'] = self._get_kwarg_install_mode(kwargs)
- m = Man(fargs, kwargs)
- self.build.man.append(m)
- return m
- @FeatureNewKwargs('subdir', '0.44.0', ['if_found'])
- @permittedKwargs(permitted_kwargs['subdir'])
- def func_subdir(self, node, args, kwargs):
- self.validate_arguments(args, 1, [str])
- mesonlib.check_direntry_issues(args)
- if '..' in args[0]:
- raise InvalidArguments('Subdir contains ..')
- if self.subdir == '' and args[0] == self.subproject_dir:
- raise InvalidArguments('Must not go into subprojects dir with subdir(), use subproject() instead.')
- if self.subdir == '' and args[0].startswith('meson-'):
- raise InvalidArguments('The "meson-" prefix is reserved and cannot be used for top-level subdir().')
- for i in mesonlib.extract_as_list(kwargs, 'if_found'):
- if not hasattr(i, 'found_method'):
- raise InterpreterException('Object used in if_found does not have a found method.')
- if not i.found_method([], {}):
- return
- prev_subdir = self.subdir
- subdir = os.path.join(prev_subdir, args[0])
- if os.path.isabs(subdir):
- raise InvalidArguments('Subdir argument must be a relative path.')
- absdir = os.path.join(self.environment.get_source_dir(), subdir)
- symlinkless_dir = os.path.realpath(absdir)
- if symlinkless_dir in self.visited_subdirs:
- raise InvalidArguments('Tried to enter directory "%s", which has already been visited.'
- % subdir)
- self.visited_subdirs[symlinkless_dir] = True
- self.subdir = subdir
- os.makedirs(os.path.join(self.environment.build_dir, subdir), exist_ok=True)
- buildfilename = os.path.join(self.subdir, environment.build_filename)
- self.build_def_files.append(buildfilename)
- absname = os.path.join(self.environment.get_source_dir(), buildfilename)
- if not os.path.isfile(absname):
- self.subdir = prev_subdir
- raise InterpreterException('Non-existent build file {!r}'.format(buildfilename))
- with open(absname, encoding='utf8') as f:
- code = f.read()
- assert(isinstance(code, str))
- try:
- codeblock = mparser.Parser(code, self.subdir).parse()
- except mesonlib.MesonException as me:
- me.file = buildfilename
- raise me
- try:
- self.evaluate_codeblock(codeblock)
- except SubdirDoneRequest:
- pass
- self.subdir = prev_subdir
- def _get_kwarg_install_mode(self, kwargs):
- if kwargs.get('install_mode', None) is None:
- return None
- install_mode = []
- mode = mesonlib.typeslistify(kwargs.get('install_mode', []), (str, int))
- for m in mode:
- # We skip any arguments that are set to `false`
- if m is False:
- m = None
- install_mode.append(m)
- if len(install_mode) > 3:
- raise InvalidArguments('Keyword argument install_mode takes at '
- 'most 3 arguments.')
- if len(install_mode) > 0 and install_mode[0] is not None and \
- not isinstance(install_mode[0], str):
- raise InvalidArguments('Keyword argument install_mode requires the '
- 'permissions arg to be a string or false')
- return FileMode(*install_mode)
- @FeatureNewKwargs('install_data', '0.46.0', ['rename'])
- @FeatureNewKwargs('install_data', '0.38.0', ['install_mode'])
- @permittedKwargs(permitted_kwargs['install_data'])
- def func_install_data(self, node, args, kwargs):
- kwsource = mesonlib.stringlistify(kwargs.get('sources', []))
- raw_sources = args + kwsource
- sources = []
- source_strings = []
- for s in raw_sources:
- if isinstance(s, mesonlib.File):
- sources.append(s)
- else:
- source_strings.append(s)
- sources += self.source_strings_to_files(source_strings)
- install_dir = kwargs.get('install_dir', None)
- if not isinstance(install_dir, (str, type(None))):
- raise InvalidArguments('Keyword argument install_dir not a string.')
- install_mode = self._get_kwarg_install_mode(kwargs)
- rename = kwargs.get('rename', None)
- data = DataHolder(build.Data(sources, install_dir, install_mode, rename))
- self.build.data.append(data.held_object)
- return data
- @FeatureNewKwargs('install_subdir', '0.42.0', ['exclude_files', 'exclude_directories'])
- @FeatureNewKwargs('install_subdir', '0.38.0', ['install_mode'])
- @permittedKwargs(permitted_kwargs['install_subdir'])
- @stringArgs
- def func_install_subdir(self, node, args, kwargs):
- if len(args) != 1:
- raise InvalidArguments('Install_subdir requires exactly one argument.')
- subdir = args[0]
- if 'install_dir' not in kwargs:
- raise InvalidArguments('Missing keyword argument install_dir')
- install_dir = kwargs['install_dir']
- if not isinstance(install_dir, str):
- raise InvalidArguments('Keyword argument install_dir not a string.')
- if 'strip_directory' in kwargs:
- if not isinstance(kwargs['strip_directory'], bool):
- raise InterpreterException('"strip_directory" keyword must be a boolean.')
- strip_directory = kwargs['strip_directory']
- else:
- strip_directory = False
- if 'exclude_files' in kwargs:
- exclude = extract_as_list(kwargs, 'exclude_files')
- for f in exclude:
- if not isinstance(f, str):
- raise InvalidArguments('Exclude argument not a string.')
- elif os.path.isabs(f):
- raise InvalidArguments('Exclude argument cannot be absolute.')
- exclude_files = set(exclude)
- else:
- exclude_files = set()
- if 'exclude_directories' in kwargs:
- exclude = extract_as_list(kwargs, 'exclude_directories')
- for d in exclude:
- if not isinstance(d, str):
- raise InvalidArguments('Exclude argument not a string.')
- elif os.path.isabs(d):
- raise InvalidArguments('Exclude argument cannot be absolute.')
- exclude_directories = set(exclude)
- else:
- exclude_directories = set()
- exclude = (exclude_files, exclude_directories)
- install_mode = self._get_kwarg_install_mode(kwargs)
- idir = InstallDir(self.subdir, subdir, install_dir, install_mode, exclude, strip_directory)
- self.build.install_dirs.append(idir)
- return idir
- @FeatureNewKwargs('configure_file', '0.47.0', ['copy', 'output_format', 'install_mode', 'encoding'])
- @FeatureNewKwargs('configure_file', '0.46.0', ['format'])
- @FeatureNewKwargs('configure_file', '0.41.0', ['capture'])
- @FeatureNewKwargs('configure_file', '0.50.0', ['install'])
- @permittedKwargs(permitted_kwargs['configure_file'])
- def func_configure_file(self, node, args, kwargs):
- if len(args) > 0:
- raise InterpreterException("configure_file takes only keyword arguments.")
- if 'output' not in kwargs:
- raise InterpreterException('Required keyword argument "output" not defined.')
- actions = set(['configuration', 'command', 'copy']).intersection(kwargs.keys())
- if len(actions) == 0:
- raise InterpreterException('Must specify an action with one of these '
- 'keyword arguments: \'configuration\', '
- '\'command\', or \'copy\'.')
- elif len(actions) == 2:
- raise InterpreterException('Must not specify both {!r} and {!r} '
- 'keyword arguments since they are '
- 'mutually exclusive.'.format(*actions))
- elif len(actions) == 3:
- raise InterpreterException('Must specify one of {!r}, {!r}, and '
- '{!r} keyword arguments since they are '
- 'mutually exclusive.'.format(*actions))
- if 'capture' in kwargs:
- if not isinstance(kwargs['capture'], bool):
- raise InterpreterException('"capture" keyword must be a boolean.')
- if 'command' not in kwargs:
- raise InterpreterException('"capture" keyword requires "command" keyword.')
- if 'format' in kwargs:
- fmt = kwargs['format']
- if not isinstance(fmt, str):
- raise InterpreterException('"format" keyword must be a string.')
- else:
- fmt = 'meson'
- if fmt not in ('meson', 'cmake', 'cmake@'):
- raise InterpreterException('"format" possible values are "meson", "cmake" or "cmake@".')
- if 'output_format' in kwargs:
- output_format = kwargs['output_format']
- if not isinstance(output_format, str):
- raise InterpreterException('"output_format" keyword must be a string.')
- else:
- output_format = 'c'
- if output_format not in ('c', 'nasm'):
- raise InterpreterException('"format" possible values are "c" or "nasm".')
- # Validate input
- inputfile = None
- ifile_abs = None
- if 'input' in kwargs:
- inputfile = kwargs['input']
- if isinstance(inputfile, list):
- if len(inputfile) != 1:
- m = "Keyword argument 'input' requires exactly one file"
- raise InterpreterException(m)
- inputfile = inputfile[0]
- if not isinstance(inputfile, (str, mesonlib.File)):
- raise InterpreterException('Input must be a string or a file')
- if isinstance(inputfile, str):
- inputfile = mesonlib.File.from_source_file(self.environment.source_dir,
- self.subdir, inputfile)
- ifile_abs = inputfile.absolute_path(self.environment.source_dir,
- self.environment.build_dir)
- elif 'command' in kwargs and '@INPUT@' in kwargs['command']:
- raise InterpreterException('@INPUT@ used as command argument, but no input file specified.')
- # Validate output
- output = kwargs['output']
- if not isinstance(output, str):
- raise InterpreterException('Output file name must be a string')
- if ifile_abs:
- values = mesonlib.get_filenames_templates_dict([ifile_abs], None)
- outputs = mesonlib.substitute_values([output], values)
- output = outputs[0]
- ofile_rpath = os.path.join(self.subdir, output)
- if ofile_rpath in self.configure_file_outputs:
- mesonbuildfile = os.path.join(self.subdir, 'meson.build')
- current_call = "{}:{}".format(mesonbuildfile, self.current_lineno)
- first_call = "{}:{}".format(mesonbuildfile, self.configure_file_outputs[ofile_rpath])
- mlog.warning('Output file', mlog.bold(ofile_rpath, True), 'for configure_file() at', current_call, 'overwrites configure_file() output at', first_call)
- else:
- self.configure_file_outputs[ofile_rpath] = self.current_lineno
- if os.path.dirname(output) != '':
- raise InterpreterException('Output file name must not contain a subdirectory.')
- (ofile_path, ofile_fname) = os.path.split(os.path.join(self.subdir, output))
- ofile_abs = os.path.join(self.environment.build_dir, ofile_path, ofile_fname)
- # Perform the appropriate action
- if 'configuration' in kwargs:
- conf = kwargs['configuration']
- if isinstance(conf, dict):
- cdata = ConfigurationDataHolder(self.subproject)
- for k, v in conf.items():
- cdata.set_method([k, v], {})
- conf = cdata
- elif not isinstance(conf, ConfigurationDataHolder):
- raise InterpreterException('Argument "configuration" is not of type configuration_data')
- mlog.log('Configuring', mlog.bold(output), 'using configuration')
- if inputfile is not None:
- os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True)
- file_encoding = kwargs.setdefault('encoding', 'utf-8')
- missing_variables, confdata_useless = \
- mesonlib.do_conf_file(ifile_abs, ofile_abs, conf.held_object,
- fmt, file_encoding)
- if missing_variables:
- var_list = ", ".join(map(repr, sorted(missing_variables)))
- mlog.warning(
- "The variable(s) %s in the input file '%s' are not "
- "present in the given configuration data." % (
- var_list, inputfile), location=node)
- if confdata_useless:
- ifbase = os.path.basename(ifile_abs)
- mlog.warning('Got an empty configuration_data() object and found no '
- 'substitutions in the input file {!r}. If you want to '
- 'copy a file to the build dir, use the \'copy:\' keyword '
- 'argument added in 0.47.0'.format(ifbase), location=node)
- else:
- mesonlib.dump_conf_header(ofile_abs, conf.held_object, output_format)
- conf.mark_used()
- elif 'command' in kwargs:
- # We use absolute paths for input and output here because the cwd
- # that the command is run from is 'unspecified', so it could change.
- # Currently it's builddir/subdir for in_builddir else srcdir/subdir.
- if ifile_abs:
- values = mesonlib.get_filenames_templates_dict([ifile_abs], [ofile_abs])
- else:
- values = mesonlib.get_filenames_templates_dict(None, [ofile_abs])
- # Substitute @INPUT@, @OUTPUT@, etc here.
- cmd = mesonlib.substitute_values(kwargs['command'], values)
- mlog.log('Configuring', mlog.bold(output), 'with command')
- res = self.run_command_impl(node, cmd, {}, True)
- if res.returncode != 0:
- raise InterpreterException('Running configure command failed.\n%s\n%s' %
- (res.stdout, res.stderr))
- if 'capture' in kwargs and kwargs['capture']:
- dst_tmp = ofile_abs + '~'
- file_encoding = kwargs.setdefault('encoding', 'utf-8')
- with open(dst_tmp, 'w', encoding=file_encoding) as f:
- f.writelines(res.stdout)
- if ifile_abs:
- shutil.copymode(ifile_abs, dst_tmp)
- mesonlib.replace_if_different(ofile_abs, dst_tmp)
- elif 'copy' in kwargs:
- os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True)
- shutil.copyfile(ifile_abs, ofile_abs)
- shutil.copymode(ifile_abs, ofile_abs)
- else:
- # Not reachable
- raise AssertionError
- # If the input is a source file, add it to the list of files that we
- # need to reconfigure on when they change. FIXME: Do the same for
- # files() objects in the command: kwarg.
- if inputfile and not inputfile.is_built:
- # Normalize the path of the conffile (relative to the
- # source root) to avoid duplicates. This is especially
- # important to convert '/' to '\' on Windows
- conffile = os.path.normpath(inputfile.relative_name())
- if conffile not in self.build_def_files:
- self.build_def_files.append(conffile)
- # Install file if requested, we check for the empty string
- # for backwards compatibility. That was the behaviour before
- # 0.45.0 so preserve it.
- idir = kwargs.get('install_dir', '')
- if idir is False:
- idir = ''
- mlog.deprecation('Please use the new `install:` kwarg instead of passing '
- '`false` to `install_dir:`', location=node)
- if not isinstance(idir, str):
- raise InterpreterException('"install_dir" must be a string')
- install = kwargs.get('install', idir != '')
- if not isinstance(install, bool):
- raise InterpreterException('"install" must be a boolean')
- if install:
- if not idir:
- raise InterpreterException('"install_dir" must be specified '
- 'when "install" in a configure_file '
- 'is true')
- cfile = mesonlib.File.from_built_file(ofile_path, ofile_fname)
- install_mode = self._get_kwarg_install_mode(kwargs)
- self.build.data.append(build.Data([cfile], idir, install_mode))
- return mesonlib.File.from_built_file(self.subdir, output)
- def extract_incdirs(self, kwargs):
- prospectives = listify(kwargs.get('include_directories', []), unholder=True)
- result = []
- for p in prospectives:
- if isinstance(p, build.IncludeDirs):
- result.append(p)
- elif isinstance(p, str):
- result.append(self.build_incdir_object([p]).held_object)
- else:
- raise InterpreterException('Include directory objects can only be created from strings or include directories.')
- return result
- @permittedKwargs(permitted_kwargs['include_directories'])
- @stringArgs
- def func_include_directories(self, node, args, kwargs):
- return self.build_incdir_object(args, kwargs.get('is_system', False))
- def build_incdir_object(self, incdir_strings, is_system=False):
- if not isinstance(is_system, bool):
- raise InvalidArguments('Is_system must be boolean.')
- src_root = self.environment.get_source_dir()
- build_root = self.environment.get_build_dir()
- absbase_src = os.path.join(src_root, self.subdir)
- absbase_build = os.path.join(build_root, self.subdir)
- for a in incdir_strings:
- if a.startswith(src_root):
- raise InvalidArguments('''Tried to form an absolute path to a source dir. You should not do that but use
- relative paths instead.
- To get include path to any directory relative to the current dir do
- incdir = include_directories(dirname)
- After this incdir will contain both the current source dir as well as the
- corresponding build dir. It can then be used in any subdirectory and
- Meson will take care of all the busywork to make paths work.
- Dirname can even be '.' to mark the current directory. Though you should
- remember that the current source and build directories are always
- put in the include directories by default so you only need to do
- include_directories('.') if you intend to use the result in a
- different subdirectory.
- ''')
- absdir_src = os.path.join(absbase_src, a)
- absdir_build = os.path.join(absbase_build, a)
- if not os.path.isdir(absdir_src) and not os.path.isdir(absdir_build):
- raise InvalidArguments('Include dir %s does not exist.' % a)
- i = IncludeDirsHolder(build.IncludeDirs(self.subdir, incdir_strings, is_system))
- return i
- @permittedKwargs(permitted_kwargs['add_test_setup'])
- @stringArgs
- def func_add_test_setup(self, node, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Add_test_setup needs one argument for the setup name.')
- setup_name = args[0]
- if re.fullmatch('([_a-zA-Z][_0-9a-zA-Z]*:)?[_a-zA-Z][_0-9a-zA-Z]*', setup_name) is None:
- raise InterpreterException('Setup name may only contain alphanumeric characters.')
- if ":" not in setup_name:
- setup_name = (self.subproject if self.subproject else self.build.project_name) + ":" + setup_name
- try:
- inp = extract_as_list(kwargs, 'exe_wrapper', unholder=True)
- exe_wrapper = []
- for i in inp:
- if isinstance(i, str):
- exe_wrapper.append(i)
- elif isinstance(i, dependencies.ExternalProgram):
- if not i.found():
- raise InterpreterException('Tried to use non-found executable.')
- exe_wrapper += i.get_command()
- else:
- raise InterpreterException('Exe wrapper can only contain strings or external binaries.')
- except KeyError:
- exe_wrapper = None
- gdb = kwargs.get('gdb', False)
- if not isinstance(gdb, bool):
- raise InterpreterException('Gdb option must be a boolean')
- timeout_multiplier = kwargs.get('timeout_multiplier', 1)
- if not isinstance(timeout_multiplier, int):
- raise InterpreterException('Timeout multiplier must be a number.')
- is_default = kwargs.get('is_default', False)
- if not isinstance(is_default, bool):
- raise InterpreterException('is_default option must be a boolean')
- if is_default:
- if self.build.test_setup_default_name is not None:
- raise InterpreterException('\'%s\' is already set as default. '
- 'is_default can be set to true only once' % self.build.test_setup_default_name)
- self.build.test_setup_default_name = setup_name
- env = self.unpack_env_kwarg(kwargs)
- self.build.test_setups[setup_name] = build.TestSetup(exe_wrapper=exe_wrapper,
- gdb=gdb,
- timeout_multiplier=timeout_multiplier,
- env=env)
- def get_argdict_on_crossness(self, native_dict, cross_dict, kwargs):
- for_native = kwargs.get('native', not self.environment.is_cross_build())
- if not isinstance(for_native, bool):
- raise InterpreterException('Keyword native must be a boolean.')
- if for_native:
- return native_dict
- else:
- return cross_dict
- @permittedKwargs(permitted_kwargs['add_global_arguments'])
- @stringArgs
- def func_add_global_arguments(self, node, args, kwargs):
- argdict = self.get_argdict_on_crossness(self.build.global_args,
- self.build.cross_global_args,
- kwargs)
- self.add_global_arguments(node, argdict, args, kwargs)
- @permittedKwargs(permitted_kwargs['add_global_link_arguments'])
- @stringArgs
- def func_add_global_link_arguments(self, node, args, kwargs):
- argdict = self.get_argdict_on_crossness(self.build.global_link_args,
- self.build.cross_global_link_args,
- kwargs)
- self.add_global_arguments(node, argdict, args, kwargs)
- @permittedKwargs(permitted_kwargs['add_project_arguments'])
- @stringArgs
- def func_add_project_arguments(self, node, args, kwargs):
- argdict = self.get_argdict_on_crossness(self.build.projects_args,
- self.build.cross_projects_args,
- kwargs)
- self.add_project_arguments(node, argdict, args, kwargs)
- @permittedKwargs(permitted_kwargs['add_project_link_arguments'])
- @stringArgs
- def func_add_project_link_arguments(self, node, args, kwargs):
- argdict = self.get_argdict_on_crossness(self.build.projects_link_args,
- self.build.cross_projects_link_args, kwargs)
- self.add_project_arguments(node, argdict, args, kwargs)
- def add_global_arguments(self, node, argsdict, args, kwargs):
- if self.is_subproject():
- msg = 'Function \'{}\' cannot be used in subprojects because ' \
- 'there is no way to make that reliable.\nPlease only call ' \
- 'this if is_subproject() returns false. Alternatively, ' \
- 'define a variable that\ncontains your language-specific ' \
- 'arguments and add it to the appropriate *_args kwarg ' \
- 'in each target.'.format(node.func_name)
- raise InvalidCode(msg)
- frozen = self.project_args_frozen or self.global_args_frozen
- self.add_arguments(node, argsdict, frozen, args, kwargs)
- def add_project_arguments(self, node, argsdict, args, kwargs):
- if self.subproject not in argsdict:
- argsdict[self.subproject] = {}
- self.add_arguments(node, argsdict[self.subproject],
- self.project_args_frozen, args, kwargs)
- def add_arguments(self, node, argsdict, args_frozen, args, kwargs):
- if args_frozen:
- msg = 'Tried to use \'{}\' after a build target has been declared.\n' \
- 'This is not permitted. Please declare all ' \
- 'arguments before your targets.'.format(node.func_name)
- raise InvalidCode(msg)
- if 'language' not in kwargs:
- raise InvalidCode('Missing language definition in {}'.format(node.func_name))
- for lang in mesonlib.stringlistify(kwargs['language']):
- lang = lang.lower()
- argsdict[lang] = argsdict.get(lang, []) + args
- @noKwargs
- @noPosargs
- def func_environment(self, node, args, kwargs):
- return EnvironmentVariablesHolder()
- @stringArgs
- @noKwargs
- def func_join_paths(self, node, args, kwargs):
- return self.join_path_strings(args)
- def run(self):
- super().run()
- mlog.log('Build targets in project:', mlog.bold(str(len(self.build.targets))))
- FeatureNew.report(self.subproject)
- FeatureDeprecated.report(self.subproject)
- if not self.is_subproject():
- self.print_extra_warnings()
- def print_extra_warnings(self):
- for c in self.build.compilers.values():
- if c.get_id() == 'clang':
- self.check_clang_asan_lundef()
- break
- def check_clang_asan_lundef(self):
- if 'b_lundef' not in self.coredata.base_options:
- return
- if 'b_sanitize' not in self.coredata.base_options:
- return
- if (self.coredata.base_options['b_lundef'].value and
- self.coredata.base_options['b_sanitize'].value != 'none'):
- mlog.warning('''Trying to use {} sanitizer on Clang with b_lundef.
- This will probably not work.
- Try setting b_lundef to false instead.'''.format(self.coredata.base_options['b_sanitize'].value),
- location=self.current_node)
- def evaluate_subproject_info(self, path_from_source_root, subproject_dirname):
- depth = 0
- subproj_name = ''
- segs = PurePath(path_from_source_root).parts
- segs_spd = PurePath(subproject_dirname).parts
- while segs and segs[0] == segs_spd[0]:
- if len(segs_spd) == 1:
- subproj_name = segs[1]
- segs = segs[2:]
- depth += 1
- else:
- segs_spd = segs_spd[1:]
- segs = segs[1:]
- return (depth, subproj_name)
- # Check that the indicated file is within the same subproject
- # as we currently are. This is to stop people doing
- # nasty things like:
- #
- # f = files('../../master_src/file.c')
- #
- # Note that this is validated only when the file
- # object is generated. The result can be used in a different
- # subproject than it is defined in (due to e.g. a
- # declare_dependency).
- def validate_within_subproject(self, subdir, fname):
- norm = os.path.normpath(os.path.join(subdir, fname))
- if os.path.isabs(norm):
- if not norm.startswith(self.environment.source_dir):
- # Grabbing files outside the source tree is ok.
- # This is for vendor stuff like:
- #
- # /opt/vendorsdk/src/file_with_license_restrictions.c
- return
- norm = os.path.relpath(norm, self.environment.source_dir)
- assert(not os.path.isabs(norm))
- (num_sps, sproj_name) = self.evaluate_subproject_info(norm, self.subproject_dir)
- plain_filename = os.path.basename(norm)
- if num_sps == 0:
- if not self.is_subproject():
- return
- raise InterpreterException('Sandbox violation: Tried to grab file %s from a different subproject.' % plain_filename)
- if num_sps > 1:
- raise InterpreterException('Sandbox violation: Tried to grab file %s from a nested subproject.' % plain_filename)
- if sproj_name != self.subproject_directory_name:
- raise InterpreterException('Sandbox violation: Tried to grab file %s from a different subproject.' % plain_filename)
- def source_strings_to_files(self, sources):
- results = []
- mesonlib.check_direntry_issues(sources)
- if not isinstance(sources, list):
- sources = [sources]
- for s in sources:
- if isinstance(s, (mesonlib.File, GeneratedListHolder,
- TargetHolder, CustomTargetIndexHolder)):
- pass
- elif isinstance(s, str):
- self.validate_within_subproject(self.subdir, s)
- s = mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s)
- else:
- raise InterpreterException('Source item is {!r} instead of '
- 'string or File-type object'.format(s))
- results.append(s)
- return results
- def add_target(self, name, tobj):
- if name == '':
- raise InterpreterException('Target name must not be empty.')
- if name.strip() == '':
- raise InterpreterException('Target name must not consist only of whitespace.')
- if name.startswith('meson-'):
- raise InvalidArguments("Target names starting with 'meson-' are reserved "
- "for Meson's internal use. Please rename.")
- if name in coredata.forbidden_target_names:
- raise InvalidArguments("Target name '%s' is reserved for Meson's "
- "internal use. Please rename." % name)
- # To permit an executable and a shared library to have the
- # same name, such as "foo.exe" and "libfoo.a".
- idname = tobj.get_id()
- if idname in self.build.targets:
- raise InvalidCode('Tried to create target "%s", but a target of that name already exists.' % name)
- self.build.targets[idname] = tobj
- if idname not in self.coredata.target_guids:
- self.coredata.target_guids[idname] = str(uuid.uuid4()).upper()
- @FeatureNew('both_libraries', '0.46.0')
- def build_both_libraries(self, node, args, kwargs):
- shared_holder = self.build_target(node, args, kwargs, SharedLibraryHolder)
- # Check if user forces non-PIC static library.
- pic = True
- if 'pic' in kwargs:
- pic = kwargs['pic']
- elif 'b_staticpic' in self.environment.coredata.base_options:
- pic = self.environment.coredata.base_options['b_staticpic'].value
- if pic:
- # Exclude sources from args and kwargs to avoid building them twice
- static_args = [args[0]]
- static_kwargs = kwargs.copy()
- static_kwargs['sources'] = []
- static_kwargs['objects'] = shared_holder.held_object.extract_all_objects()
- else:
- static_args = args
- static_kwargs = kwargs
- static_holder = self.build_target(node, static_args, static_kwargs, StaticLibraryHolder)
- return BothLibrariesHolder(shared_holder, static_holder, self)
- def build_library(self, node, args, kwargs):
- default_library = self.coredata.get_builtin_option('default_library')
- if default_library == 'shared':
- return self.build_target(node, args, kwargs, SharedLibraryHolder)
- elif default_library == 'static':
- return self.build_target(node, args, kwargs, StaticLibraryHolder)
- elif default_library == 'both':
- return self.build_both_libraries(node, args, kwargs)
- else:
- raise InterpreterException('Unknown default_library value: %s.', default_library)
- def build_target(self, node, args, kwargs, targetholder):
- @FeatureNewKwargs('build target', '0.42.0', ['rust_crate_type', 'build_rpath', 'implicit_include_directories'])
- @FeatureNewKwargs('build target', '0.41.0', ['rust_args'])
- @FeatureNewKwargs('build target', '0.40.0', ['build_by_default'])
- @FeatureNewKwargs('build target', '0.48.0', ['gnu_symbol_visibility'])
- def build_target_decorator_caller(self, node, args, kwargs):
- return True
- build_target_decorator_caller(self, node, args, kwargs)
- if not args:
- raise InterpreterException('Target does not have a name.')
- name = args[0]
- sources = listify(args[1:])
- if self.environment.is_cross_build():
- if kwargs.get('native', False):
- is_cross = False
- else:
- is_cross = True
- else:
- is_cross = False
- if 'sources' in kwargs:
- sources += listify(kwargs['sources'])
- sources = self.source_strings_to_files(sources)
- objs = extract_as_list(kwargs, 'objects')
- kwargs['dependencies'] = extract_as_list(kwargs, 'dependencies')
- kwargs['install_mode'] = self._get_kwarg_install_mode(kwargs)
- if 'extra_files' in kwargs:
- ef = extract_as_list(kwargs, 'extra_files')
- kwargs['extra_files'] = self.source_strings_to_files(ef)
- self.check_sources_exist(os.path.join(self.source_root, self.subdir), sources)
- if targetholder is ExecutableHolder:
- targetclass = build.Executable
- elif targetholder is SharedLibraryHolder:
- targetclass = build.SharedLibrary
- elif targetholder is SharedModuleHolder:
- targetclass = build.SharedModule
- elif targetholder is StaticLibraryHolder:
- targetclass = build.StaticLibrary
- elif targetholder is JarHolder:
- targetclass = build.Jar
- else:
- mlog.debug('Unknown target type:', str(targetholder))
- raise RuntimeError('Unreachable code')
- self.kwarg_strings_to_includedirs(kwargs)
- # Filter out kwargs from other target types. For example 'soversion'
- # passed to library() when default_library == 'static'.
- kwargs = {k: v for k, v in kwargs.items() if k in targetclass.known_kwargs}
- kwargs['include_directories'] = self.extract_incdirs(kwargs)
- target = targetclass(name, self.subdir, self.subproject, is_cross, sources, objs, self.environment, kwargs)
- if is_cross:
- self.add_cross_stdlib_info(target)
- l = targetholder(target, self)
- self.add_target(name, l.held_object)
- self.project_args_frozen = True
- return l
- def kwarg_strings_to_includedirs(self, kwargs):
- if 'd_import_dirs' in kwargs:
- items = mesonlib.extract_as_list(kwargs, 'd_import_dirs')
- cleaned_items = []
- for i in items:
- if isinstance(i, str):
- # BW compatibility. This was permitted so we must support it
- # for a few releases so people can transition to "correct"
- # path declarations.
- if os.path.normpath(i).startswith(self.environment.get_source_dir()):
- mlog.warning('''Building a path to the source dir is not supported. Use a relative path instead.
- This will become a hard error in the future.''', location=self.current_node)
- i = os.path.relpath(i, os.path.join(self.environment.get_source_dir(), self.subdir))
- i = self.build_incdir_object([i])
- cleaned_items.append(i)
- kwargs['d_import_dirs'] = cleaned_items
- def get_used_languages(self, target):
- result = {}
- for i in target.sources:
- for lang, c in self.build.compilers.items():
- if c.can_compile(i):
- result[lang] = True
- break
- return result
- def add_cross_stdlib_info(self, target):
- for l in self.get_used_languages(target):
- props = self.environment.properties.host
- if props.has_stdlib(l) \
- and self.subproject != props.get_stdlib(l)[0]:
- target.add_deps(self.build.cross_stdlibs[l])
- def check_sources_exist(self, subdir, sources):
- for s in sources:
- if not isinstance(s, str):
- continue # This means a generated source and they always exist.
- fname = os.path.join(subdir, s)
- if not os.path.isfile(fname):
- raise InterpreterException('Tried to add non-existing source file %s.' % s)
- def format_string(self, templ, args):
- if isinstance(args, mparser.ArgumentNode):
- args = args.arguments
- arg_strings = []
- for arg in args:
- arg = self.evaluate_statement(arg)
- if isinstance(arg, bool): # Python boolean is upper case.
- arg = str(arg).lower()
- arg_strings.append(str(arg))
- def arg_replace(match):
- idx = int(match.group(1))
- if idx >= len(arg_strings):
- raise InterpreterException('Format placeholder @{}@ out of range.'.format(idx))
- return arg_strings[idx]
- return re.sub(r'@(\d+)@', arg_replace, templ)
- # Only permit object extraction from the same subproject
- def validate_extraction(self, buildtarget):
- if not self.subdir.startswith(self.subproject_dir):
- if buildtarget.subdir.startswith(self.subproject_dir):
- raise InterpreterException('Tried to extract objects from a subproject target.')
- else:
- if not buildtarget.subdir.startswith(self.subproject_dir):
- raise InterpreterException('Tried to extract objects from the main project from a subproject.')
- if self.subdir.split('/')[1] != buildtarget.subdir.split('/')[1]:
- raise InterpreterException('Tried to extract objects from a different subproject.')
- def check_contains(self, obj, args):
- if len(args) != 1:
- raise InterpreterException('Contains method takes exactly one argument.')
- item = args[0]
- for element in obj:
- if isinstance(element, list):
- found = self.check_contains(element, args)
- if found:
- return True
- if element == item:
- return True
- return False
- def is_subproject(self):
- return self.subproject != ''
- @noKwargs
- @noArgsFlattening
- def func_set_variable(self, node, args, kwargs):
- if len(args) != 2:
- raise InvalidCode('Set_variable takes two arguments.')
- varname = args[0]
- value = args[1]
- self.set_variable(varname, value)
- @noKwargs
- @noArgsFlattening
- def func_get_variable(self, node, args, kwargs):
- if len(args) < 1 or len(args) > 2:
- raise InvalidCode('Get_variable takes one or two arguments.')
- varname = args[0]
- if not isinstance(varname, str):
- raise InterpreterException('First argument must be a string.')
- try:
- return self.variables[varname]
- except KeyError:
- pass
- if len(args) == 2:
- return args[1]
- raise InterpreterException('Tried to get unknown variable "%s".' % varname)
- @stringArgs
- @noKwargs
- def func_is_variable(self, node, args, kwargs):
- if len(args) != 1:
- raise InvalidCode('Is_variable takes two arguments.')
- varname = args[0]
- return varname in self.variables
|