12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751 |
- # Copyright 2012-2019 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, unholder
- from .dependencies import ExternalProgram
- from .dependencies import InternalDependency, Dependency, NotFoundDependency, DependencyException
- from .depfile import DepFile
- 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
- from .cmake import CMakeInterpreter
- from .backend.backends import TestProtocol
- from pathlib import Path, PurePath
- import os
- import shutil
- import uuid
- import re
- import shlex
- import subprocess
- import collections
- import functools
- import typing as T
- 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 OverrideProgram(dependencies.ExternalProgram):
- pass
- class FeatureOptionHolder(InterpreterObject, ObjectHolder):
- def __init__(self, env, name, option):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, option)
- if option.is_auto():
- self.held_object = env.coredata.builtins['auto_features']
- self.name = 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
- def extract_search_dirs(kwargs):
- search_dirs = mesonlib.stringlistify(kwargs.get('dirs', []))
- search_dirs = [Path(d).expanduser() for d in search_dirs]
- for d in search_dirs:
- if mesonlib.is_windows() and d.root.startswith('\\'):
- # a Unix-path starting with `/` that is not absolute on Windows.
- # discard without failing for end-user ease of cross-platform directory arrays
- continue
- if not d.is_absolute():
- raise InvalidCode('Search directory {} is not an absolute path.'.format(d))
- return list(map(str, search_dirs))
- 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, initial_values=None):
- MutableInterpreterObject.__init__(self)
- ObjectHolder.__init__(self, build.EnvironmentVariables())
- self.methods.update({'set': self.set_method,
- 'append': self.append_method,
- 'prepend': self.prepend_method,
- })
- if isinstance(initial_values, dict):
- for k, v in initial_values.items():
- self.set_method([k, v], {})
- elif isinstance(initial_values, list):
- for e in initial_values:
- 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.')
- self.set_method([k, val], {})
- elif initial_values:
- raise AssertionError('Unsupported EnvironmentVariablesHolder initial_values')
- 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")
- # Warn when someone tries to use append() or prepend() on an env var
- # which already has an operation set on it. People seem to think that
- # multiple append/prepend operations stack, but they don't.
- if method != self.held_object.set and self.held_object.has_name(args[0]):
- mlog.warning('Overriding previous value of environment variable {!r} with a new one'
- .format(args[0]), location=self.current_node)
- self.held_object.add_var(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, initial_values=None):
- 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,
- })
- if isinstance(initial_values, dict):
- for k, v in initial_values.items():
- self.set_method([k, v], {})
- elif initial_values:
- raise AssertionError('Unsupported ConfigurationDataHolder initial_values')
- 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, val = args
- 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,
- 'get_variable': self.variable_method,
- 'partial_dependency': self.partial_dependency_method,
- 'include_type': self.include_type_method,
- 'as_system': self.as_system_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)
- @FeatureNew('dep.get_variable', '0.51.0')
- @noPosargs
- @permittedKwargs({'cmake', 'pkgconfig', 'configtool', 'internal', 'default_value', 'pkgconfig_define'})
- @FeatureNewKwargs('dep.get_variable', '0.54.0', ['internal'])
- def variable_method(self, args, kwargs):
- return self.held_object.get_variable(**kwargs)
- @FeatureNew('dep.include_type', '0.52.0')
- @noPosargs
- @permittedKwargs({})
- def include_type_method(self, args, kwargs):
- return self.held_object.get_include_type()
- @FeatureNew('dep.as_system', '0.52.0')
- @permittedKwargs({})
- def as_system_method(self, args, kwargs):
- args = listify(args)
- new_is_system = 'system'
- if len(args) > 1:
- raise InterpreterException('as_system takes only one optional value')
- if len(args) == 1:
- new_is_system = args[0]
- new_dep = self.held_object.generate_system_dependency(new_is_system)
- return DependencyHolder(new_dep, self.subproject)
- class ExternalProgramHolder(InterpreterObject, ObjectHolder):
- def __init__(self, ep, subproject, backend=None):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, ep)
- self.subproject = subproject
- self.backend = backend
- self.methods.update({'found': self.found_method,
- 'path': self.path_method,
- 'full_path': self.full_path_method})
- self.cached_version = None
- @noPosargs
- @permittedKwargs({})
- def found_method(self, args, kwargs):
- return self.found()
- @noPosargs
- @permittedKwargs({})
- def path_method(self, args, kwargs):
- mlog.deprecation('path() method is deprecated and replaced by full_path()')
- return self._full_path()
- @noPosargs
- @permittedKwargs({})
- @FeatureNew('ExternalProgram.full_path', '0.55.0')
- def full_path_method(self, args, kwargs):
- return self._full_path()
- def _full_path(self):
- exe = self.held_object
- if isinstance(exe, build.Executable):
- return self.backend.get_target_filename_abs(exe)
- return exe.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):
- exe = self.held_object
- if isinstance(exe, build.Executable):
- return exe.name
- return exe.get_name()
- def get_version(self, interpreter):
- if isinstance(self.held_object, build.Executable):
- return self.held_object.project_version
- if not self.cached_version:
- raw_cmd = self.get_command() + ['--version']
- cmd = [self, '--version']
- res = interpreter.run_command_impl(interpreter.current_node, cmd, {}, True)
- if res.returncode != 0:
- m = 'Running {!r} failed'
- raise InterpreterException(m.format(raw_cmd))
- output = res.stdout.strip()
- if not output:
- output = res.stderr.strip()
- match = re.search(r'([0-9][0-9\.]+)', output)
- if not match:
- m = 'Could not find a version number in output of {!r}'
- raise InterpreterException(m.format(raw_cmd))
- self.cached_version = match.group(1)
- return self.cached_version
- 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=None):
- InterpreterObject.__init__(self)
- if isinstance(arg1, GeneratorHolder):
- ObjectHolder.__init__(self, build.GeneratedList(arg1.held_object, extra_args if extra_args is not None else []))
- 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', '')
- if os.path.isabs(self.install_subdir):
- mlog.deprecation('Subdir keyword must not be an absolute path. This will be a hard error in the next release.')
- 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,
- 'name': self.name_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 not self.held_object.environment.machines.matches_build_machine(self.held_object.for_machine)
- @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()
- @FeatureNew('name', '0.54.0')
- @noPosargs
- @permittedKwargs({})
- def name_method(self, args, kwargs):
- return self.held_object.name
- 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(TargetHolder):
- def __init__(self, target, interp):
- super().__init__(target, interp)
- self.methods.update({'full_path': self.full_path_method,
- })
- @FeatureNew('custom_target[i].full_path', '0.54.0')
- @noPosargs
- @permittedKwargs({})
- def full_path_method(self, args, kwargs):
- return self.interpreter.backend.get_target_filename_abs(self.held_object)
- class CustomTargetHolder(TargetHolder):
- def __init__(self, target, interp):
- super().__init__(target, interp)
- self.methods.update({'full_path': self.full_path_method,
- 'to_list': self.to_list_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)
- @FeatureNew('custom_target.to_list', '0.54.0')
- @noPosargs
- @permittedKwargs({})
- def to_list_method(self, args, kwargs):
- result = []
- for i in self.held_object:
- result.append(CustomTargetIndexHolder(i, self.interpreter))
- return result
- def __getitem__(self, index):
- return CustomTargetIndexHolder(self.held_object[index], self.interpreter)
- def __setitem__(self, index, value): # lgtm[py/unexpected-raise-in-special-method]
- raise InterpreterException('Cannot set a member of a CustomTarget')
- def __delitem__(self, index): # lgtm[py/unexpected-raise-in-special-method]
- 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(TargetHolder):
- def __init__(self, target, interp):
- super().__init__(target, interp)
- 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: str, project: str, suite: T.List[str], exe: build.Executable,
- depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]],
- is_parallel: bool, cmd_args: T.List[str], env: build.EnvironmentVariables,
- should_fail: bool, timeout: int, workdir: T.Optional[str], protocol: str,
- priority: int):
- 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
- self.protocol = TestProtocol.from_str(protocol)
- self.priority = priority
- 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, warnings=0, disabled_feature=None,
- exception=None):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, subinterpreter)
- self.name = name
- self.warnings = warnings
- self.disabled_feature = disabled_feature
- self.exception = exception
- 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({})
- @noArgsFlattening
- def get_variable_method(self, args, kwargs):
- if len(args) < 1 or len(args) > 2:
- raise InterpreterException('Get_variable takes one or two arguments.')
- 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 first argument must be a string.')
- try:
- return self.held_object.variables[varname]
- except KeyError:
- pass
- if len(args) == 2:
- return args[1]
- raise InvalidArguments('Requested variable "{0}" not found.'.format(varname))
- header_permitted_kwargs = set([
- 'required',
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- ])
- find_library_permitted_kwargs = set([
- 'has_headers',
- 'required',
- 'dirs',
- 'static',
- ])
- 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,
- 'get_linker_id': self.get_linker_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)
- if not nobuiltins:
- for_machine = Interpreter.machine_from_native_kwarg(kwargs)
- opts = self.environment.coredata.compiler_options[for_machine][self.compiler.language]
- 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({})
- @FeatureNew('compiler.get_linker_id', '0.53.0')
- def get_linker_id_method(self, args, kwargs):
- return self.compiler.get_linker_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, membername = args
- 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, cached = self.compiler.has_members(typename, [membername], prefix,
- self.environment,
- extra_args=extra_args,
- dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- 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, cached)
- 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, *membernames = args
- 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, cached = self.compiler.has_members(typename, membernames, prefix,
- self.environment,
- extra_args=extra_args,
- dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- 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, cached)
- 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, cached = self.compiler.has_function(funcname, prefix, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- if had:
- hadtxt = mlog.green('YES')
- else:
- hadtxt = mlog.red('NO')
- mlog.log('Checking for function', mlog.bold(funcname, True), msg, hadtxt, cached)
- 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, cached = self.compiler.has_type(typename, prefix, self.environment,
- extra_args=extra_args, dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- if had:
- hadtxt = mlog.green('YES')
- else:
- hadtxt = mlog.red('NO')
- mlog.log('Checking for type', mlog.bold(typename, True), msg, hadtxt, cached)
- 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, cached = self.compiler.get_define(element, prefix, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- mlog.log('Fetching value of define', mlog.bold(element, True), msg, value, cached)
- 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, cached = 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')
- cached = mlog.blue('(cached)') if cached else ''
- mlog.log('Checking if', mlog.bold(testname, True), msg, 'compiles:', h, cached)
- 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, cached = self.compiler.links(code, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- 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, cached)
- 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, cached = self.compiler.check_header(hname, prefix, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- 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, cached)
- 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, cached = self.compiler.has_header(hname, prefix, self.environment,
- extra_args=extra_args, dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- 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, cached)
- 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, symbol = args
- 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, cached = 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')
- cached = mlog.blue('(cached)') if cached else ''
- mlog.log('Header <{0}> has symbol'.format(hname), mlog.bold(symbol, True), msg, h, cached)
- 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.51.0', ['static'])
- @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 = extract_search_dirs(kwargs)
- libtype = mesonlib.LibType.PREFER_SHARED
- if 'static' in kwargs:
- if not isinstance(kwargs['static'], bool):
- raise InterpreterException('static must be a boolean')
- libtype = mesonlib.LibType.STATIC if kwargs['static'] else mesonlib.LibType.SHARED
- linkargs = self.compiler.find_library(libname, self.environment, search_dirs, libtype)
- 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: T.Sequence[str], kwargs) -> bool:
- 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: T.Sequence[str], kwargs: dict):
- args = mesonlib.stringlistify(args)
- result, cached = self.compiler.has_multi_arguments(args, self.environment)
- if result:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- cached = mlog.blue('(cached)') if cached else ''
- mlog.log(
- 'Compiler for {} supports arguments {}:'.format(
- self.compiler.get_display_language(), ' '.join(args)),
- h, cached)
- 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: T.Sequence[str], kwargs: dict) -> T.List[str]:
- for arg in mesonlib.stringlistify(args):
- if self.has_argument_method(arg, kwargs):
- mlog.log('First supported argument:', mlog.bold(arg))
- return [arg]
- 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, cached = self.compiler.has_multi_link_arguments(args, self.environment)
- cached = mlog.blue('(cached)') if cached else ''
- 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, cached)
- 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, cached = self.compiler.has_func_attribute(args[0], self.environment)
- cached = mlog.blue('(cached)') if cached else ''
- 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, cached)
- 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 = collections.namedtuple('ModuleState', [
- 'source_root', 'build_to_src', 'subproject', 'subdir', 'current_lineno', 'environment',
- 'project_name', 'project_version', 'backend', '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(
- source_root = self.interpreter.environment.get_source_dir(),
- 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,
- targets=self.interpreter.build.targets,
- data=self.interpreter.build.data,
- headers=self.interpreter.build.get_headers(),
- man=self.interpreter.build.get_man(),
- #global_args_for_build = self.interpreter.build.global_args.build,
- global_args = self.interpreter.build.global_args.host,
- #project_args_for_build = self.interpreter.build.projects_args.build.get(self.interpreter.subproject, {}),
- project_args = self.interpreter.build.projects_args.host.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
- )
- # Many modules do for example self.interpreter.find_program_impl(),
- # so we have to ensure they use the current interpreter and not the one
- # that first imported that module, otherwise it will use outdated
- # overrides.
- self.held_object.interpreter = self.interpreter
- 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 Summary:
- def __init__(self, project_name, project_version):
- self.project_name = project_name
- self.project_version = project_version
- self.sections = collections.defaultdict(dict)
- self.max_key_len = 0
- def add_section(self, section, values, kwargs):
- bool_yn = kwargs.get('bool_yn', False)
- if not isinstance(bool_yn, bool):
- raise InterpreterException('bool_yn keyword argument must be boolean')
- list_sep = kwargs.get('list_sep')
- if list_sep is not None and not isinstance(list_sep, str):
- raise InterpreterException('list_sep keyword argument must be string')
- for k, v in values.items():
- if k in self.sections[section]:
- raise InterpreterException('Summary section {!r} already have key {!r}'.format(section, k))
- formatted_values = []
- for i in listify(v):
- if not isinstance(i, (str, int)):
- m = 'Summary value in section {!r}, key {!r}, must be string, integer or boolean'
- raise InterpreterException(m.format(section, k))
- if bool_yn and isinstance(i, bool):
- formatted_values.append(mlog.green('YES') if i else mlog.red('NO'))
- else:
- formatted_values.append(i)
- self.sections[section][k] = (formatted_values, list_sep)
- self.max_key_len = max(self.max_key_len, len(k))
- def dump(self):
- mlog.log(self.project_name, mlog.normal_cyan(self.project_version))
- for section, values in self.sections.items():
- mlog.log('') # newline
- if section:
- mlog.log(' ', mlog.bold(section))
- for k, v in values.items():
- v, list_sep = v
- indent = self.max_key_len - len(k) + 3
- end = ' ' if v else ''
- mlog.log(' ' * indent, k + ':', end=end)
- if list_sep is None:
- indent = self.max_key_len + 6
- list_sep = '\n' + ' ' * indent
- mlog.log(*v, sep=list_sep)
- mlog.log('') # newline
- 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_dependency': self.override_dependency_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,
- 'get_external_property': self.get_external_property_method,
- 'backend': self.backend_method,
- })
- def _find_source_script(self, prog: T.Union[str, ExecutableHolder], args):
- if isinstance(prog, ExecutableHolder):
- prog_path = self.interpreter.backend.get_target_filename(prog.held_object)
- return build.RunScript([prog_path], args)
- elif isinstance(prog, ExternalProgramHolder):
- return build.RunScript(prog.get_command(), args)
- # Prefer scripts in the current source directory
- search_dir = os.path.join(self.interpreter.environment.source_dir,
- self.interpreter.subdir)
- key = (prog, search_dir)
- if key in self._found_source_scripts:
- found = self._found_source_scripts[key]
- else:
- found = dependencies.ExternalProgram(prog, 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(prog))
- return build.RunScript(found.get_command(), args)
- def _process_script_args(
- self, name: str, args: T.List[T.Union[
- str, mesonlib.File, CustomTargetHolder,
- CustomTargetIndexHolder, ConfigureFileHolder,
- ExternalProgramHolder, ExecutableHolder,
- ]], allow_built: bool = False) -> T.List[str]:
- script_args = [] # T.List[str]
- new = False
- for a in args:
- a = unholder(a)
- if isinstance(a, str):
- script_args.append(a)
- elif isinstance(a, mesonlib.File):
- new = True
- script_args.append(a.rel_to_builddir(self.interpreter.environment.source_dir))
- elif isinstance(a, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)):
- if not allow_built:
- raise InterpreterException('Arguments to {} cannot be built'.format(name))
- new = True
- script_args.extend([os.path.join(a.get_subdir(), o) for o in a.get_outputs()])
- # This feels really hacky, but I'm not sure how else to fix
- # this without completely rewriting install script handling.
- # This is complicated by the fact that the install target
- # depends on all.
- if isinstance(a, build.CustomTargetIndex):
- a.target.build_by_default = True
- else:
- a.build_by_default = True
- elif isinstance(a, build.ConfigureFile):
- new = True
- script_args.append(os.path.join(a.subdir, a.targetname))
- elif isinstance(a, dependencies.ExternalProgram):
- script_args.extend(a.command)
- new = True
- else:
- raise InterpreterException(
- 'Arguments to {} must be strings, Files, CustomTargets, '
- 'Indexes of CustomTargets, or ConfigureFiles'.format(name))
- if new:
- FeatureNew('Calling "{}" with File, CustomTaget, Index of CustomTarget, ConfigureFile, Executable, or ExternalProgram'.format(name), '0.55.0').use(
- self.interpreter.subproject)
- return script_args
- @permittedKwargs(set())
- def add_install_script_method(self, args: 'T.Tuple[T.Union[str, ExecutableHolder], T.Union[str, mesonlib.File, CustomTargetHolder, CustomTargetIndexHolder, ConfigureFileHolder], ...]', kwargs):
- if len(args) < 1:
- raise InterpreterException('add_install_script takes one or more arguments')
- script_args = self._process_script_args('add_install_script', args[1:], allow_built=True)
- script = self._find_source_script(args[0], script_args)
- self.build.install_scripts.append(script)
- @permittedKwargs(set())
- def add_postconf_script_method(self, args, kwargs):
- if len(args) < 1:
- raise InterpreterException('add_postconf_script takes one or more arguments')
- script_args = self._process_script_args('add_postconf_script', args[1:], allow_built=True)
- script = self._find_source_script(args[0], script_args)
- self.build.postconf_scripts.append(script)
- @permittedKwargs(set())
- 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)
- if self.interpreter.subproject != '':
- raise InterpreterException('add_dist_script may not be used in a subproject.')
- script_args = self._process_script_args('add_dist_script', args[1:], allow_built=True)
- script = self._find_source_script(args[0], script_args)
- 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]
- for_machine = Interpreter.machine_from_native_kwarg(kwargs)
- clist = self.interpreter.coredata.compilers[for_machine]
- 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, exe = args
- 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 = OverrideProgram(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)
- @FeatureNew('meson.override_dependency', '0.54.0')
- @permittedKwargs({'native'})
- def override_dependency_method(self, args, kwargs):
- if len(args) != 2:
- raise InterpreterException('Override needs two arguments')
- name = args[0]
- dep = args[1]
- if not isinstance(name, str) or not name:
- raise InterpreterException('First argument must be a string and cannot be empty')
- if hasattr(dep, 'held_object'):
- dep = dep.held_object
- if not isinstance(dep, dependencies.Dependency):
- raise InterpreterException('Second argument must be a dependency object')
- identifier = dependencies.get_dep_identifier(name, kwargs)
- for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
- override = self.build.dependency_overrides[for_machine].get(identifier)
- if override:
- m = 'Tried to override dependency {!r} which has already been resolved or overridden at {}'
- location = mlog.get_error_location_string(override.node.filename, override.node.lineno)
- raise InterpreterException(m.format(name, location))
- self.build.dependency_overrides[for_machine][identifier] = \
- build.DependencyOverride(dep, self.interpreter.current_node)
- @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) -> str:
- 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)
- @noArgsFlattening
- @permittedKwargs({'native'})
- @FeatureNew('meson.get_external_property', '0.54.0')
- def get_external_property_method(self, args: T.Sequence[str], kwargs: dict) -> str:
- if len(args) < 1 or len(args) > 2:
- raise InterpreterException('Must have one or two positional arguments.')
- propname = args[0]
- if not isinstance(propname, str):
- raise InterpreterException('Property name must be string.')
- def _get_native() -> str:
- try:
- props = self.interpreter.environment.properties.build
- return props[propname]
- except Exception:
- if len(args) == 2:
- return args[1]
- raise InterpreterException('Unknown native property: %s.' % propname)
- if 'native' in kwargs:
- if kwargs['native']:
- return _get_native()
- else:
- return self.get_cross_property_method(args, {})
- else: # native: not specified
- if self.build.environment.is_cross_build():
- return self.get_cross_property_method(args, kwargs)
- else:
- return _get_native()
- 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'}
- )
- _base_test_args = {'args', 'depends', 'env', 'should_fail', 'timeout', 'workdir', 'suite', 'priority', 'protocol'}
- permitted_kwargs = {'add_global_arguments': {'language', 'native'},
- 'add_global_link_arguments': {'language', 'native'},
- 'add_languages': {'required', 'native'},
- 'add_project_link_arguments': {'language', 'native'},
- 'add_project_arguments': {'language', 'native'},
- 'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env', 'is_default'},
- 'benchmark': _base_test_args,
- 'build_target': known_build_target_kwargs,
- 'configure_file': {'input',
- 'output',
- 'configuration',
- 'command',
- 'copy',
- 'depfile',
- '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',
- 'embed',
- 'fallback',
- 'language',
- 'main',
- 'method',
- 'modules',
- 'components',
- 'cmake_module_path',
- 'optional_modules',
- 'native',
- 'not_found_message',
- 'required',
- 'static',
- 'version',
- 'private_headers',
- 'cmake_args',
- 'include_type',
- },
- 'declare_dependency': {'include_directories',
- 'link_with',
- 'sources',
- 'dependencies',
- 'compile_args',
- 'link_args',
- 'link_whole',
- 'version',
- 'variables',
- },
- 'executable': build.known_exe_kwargs,
- 'find_program': {'required', 'native', 'version', 'dirs'},
- 'generator': {'arguments',
- 'output',
- 'depends',
- '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': set.union(_base_test_args, {'is_parallel'}),
- '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, ast=None):
- super().__init__(build.environment.get_source_dir(), subdir, subproject)
- 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.summary = {}
- 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 and ast is None:
- self.load_root_meson_file()
- self.sanity_check_ast()
- elif ast is not None:
- self.ast = ast
- 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()
- self._redetect_machines()
- def _redetect_machines(self):
- # Re-initialize machine descriptions. We can do a better job now because we
- # have the compilers needed to gain more knowledge, so wipe out old
- # inference and start over.
- machines = self.build.environment.machines.miss_defaulting()
- machines.build = environment.detect_machine_info(self.coredata.compilers.build)
- self.build.environment.machines = 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 opts in env.coredata.get_all_options():
- cur_opt_value = opts.get(def_opt_name)
- if cur_opt_value is not None:
- 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,
- 'alias_target': self.func_alias_target,
- '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_disabler': self.func_is_disabler,
- '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,
- 'summary': self.func_summary,
- '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, dict):
- return {k: self.holderify(v) for k, v in item.items()}
- if isinstance(item, build.CustomTarget):
- return CustomTargetHolder(item, self)
- elif isinstance(item, (int, str, bool, Disabler)) 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.Dependency):
- return DependencyHolder(item, self.subproject)
- elif isinstance(item, dependencies.ExternalProgram):
- return ExternalProgramHolder(item, self.subproject)
- 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, self.subproject)
- 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, Disabler)):
- 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 add_build_def_file(self, f):
- # Use relative path for files within source directory, and absolute path
- # for system files. Skip files within build directory. Also skip not regular
- # files (e.g. /dev/stdout) Normalize the path to avoid duplicates, this
- # is especially important to convert '/' to '\' on Windows.
- if isinstance(f, mesonlib.File):
- if f.is_built:
- return
- f = os.path.normpath(f.relative_name())
- elif os.path.isfile(f) and not f.startswith('/dev'):
- srcdir = Path(self.environment.get_source_dir())
- builddir = Path(self.environment.get_build_dir())
- f = Path(f).resolve()
- if builddir in f.parents:
- return
- if srcdir in f.parents:
- f = f.relative_to(srcdir)
- f = str(f)
- else:
- return
- if f not in self.build_def_files:
- self.build_def_files.append(f)
- def get_variables(self):
- return self.variables
- def check_stdlibs(self):
- for for_machine in MachineChoice:
- props = self.build.environment.properties[for_machine]
- for l in self.coredata.compilers[for_machine].keys():
- 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, 'meson', {})
- self.build.stdlibs.host[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'])
- @FeatureNewKwargs('declare_dependency', '0.54.0', ['variables'])
- @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 = unholder(extract_as_list(kwargs, 'link_with'))
- libs_whole = unholder(extract_as_list(kwargs, 'link_whole'))
- sources = extract_as_list(kwargs, 'sources')
- sources = unholder(listify(self.source_strings_to_files(sources)))
- deps = unholder(extract_as_list(kwargs, 'dependencies'))
- compile_args = mesonlib.stringlistify(kwargs.get('compile_args', []))
- link_args = mesonlib.stringlistify(kwargs.get('link_args', []))
- variables = kwargs.get('variables', {})
- if not isinstance(variables, dict):
- raise InterpreterException('variables must be a dict.')
- if not all(isinstance(v, str) for v in variables.values()):
- # Because that is how they will come from pkg-config and cmake
- raise InterpreterException('variables values be strings.')
- 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,
- variables)
- return DependencyHolder(dep, self.subproject)
- @noKwargs
- def func_assert(self, node, args, kwargs):
- if len(args) == 1:
- FeatureNew('assert function without message argument', '0.53.0').use(self.subproject)
- value = args[0]
- message = None
- elif len(args) == 2:
- value, message = args
- if not isinstance(message, str):
- raise InterpreterException('Assert message not a string.')
- else:
- raise InterpreterException('Assert takes between one and two arguments')
- if not isinstance(value, bool):
- raise InterpreterException('Assert value not bool.')
- if not value:
- if message is None:
- from .ast import AstPrinter
- printer = AstPrinter()
- node.args.arguments[0].accept(printer)
- message = printer.result
- 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 actual, wanted in zip(args, arg_types):
- 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, *cargs = args
- 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}'
- expanded_args = []
- 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()))
- if not cmd.found():
- raise InterpreterException('command {!r} not found or not executable'.format(cmd.get_name()))
- elif isinstance(cmd, CompilerHolder):
- exelist = cmd.compiler.get_exelist()
- cmd = exelist[0]
- prog = ExternalProgram(cmd, silent=True)
- if not prog.found():
- raise InterpreterException('Program {!r} not found '
- 'or not executable'.format(cmd))
- cmd = prog
- expanded_args = exelist[1:]
- 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
- 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))
- # If any file that was used as an argument to the command
- # changes, we must re-run the configuration step.
- self.add_build_def_file(cmd.get_path())
- for a in expanded_args:
- if not os.path.isabs(a):
- a = os.path.join(builddir if in_builddir else srcdir, self.subdir, a)
- self.add_build_def_file(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, 'meson', kwargs)
- def disabled_subproject(self, dirname, disabled_feature=None, exception=None):
- sub = SubprojectHolder(None, self.subproject_dir, dirname,
- disabled_feature=disabled_feature, exception=exception)
- self.subprojects[dirname] = sub
- return sub
- def do_subproject(self, dirname: str, method: str, 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, disabled_feature=feature)
- 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, method)
- 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, exception=e)
- raise e
- subdir = os.path.join(self.subproject_dir, resolved)
- subdir_abs = os.path.join(subproject_dir_abs, 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), 'method', mlog.bold(method), '\n')
- try:
- if method == 'meson':
- return self._do_subproject_meson(dirname, subdir, default_options, kwargs)
- elif method == 'cmake':
- return self._do_subproject_cmake(dirname, subdir, subdir_abs, default_options, kwargs)
- else:
- raise InterpreterException('The method {} is invalid for the subproject {}'.format(method, dirname))
- # 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=mlog.yellow('Exception:'))
- mlog.log('\nSubproject', mlog.bold(dirname), 'is buildable:', mlog.red('NO'), '(disabling)')
- return self.disabled_subproject(dirname, exception=e)
- raise e
- def _do_subproject_meson(self, dirname, subdir, default_options, kwargs, ast=None, build_def_files=None):
- with mlog.nested():
- new_build = self.build.copy()
- subi = Interpreter(new_build, self.backend, dirname, subdir, self.subproject_dir,
- self.modules, default_options, ast=ast)
- subi.subprojects = self.subprojects
- subi.subproject_stack = self.subproject_stack + [dirname]
- current_active = self.active_projectname
- current_warnings_counter = mlog.log_warnings_counter
- mlog.log_warnings_counter = 0
- subi.run()
- subi_warnings = mlog.log_warnings_counter
- mlog.log_warnings_counter = current_warnings_counter
- mlog.log('Subproject', mlog.bold(dirname), 'finished.')
- 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,
- warnings=subi_warnings)
- # Duplicates are possible when subproject uses files from project root
- if build_def_files:
- self.build_def_files = list(set(self.build_def_files + build_def_files))
- else:
- self.build_def_files = list(set(self.build_def_files + subi.build_def_files))
- self.build.merge(subi.build)
- self.build.subprojects[dirname] = subi.project_version
- self.summary.update(subi.summary)
- return self.subprojects[dirname]
- def _do_subproject_cmake(self, dirname, subdir, subdir_abs, default_options, kwargs):
- with mlog.nested():
- new_build = self.build.copy()
- prefix = self.coredata.builtins['prefix'].value
- cmake_options = mesonlib.stringlistify(kwargs.get('cmake_options', []))
- cm_int = CMakeInterpreter(new_build, subdir, subdir_abs, prefix, new_build.environment, self.backend)
- cm_int.initialise(cmake_options)
- cm_int.analyse()
- # Generate a meson ast and execute it with the normal do_subproject_meson
- ast = cm_int.pretend_to_be_meson()
- mlog.log()
- with mlog.nested():
- mlog.log('Processing generated meson AST')
- # Debug print the generated meson file
- from .ast import AstIndentationGenerator, AstPrinter
- printer = AstPrinter()
- ast.accept(AstIndentationGenerator())
- ast.accept(printer)
- printer.post_process()
- meson_filename = os.path.join(self.build.environment.get_build_dir(), subdir, 'meson.build')
- with open(meson_filename, "w") as f:
- f.write(printer.result)
- mlog.log('Build file:', meson_filename)
- mlog.cmd_ci_include(meson_filename)
- mlog.log()
- result = self._do_subproject_meson(dirname, subdir, default_options, kwargs, ast, cm_int.bs_files)
- result.cm_interpreter = cm_int
- mlog.log()
- return result
- def get_option_internal(self, optname):
- raw_optname = optname
- if self.is_subproject():
- optname = self.subproject + ':' + optname
- for opts in [
- self.coredata.base_options, compilers.base_options, self.coredata.builtins,
- dict(self.coredata.get_prefixed_options_per_machine(self.coredata.builtins_per_machine)),
- dict(self.coredata.flatten_lang_iterator(
- self.coredata.get_prefixed_options_per_machine(self.coredata.compiler_options))),
- ]:
- v = opts.get(optname)
- if v is None or v.yielding:
- v = opts.get(raw_optname)
- if v is not None:
- return v
- 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, optname, 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:
- FeatureNew('configuration_data dictionary', '0.49.0').use(self.subproject)
- initial_values = args[0]
- if not isinstance(initial_values, dict):
- raise InterpreterException('configuration_data first argument must be a dictionary')
- else:
- initial_values = {}
- return ConfigurationDataHolder(self.subproject, initial_values)
- 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, self)
- 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, *proj_langs = args
- if ':' in proj_name:
- raise InvalidArguments("Project name {!r} must not contain ':'".format(proj_name))
- if 'meson_version' in kwargs:
- cv = coredata.version
- pv = kwargs['meson_version']
- if not mesonlib.version_compare(cv, pv):
- raise InterpreterException('Meson version is %s but project requires %s' % (cv, pv))
- if os.path.exists(self.option_file):
- oi = optinterpreter.OptionInterpreter(self.subproject)
- oi.process(self.option_file)
- self.coredata.merge_user_options(oi.options)
- self.add_build_def_file(self.option_file)
- # 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)
- self.coredata.init_builtins(self.subproject)
- else:
- default_options = {}
- self.coredata.set_default_options(default_options, self.subproject, self.environment)
- 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:
- mesonlib.project_meson_versions[self.subproject] = kwargs['meson_version']
- 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, MachineChoice.BUILD)
- self.add_languages(proj_langs, True, MachineChoice.HOST)
- self.set_backend()
- if not self.is_subproject():
- self.check_stdlibs()
- @FeatureNewKwargs('add_languages', '0.54.0', ['native'])
- @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
- if 'native' in kwargs:
- return self.add_languages(args, required, self.machine_from_native_kwarg(kwargs))
- else:
- # absent 'native' means 'both' for backwards compatibility
- mlog.warning('add_languages is missing native:, assuming languages are wanted for both host and build.',
- location=self.current_node)
- success = self.add_languages(args, False, MachineChoice.BUILD)
- success &= self.add_languages(args, required, MachineChoice.HOST)
- return success
- def get_message_string_arg(self, arg):
- 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
- @noArgsFlattening
- @noKwargs
- def func_message(self, node, args, kwargs):
- if len(args) > 1:
- FeatureNew('message with more than one argument', '0.54.0').use(self.subproject)
- args_str = [self.get_message_string_arg(i) for i in args]
- self.message_impl(args_str)
- def message_impl(self, args):
- mlog.log(mlog.bold('Message:'), *args)
- @noArgsFlattening
- @FeatureNewKwargs('summary', '0.54.0', ['list_sep'])
- @permittedKwargs({'section', 'bool_yn', 'list_sep'})
- @FeatureNew('summary', '0.53.0')
- def func_summary(self, node, args, kwargs):
- if len(args) == 1:
- if not isinstance(args[0], dict):
- raise InterpreterException('Summary first argument must be dictionary.')
- values = args[0]
- elif len(args) == 2:
- if not isinstance(args[0], str):
- raise InterpreterException('Summary first argument must be string.')
- values = {args[0]: args[1]}
- else:
- raise InterpreterException('Summary accepts at most 2 arguments.')
- section = kwargs.get('section', '')
- if not isinstance(section, str):
- raise InterpreterException('Summary\'s section keyword argument must be string.')
- self.summary_impl(section, values, kwargs)
- def summary_impl(self, section, values, kwargs):
- if self.subproject not in self.summary:
- self.summary[self.subproject] = Summary(self.active_projectname, self.project_version)
- self.summary[self.subproject].add_section(section, values, kwargs)
- def _print_summary(self):
- # Add automatic 'Supbrojects' section in main project.
- all_subprojects = collections.OrderedDict()
- for name, subp in sorted(self.subprojects.items()):
- value = subp.found()
- if subp.disabled_feature:
- value = [value, 'Feature {!r} disabled'.format(subp.disabled_feature)]
- elif subp.exception:
- value = [value, str(subp.exception)]
- elif subp.warnings > 0:
- value = [value, '{} warnings'.format(subp.warnings)]
- all_subprojects[name] = value
- if all_subprojects:
- self.summary_impl('Subprojects', all_subprojects,
- {'bool_yn': True,
- 'list_sep': ' ',
- })
- # Print all summaries, main project last.
- mlog.log('') # newline
- main_summary = self.summary.pop('', None)
- for _, summary in sorted(self.summary.items()):
- summary.dump()
- if main_summary:
- main_summary.dump()
- @noArgsFlattening
- @FeatureNew('warning', '0.44.0')
- @noKwargs
- def func_warning(self, node, args, kwargs):
- if len(args) > 1:
- FeatureNew('warning with more than one argument', '0.54.0').use(self.subproject)
- args_str = [self.get_message_string_arg(i) for i in args]
- mlog.warning(*args_str, 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: T.Sequence[str], required: bool, for_machine: MachineChoice) -> bool:
- success = self.add_languages_for(args, required, for_machine)
- if not self.coredata.is_cross_build():
- self.coredata.copy_build_options_from_regular_ones()
- self._redetect_machines()
- return success
- def should_skip_sanity_check(self, for_machine: MachineChoice) -> bool:
- if for_machine != MachineChoice.HOST:
- return False
- if not self.environment.is_cross_build():
- return False
- should = self.environment.properties.host.get('skip_sanity_check', False)
- if not isinstance(should, bool):
- raise InterpreterException('Option skip_sanity_check must be a boolean.')
- return should
- def add_languages_for(self, args, required, for_machine: MachineChoice):
- success = True
- for lang in sorted(args, key=compilers.sort_clink):
- lang = lang.lower()
- clist = self.coredata.compilers[for_machine]
- machine_name = for_machine.get_lower_case_name()
- if lang in clist:
- comp = clist[lang]
- else:
- try:
- comp = self.environment.detect_compiler_for(lang, for_machine)
- if comp is None:
- raise InvalidArguments('Tried to use unknown language "%s".' % lang)
- if self.should_skip_sanity_check(for_machine):
- mlog.log_once('Cross compiler sanity tests disabled via the cross file.')
- else:
- comp.sanity_check(self.environment.get_scratch_dir(), self.environment)
- except Exception:
- if not required:
- mlog.log('Compiler for language',
- mlog.bold(lang), 'for the', machine_name,
- 'machine not found.')
- success = False
- continue
- else:
- raise
- if for_machine == MachineChoice.HOST or self.environment.is_cross_build():
- logger_fun = mlog.log
- else:
- logger_fun = mlog.debug
- logger_fun(comp.get_display_language(), 'compiler for the', machine_name, 'machine:',
- mlog.bold(' '.join(comp.get_exelist())), comp.get_version_string())
- if comp.linker is not None:
- logger_fun(comp.get_display_language(), 'linker for the', machine_name, 'machine:',
- mlog.bold(' '.join(comp.linker.get_exelist())), comp.linker.id, comp.linker.version)
- self.build.ensure_static_linker(comp)
- langs = self.coredata.compilers[for_machine].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.')
- return success
- def program_from_file_for(self, for_machine, prognames, silent):
- for p in unholder(prognames):
- 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(self.environment, for_machine, p)
- if prog.found():
- return ExternalProgramHolder(prog, self.subproject)
- return None
- def program_from_system(self, args, search_dirs, 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
- extra_search_dirs = []
- elif isinstance(exename, str):
- search_dir = source_dir
- extra_search_dirs = search_dirs
- else:
- raise InvalidArguments('find_program only accepts strings and '
- 'files, not {!r}'.format(exename))
- extprog = dependencies.ExternalProgram(exename, search_dir=search_dir,
- extra_search_dirs=extra_search_dirs,
- silent=silent)
- progobj = ExternalProgramHolder(extprog, self.subproject)
- 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, self.subproject, self.backend)
- 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
- # TODO update modules to always pass `for_machine`. It is bad-form to assume
- # the host machine.
- def find_program_impl(self, args, for_machine: MachineChoice = MachineChoice.HOST,
- required=True, silent=True, wanted='', search_dirs=None):
- if not isinstance(args, list):
- args = [args]
- progobj = self.program_from_overrides(args, silent=silent)
- if progobj is None:
- progobj = self.program_from_file_for(for_machine, args, silent=silent)
- if progobj is None:
- progobj = self.program_from_system(args, search_dirs, silent=silent)
- if progobj is None and args[0].endswith('python3'):
- prog = dependencies.ExternalProgram('python3', mesonlib.python_command, silent=True)
- progobj = ExternalProgramHolder(prog, self.subproject)
- 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(' '.join(args)), self.subproject)
- # Only store successful lookups
- self.store_name_lookups(args)
- if wanted:
- version = progobj.get_version(self)
- is_found, not_found, found = mesonlib.version_compare_many(version, wanted)
- if not is_found:
- mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.red('NO'),
- 'found {!r} but need:'.format(version),
- ', '.join(["'{}'".format(e) for e in not_found]))
- if required:
- m = 'Invalid version of program, need {!r} {!r} found {!r}.'
- raise InvalidArguments(m.format(progobj.get_name(), not_found, version))
- return ExternalProgramHolder(dependencies.NonExistingExternalProgram(' '.join(args)), self.subproject)
- return progobj
- @FeatureNewKwargs('find_program', '0.53.0', ['dirs'])
- @FeatureNewKwargs('find_program', '0.52.0', ['version'])
- @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(' '.join(args)), self.subproject)
- search_dirs = extract_search_dirs(kwargs)
- wanted = mesonlib.stringlistify(kwargs.get('version', []))
- for_machine = self.machine_from_native_kwarg(kwargs)
- return self.find_program_impl(args, for_machine, required=required,
- silent=False, wanted=wanted,
- search_dirs=search_dirs)
- 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, display_name, kwargs):
- # Check if we want this as a build-time / build machine or runt-time /
- # host machine dep.
- for_machine = self.machine_from_native_kwarg(kwargs)
- identifier = dependencies.get_dep_identifier(name, kwargs)
- wanted_vers = mesonlib.stringlistify(kwargs.get('version', []))
- override = self.build.dependency_overrides[for_machine].get(identifier)
- if override:
- info = [mlog.blue('(overridden)' if override.explicit else '(cached)')]
- cached_dep = override.dep
- # We don't implicitly override not-found dependencies, but user could
- # have explicitly called meson.override_dependency() with a not-found
- # dep.
- if not cached_dep.found():
- mlog.log('Dependency', mlog.bold(display_name),
- 'found:', mlog.red('NO'), *info)
- return identifier, cached_dep
- found_vers = cached_dep.get_version()
- if not self.check_version(wanted_vers, found_vers):
- mlog.log('Dependency', mlog.bold(name),
- 'found:', mlog.red('NO'),
- 'found', mlog.normal_cyan(found_vers), 'but need:',
- mlog.bold(', '.join(["'{}'".format(e) for e in wanted_vers])),
- *info)
- return identifier, NotFoundDependency(self.environment)
- else:
- info = [mlog.blue('(cached)')]
- cached_dep = self.coredata.deps[for_machine].get(identifier)
- if cached_dep:
- found_vers = cached_dep.get_version()
- if not self.check_version(wanted_vers, found_vers):
- return identifier, None
- if cached_dep:
- if found_vers:
- info = [mlog.normal_cyan(found_vers), *info]
- mlog.log('Dependency', mlog.bold(display_name),
- 'found:', mlog.green('YES'), *info)
- return identifier, cached_dep
- return identifier, None
- @staticmethod
- def check_version(wanted, found):
- if not wanted:
- 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 verify_fallback_consistency(self, dirname, varname, cached_dep):
- subi = self.subprojects.get(dirname)
- if not cached_dep or not varname or not subi or not cached_dep.found():
- return
- dep = subi.get_variable_method([varname], {})
- if dep.held_object != cached_dep:
- m = 'Inconsistency: Subproject has overridden the dependency with another variable than {!r}'
- raise DependencyException(m.format(varname))
- def get_subproject_dep(self, name, display_name, dirname, varname, kwargs):
- required = kwargs.get('required', True)
- wanted = mesonlib.stringlistify(kwargs.get('version', []))
- subproj_path = os.path.join(self.subproject_dir, dirname)
- dep = self.notfound_dependency()
- try:
- subproject = self.subprojects[dirname]
- _, cached_dep = self._find_cached_dep(name, display_name, kwargs)
- if varname is None:
- # Assuming the subproject overridden the dependency we want
- if cached_dep:
- if required and not cached_dep.found():
- m = 'Dependency {!r} is not satisfied'
- raise DependencyException(m.format(display_name))
- return DependencyHolder(cached_dep, self.subproject)
- else:
- m = 'Subproject {} did not override dependency {}'
- raise DependencyException(m.format(subproj_path, display_name))
- if subproject.found():
- self.verify_fallback_consistency(dirname, varname, cached_dep)
- 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))
- 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_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('Dependency', mlog.bold(display_name), 'from subproject',
- mlog.bold(subproj_path), 'found:', mlog.red('NO'),
- 'found', mlog.normal_cyan(found), 'but need:',
- mlog.bold(', '.join(["'{}'".format(e) for e in wanted])))
- return self.notfound_dependency()
- found = mlog.normal_cyan(found) if found else None
- mlog.log('Dependency', mlog.bold(display_name), 'from subproject',
- mlog.bold(subproj_path), 'found:', mlog.green('YES'), found)
- 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.54.0', ['components'])
- @FeatureNewKwargs('dependency', '0.52.0', ['include_type'])
- @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)'
- mods = extract_as_list(kwargs, 'modules')
- if mods:
- display_name += ' (modules: {})'.format(', '.join(str(i) for i in mods))
- 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])
- self.message_impl([not_found_message])
- # Override this dependency to have consistent results in subsequent
- # dependency lookups.
- if name and d.found():
- for_machine = self.machine_from_native_kwarg(kwargs)
- identifier = dependencies.get_dep_identifier(name, kwargs)
- if identifier not in self.build.dependency_overrides[for_machine]:
- self.build.dependency_overrides[for_machine][identifier] = \
- build.DependencyOverride(d.held_object, node, explicit=False)
- 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, display_name, kwargs)
- if cached_dep:
- if has_fallback:
- dirname, varname = self.get_subproject_infos(kwargs)
- self.verify_fallback_consistency(dirname, varname, 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, display_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():
- for_machine = self.machine_from_native_kwarg(kwargs)
- self.coredata.deps[for_machine].put(identifier, dep)
- return DependencyHolder(dep, self.subproject)
- if has_fallback:
- return self.dependency_fallback(name, 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 = mesonlib.stringlistify(kwargs['fallback'])
- if len(fbinfo) == 1:
- FeatureNew('Fallback without variable name', '0.53.0').use(self.subproject)
- return fbinfo[0], None
- elif len(fbinfo) != 2:
- raise InterpreterException('Fallback info must have one or two items.')
- return fbinfo
- def dependency_fallback(self, name, display_name, kwargs):
- required = kwargs.get('required', True)
- 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.')
- if required:
- m = 'Dependency {!r} not found and fallback is disabled'
- raise DependencyException(m.format(display_name))
- 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': required,
- }
- self.do_subproject(dirname, 'meson', sp_kwargs)
- return self.get_subproject_dep(name, 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 == 'shared_module':
- FeatureNew('build_target(target_type: \'shared_module\')',
- '0.51.0').use(self.subproject)
- return self.build_target(node, args, kwargs, SharedModuleHolder)
- 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, backend=self.backend), 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 = unholder(extract_as_list(kwargs, 'depends'))
- else:
- raise InterpreterException('Run_target needs at least one positional argument.')
- cleaned_args = []
- for i in unholder(listify(all_args)):
- 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, *cmd_args = cleaned_args
- tg = RunTargetHolder(build.RunTarget(name, command, cmd_args, cleaned_deps, self.subdir, self.subproject), self)
- self.add_target(name, tg.held_object)
- full_name = (self.subproject, name)
- assert(full_name not in self.build.run_target_names)
- self.build.run_target_names.add(full_name)
- return tg
- @FeatureNew('alias_target', '0.52.0')
- @noKwargs
- def func_alias_target(self, node, args, kwargs):
- if len(args) < 2:
- raise InvalidCode('alias_target takes at least 2 arguments.')
- name = args[0]
- if not isinstance(name, str):
- raise InterpreterException('First argument must be a string.')
- deps = unholder(listify(args[1:]))
- for d in deps:
- if not isinstance(d, (build.BuildTarget, build.CustomTarget)):
- raise InterpreterException('Depends items must be build targets.')
- tg = RunTargetHolder(build.AliasTarget(name, deps, self.subdir, self.subproject), self)
- 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
- @FeatureNewKwargs('benchmark', '0.46.0', ['depends'])
- @FeatureNewKwargs('benchmark', '0.52.0', ['priority'])
- @permittedKwargs(permitted_kwargs['benchmark'])
- def func_benchmark(self, node, args, kwargs):
- # is_parallel isn't valid here, so make sure it isn't passed
- if 'is_parallel' in kwargs:
- del kwargs['is_parallel']
- self.add_test(node, args, kwargs, False)
- @FeatureNewKwargs('test', '0.46.0', ['depends'])
- @FeatureNewKwargs('test', '0.52.0', ['priority'])
- @permittedKwargs(permitted_kwargs['test'])
- def func_test(self, node, args, kwargs):
- if kwargs.get('protocol') == 'gtest':
- FeatureNew('"gtest" protocol for tests', '0.55.0').use(self.subproject)
- self.add_test(node, args, kwargs, True)
- def unpack_env_kwarg(self, kwargs) -> build.EnvironmentVariables:
- envlist = kwargs.get('env', EnvironmentVariablesHolder())
- if isinstance(envlist, EnvironmentVariablesHolder):
- env = envlist.held_object
- elif isinstance(envlist, dict):
- FeatureNew('environment dictionary', '0.52.0').use(self.subproject)
- env = EnvironmentVariablesHolder(envlist)
- env = env.held_object
- else:
- envlist = listify(envlist)
- # Convert from array to environment object
- env = EnvironmentVariablesHolder(envlist)
- env = env.held_object
- return env
- def add_test(self, node, args, kwargs, is_base_test):
- if len(args) != 2:
- raise InterpreterException('test expects 2 arguments, {} given'.format(len(args)))
- 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 = unholder(extract_as_list(kwargs, 'args'))
- 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.')
- protocol = kwargs.get('protocol', 'exitcode')
- if protocol not in {'exitcode', 'tap', 'gtest'}:
- raise InterpreterException('Protocol must be "exitcode", "tap", or "gtest".')
- 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 = unholder(extract_as_list(kwargs, 'depends'))
- for dep in depends:
- if not isinstance(dep, (build.CustomTarget, build.BuildTarget)):
- raise InterpreterException('Depends items must be build targets.')
- priority = kwargs.get('priority', 0)
- if not isinstance(priority, int):
- raise InterpreterException('Keyword argument priority must be an integer.')
- t = Test(args[0], prj, suite, exe.held_object, depends, par, cmd_args,
- env, should_fail, timeout, workdir, protocol, priority)
- 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, absname).parse()
- except mesonlib.MesonException as me:
- me.file = absname
- 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)
- elif isinstance(s, str):
- source_strings.append(s)
- else:
- raise InvalidArguments('Argument {!r} must be string or file.'.format(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'])
- @FeatureNewKwargs('configure_file', '0.52.0', ['depfile'])
- @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".')
- if 'depfile' in kwargs:
- depfile = kwargs['depfile']
- if not isinstance(depfile, str):
- raise InterpreterException('depfile file name must be a string')
- else:
- depfile = None
- # Validate input
- inputs = self.source_strings_to_files(extract_as_list(kwargs, 'input'))
- inputs_abs = []
- for f in inputs:
- if isinstance(f, mesonlib.File):
- inputs_abs.append(f.absolute_path(self.environment.source_dir,
- self.environment.build_dir))
- self.add_build_def_file(f)
- else:
- raise InterpreterException('Inputs can only be strings or file objects')
- # Validate output
- output = kwargs['output']
- if not isinstance(output, str):
- raise InterpreterException('Output file name must be a string')
- if inputs_abs:
- values = mesonlib.get_filenames_templates_dict(inputs_abs, None)
- outputs = mesonlib.substitute_values([output], values)
- output = outputs[0]
- if depfile:
- depfile = mesonlib.substitute_values([depfile], values)[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):
- FeatureNew('configure_file.configuration dictionary', '0.49.0').use(self.subproject)
- conf = ConfigurationDataHolder(self.subproject, conf)
- elif not isinstance(conf, ConfigurationDataHolder):
- raise InterpreterException('Argument "configuration" is not of type configuration_data')
- mlog.log('Configuring', mlog.bold(output), 'using configuration')
- if len(inputs) > 1:
- raise InterpreterException('At most one input file can given in configuration mode')
- if inputs:
- 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(inputs_abs[0], 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, inputs[0]), location=node)
- if confdata_useless:
- ifbase = os.path.basename(inputs_abs[0])
- 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:
- if len(inputs) > 1:
- FeatureNew('multiple inputs in configure_file()', '0.52.0').use(self.subproject)
- # 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.
- values = mesonlib.get_filenames_templates_dict(inputs_abs, [ofile_abs])
- if depfile:
- depfile = os.path.join(self.environment.get_scratch_dir(), depfile)
- values['@DEPFILE@'] = depfile
- # 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 inputs_abs:
- shutil.copymode(inputs_abs[0], dst_tmp)
- mesonlib.replace_if_different(ofile_abs, dst_tmp)
- if depfile:
- mlog.log('Reading depfile:', mlog.bold(depfile))
- with open(depfile, 'r') as f:
- df = DepFile(f.readlines())
- deps = df.get_all_dependencies(ofile_fname)
- for dep in deps:
- self.add_build_def_file(dep)
- elif 'copy' in kwargs:
- if len(inputs_abs) != 1:
- raise InterpreterException('Exactly one input file must be given in copy mode')
- os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True)
- shutil.copyfile(inputs_abs[0], ofile_abs)
- shutil.copystat(inputs_abs[0], ofile_abs)
- else:
- # Not reachable
- raise AssertionError
- # 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):
- if isinstance(idir, list) and len(idir) == 0:
- mlog.deprecation('install_dir: kwarg must be a string and not an empty array. '
- 'Please use the install: kwarg to enable or disable installation. '
- 'This will be a hard error in the next release.')
- else:
- 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 = unholder(extract_as_list(kwargs, 'include_directories'))
- 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 = unholder(extract_as_list(kwargs, 'exe_wrapper'))
- 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, gdb, timeout_multiplier, env)
- @permittedKwargs(permitted_kwargs['add_global_arguments'])
- @stringArgs
- def func_add_global_arguments(self, node, args, kwargs):
- for_machine = self.machine_from_native_kwarg(kwargs)
- self.add_global_arguments(node, self.build.global_args[for_machine], args, kwargs)
- @permittedKwargs(permitted_kwargs['add_global_link_arguments'])
- @stringArgs
- def func_add_global_link_arguments(self, node, args, kwargs):
- for_machine = self.machine_from_native_kwarg(kwargs)
- self.add_global_arguments(node, self.build.global_link_args[for_machine], args, kwargs)
- @permittedKwargs(permitted_kwargs['add_project_arguments'])
- @stringArgs
- def func_add_project_arguments(self, node, args, kwargs):
- for_machine = self.machine_from_native_kwarg(kwargs)
- self.add_project_arguments(node, self.build.projects_args[for_machine], args, kwargs)
- @permittedKwargs(permitted_kwargs['add_project_link_arguments'])
- @stringArgs
- def func_add_project_link_arguments(self, node, args, kwargs):
- for_machine = self.machine_from_native_kwarg(kwargs)
- self.add_project_arguments(node, self.build.projects_link_args[for_machine], args, kwargs)
- def warn_about_builtin_args(self, args):
- warnargs = ('/W1', '/W2', '/W3', '/W4', '/Wall', '-Wall', '-Wextra', '-Wpedantic')
- optargs = ('-O0', '-O2', '-O3', '-Os', '/O1', '/O2', '/Os')
- for arg in args:
- if arg in warnargs:
- mlog.warning('Consider using the built-in warning_level option instead of using "{}".'.format(arg),
- location=self.current_node)
- elif arg in optargs:
- mlog.warning('Consider using the built-in optimization level instead of using "{}".'.format(arg),
- location=self.current_node)
- elif arg == '-g':
- mlog.warning('Consider using the built-in debug option instead of using "{}".'.format(arg),
- location=self.current_node)
- elif arg == '-pipe':
- mlog.warning("You don't need to add -pipe, Meson will use it automatically when it is available.",
- location=self.current_node)
- elif arg.startswith('-fsanitize'):
- mlog.warning('Consider using the built-in option for sanitizers instead of using "{}".'.format(arg),
- location=self.current_node)
- elif arg.startswith('-std=') or arg.startswith('/std:'):
- mlog.warning('Consider using the built-in option for language standard version instead of using "{}".'.format(arg),
- location=self.current_node)
- 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))
- self.warn_about_builtin_args(args)
- for lang in mesonlib.stringlistify(kwargs['language']):
- lang = lang.lower()
- argsdict[lang] = argsdict.get(lang, []) + args
- @noKwargs
- @noArgsFlattening
- def func_environment(self, node, args, kwargs):
- if len(args) > 1:
- raise InterpreterException('environment takes only one optional positional arguments')
- elif len(args) == 1:
- FeatureNew('environment positional arguments', '0.52.0').use(self.subproject)
- initial_values = args[0]
- if not isinstance(initial_values, dict) and not isinstance(initial_values, list):
- raise InterpreterException('environment first argument must be a dictionary or a list')
- else:
- initial_values = {}
- return EnvironmentVariablesHolder(initial_values)
- @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()
- if self.subproject == '':
- self._print_summary()
- def print_extra_warnings(self):
- # TODO cross compilation
- for c in self.coredata.compilers.host.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,
- GeneratedObjectsHolder)):
- 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', self.subproject)
- 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, *sources = args
- for_machine = self.machine_from_native_kwarg(kwargs)
- 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 == ExecutableHolder:
- targetclass = build.Executable
- elif targetholder == SharedLibraryHolder:
- targetclass = build.SharedLibrary
- elif targetholder == SharedModuleHolder:
- targetclass = build.SharedModule
- elif targetholder == StaticLibraryHolder:
- targetclass = build.StaticLibrary
- elif targetholder == 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, for_machine, sources, objs, self.environment, kwargs)
- target.project_version = self.project_version
- if not self.environment.machines.matches_build_machine(for_machine):
- 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:
- # TODO other platforms
- for lang, c in self.coredata.compilers.host.items():
- if c.can_compile(i):
- result[lang] = True
- break
- return result
- def add_cross_stdlib_info(self, target):
- if target.for_machine != MachineChoice.HOST:
- return
- 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.stdlibs.host[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)
- # Only permit object extraction from the same subproject
- def validate_extraction(self, buildtarget: InterpreterObject) -> None:
- 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 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, value = args
- 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 isinstance(varname, Disabler):
- return varname
- 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
- @staticmethod
- def machine_from_native_kwarg(kwargs: T.Dict[str, T.Any]) -> MachineChoice:
- native = kwargs.get('native', False)
- if not isinstance(native, bool):
- raise InvalidArguments('Argument to "native" must be a boolean.')
- return MachineChoice.BUILD if native else MachineChoice.HOST
- @FeatureNew('is_disabler', '0.52.0')
- @noKwargs
- def func_is_disabler(self, node, args, kwargs):
- if len(args) != 1:
- raise InvalidCode('Is_disabler takes one argument.')
- varname = args[0]
- return isinstance(varname, Disabler)
|