SConscript 123 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342
  1. # scons file. Only meant to be run from SContruct.
  2. #
  3. # This file is Copyright 2010 by the GPSD project
  4. # SPDX-License-Identifier: BSD-2-clause
  5. #
  6. # This code runs compatibly under Python 2 and 3.x for x >= 2.
  7. # Preserve this property!
  8. from __future__ import print_function
  9. import ast
  10. import atexit # for atexit.register()
  11. import functools
  12. import glob
  13. import operator
  14. import os
  15. import pickle
  16. import re
  17. # replacement for functions from the commands module, which is deprecated.
  18. import subprocess
  19. import sys
  20. import time
  21. try:
  22. from setuptools import sysconfig
  23. except:
  24. from distutils import sysconfig
  25. import SCons
  26. # scons does not like targets that come and go (if cleaning, if python,
  27. # etc). All targets are needed for proper cleaning. If a target should
  28. # not be built (if not python), then do not include the target in the
  29. # next layer sources.
  30. # scons gets confused by targets that are not a real file (shmclean, etc.).
  31. # Set them Pseudo (like in a Makefile) and use an Alias() for them.
  32. # Facilitate debugging with pdb.
  33. # At pdb startup, the environment is such that setting breakpoints in
  34. # SConstruct requires specifying its full absolute path, which is incovenient.
  35. # Stopping here at an automatic breakpoint makes this easier. Note that this
  36. # code has no effect unless pdb is loaded.
  37. # To use this, run with pdb and continue from the initial pdb prompt.
  38. pdb_module = sys.modules.get('pdb')
  39. if pdb_module:
  40. pdb_module.set_trace()
  41. pass # Breakpoint default file is now SConscript
  42. # gpsd needs Scons version at least 2.3
  43. EnsureSConsVersion(2, 3, 0)
  44. # gpsd needs Python version at least 2.6
  45. EnsurePythonVersion(2, 6)
  46. # By user choice, or due to system-dependent availability, the scons
  47. # executable may be called using names other than plain "scons",
  48. # e.g. "scons-3" on CentOS 8.
  49. scons_executable_name = os.path.basename(sys.argv[0]) or 'scons'
  50. # Have scons rebuild an existing target when the source(s) MD5 changes
  51. # Do not use time to prevent rebuilding when sources, like gpsd_config.h,
  52. # are rebuilt, but with no changes.
  53. Decider('MD5')
  54. # Put .sconsign*dblite and .scons-options-cache in variantdir for
  55. # one-touch cleaning
  56. # support building with various Python versions.
  57. sconsign_file = '.sconsign.{}.dblite'.format(pickle.HIGHEST_PROTOCOL)
  58. SConsignFile(os.getcwd() + os.path.sep + sconsign_file)
  59. # Start by reading configuration variables from the cache
  60. opts = Variables('.scons-option-cache')
  61. # ugly hack from http://www.catb.org/esr/faqs/practical-python-porting/
  62. # handle python2/3 strings
  63. def polystr(o):
  64. if bytes is str: # Python 2
  65. return str(o)
  66. # python 3.
  67. if isinstance(o, str):
  68. return o
  69. if isinstance(o, (bytes, bytearray)):
  70. return str(o, encoding='latin1')
  71. if isinstance(o, int):
  72. return str(o)
  73. raise ValueError
  74. def strtobool (val):
  75. val = val.lower()
  76. if val in ('y', 'yes', 't', 'true', 'on', '1'):
  77. return True
  78. elif val in ('n', 'no', 'f', 'false', 'off', '0'):
  79. return False
  80. else:
  81. raise ValueError("invalid truth value %r" % (val,))
  82. # Helper functions for revision hackery
  83. def GetMtime(file):
  84. """Get mtime of given file, or 0."""
  85. try:
  86. return os.stat(file).st_mtime
  87. except OSError:
  88. return 0
  89. def FileList(patterns, exclusion=None):
  90. """Get list of files based on patterns, minus excluded path."""
  91. files = functools.reduce(operator.add, map(glob.glob, patterns), [])
  92. for file in files:
  93. if file.find(exclusion):
  94. files.remove(file)
  95. return files
  96. # FIXME: replace with TryAction()
  97. def _getstatusoutput(cmd, nput=None, shell=True, cwd=None, env=None):
  98. pipe = subprocess.Popen(cmd, shell=shell, cwd=cwd, env=env,
  99. stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
  100. (output, errout) = pipe.communicate(input=nput)
  101. status = pipe.returncode
  102. return (status, output)
  103. # Workaround for old SCons bug that couldn't overwrite existing symlinks
  104. # This was fixed in 2.3.2, but we allow 2.3.0 (e.g., on Ubuntu 14)
  105. #
  106. # In the troublesome versions, we monkey-patch os.symlink to bully it through
  107. standard_os_symlink = os.symlink
  108. def _forced_symlink(source, link_name):
  109. try:
  110. standard_os_symlink(source, link_name)
  111. except OSError:
  112. # Out of paranoia, only do this when the target is a symlink
  113. if os.path.islink(link_name):
  114. os.remove(link_name)
  115. standard_os_symlink(source, link_name)
  116. else:
  117. raise
  118. if SCons.__version__ in ['2.3.0', '2.3.1']:
  119. os.symlink = _forced_symlink
  120. # SCons 2.3.0 is also missing the Psuedo method. See the workaround after
  121. # the initial 'env' setup.
  122. # All man pages. Always build them all.
  123. all_manpages = {
  124. "man/cgps.1": "man/cgps.adoc",
  125. "man/gegps.1": "man/gegps.adoc",
  126. "man/gps.1": "man/gps.adoc",
  127. "man/gps2udp.1": "man/gps2udp.adoc",
  128. "man/gpscsv.1": "man/gpscsv.adoc",
  129. "man/gpscat.1": "man/gpscat.adoc",
  130. "man/gpsctl.1": "man/gpsctl.adoc",
  131. "man/gpsd.8": "man/gpsd.adoc",
  132. "man/gpsdebuginfo.1": "man/gpsdebuginfo.adoc",
  133. "man/gpsdctl.8": "man/gpsdctl.adoc",
  134. "man/gpsdecode.1": "man/gpsdecode.adoc",
  135. "man/gpsd_json.5": "man/gpsd_json.adoc",
  136. "man/gpsfake.1": "man/gpsfake.adoc",
  137. "man/gpsinit.8": "man/gpsinit.adoc",
  138. "man/gpsmon.1": "man/gpsmon.adoc",
  139. "man/gpspipe.1": "man/gpspipe.adoc",
  140. "man/gpsplot.1": "man/gpsplot.adoc",
  141. "man/gpsprof.1": "man/gpsprof.adoc",
  142. "man/gpsrinex.1": "man/gpsrinex.adoc",
  143. "man/gpssnmp.1": "man/gpssnmp.adoc",
  144. "man/gpssubframe.1": "man/gpssubframe.adoc",
  145. "man/gpxlogger.1": "man/gpxlogger.adoc",
  146. "man/lcdgps.1": "man/lcdgps.adoc",
  147. "man/libgps.3": "man/libgps.adoc",
  148. "man/libgpsmm.3": "man/libgpsmm.adoc",
  149. "man/libQgpsmm.3": "man/libgpsmm.adoc",
  150. "man/ntpshmmon.1": "man/ntpshmmon.adoc",
  151. "man/ppscheck.8": "man/ppscheck.adoc",
  152. "man/ubxtool.1": "man/ubxtool.adoc",
  153. "man/xgps.1": "man/xgps.adoc",
  154. "man/xgpsspeed.1": "man/xgpsspeed.adoc",
  155. "man/zerk.1": "man/zerk.adoc",
  156. }
  157. # doc files to install in share/gpsd/doc
  158. doc_files = [
  159. 'AUTHORS',
  160. 'build.adoc',
  161. 'COPYING',
  162. 'www/example1.c.txt',
  163. 'NEWS',
  164. 'README.adoc',
  165. 'SUPPORT.adoc',
  166. ]
  167. # doc files to install in share/gpsd/doc
  168. icon_files = [
  169. 'packaging/X11/gpsd-logo.png',
  170. ]
  171. # gpsd_version, and variantdir, from SConstruct
  172. Import('*')
  173. # Create a fixed-name symlink to the build tree, for scripts and symlinks.
  174. # FIXME: Make this work with Execute()
  175. vdir_parent = os.path.dirname(os.path.abspath(os.path.dirname(variantdir)))
  176. try:
  177. os.symlink(variantdir, os.path.join(vdir_parent, 'buildtmp'))
  178. except OSError:
  179. pass # May already exist
  180. # API (JSON) version
  181. api_version_major = 3
  182. api_version_minor = 14
  183. # client library version
  184. libgps_version_current = 29
  185. libgps_version_revision = 0
  186. libgps_version_age = 0
  187. libgps_version = "%d.%d.%d" % (libgps_version_current, libgps_version_age,
  188. libgps_version_revision)
  189. #
  190. # Release identification ends here
  191. # Hosting information (mainly used for templating web pages) begins here
  192. # Each variable foo has a corresponding @FOO@ expanded in .in files.
  193. # There are no project-dependent URLs or references to the hosting site
  194. # anywhere else in the distribution; preserve this property!
  195. annmail = "gpsd-announce@nongnu.org"
  196. bugtracker = "https://gitlab.com/gpsd/gpsd/-/issues"
  197. cgiupload = "root@thyrsus.com:/var/www/cgi-bin/"
  198. clonerepo = "git@gitlab.com:gpsd/gpsd.git"
  199. devmail = "gpsd-dev@lists.nongnu.org"
  200. download = "http://download-mirror.savannah.gnu.org/releases/gpsd/"
  201. formserver = "www@thyrsus.com"
  202. gitrepo = "git@gitlab.com:gpsd/gpsd.git"
  203. ircchan = "irc://chat.freenode.net/#gpsd"
  204. mailman = "https://lists.nongnu.org/mailman/listinfo/"
  205. mainpage = "https://gpsd.io"
  206. projectpage = "https://gitlab.com/gpsd/gpsd"
  207. scpupload = "garyemiller@dl.sv.nongnu.org:/releases/gpsd/"
  208. sitename = "GPSD"
  209. sitesearch = "gpsd.io"
  210. tiplink = "<a href='https://www.patreon.com/esr'>" \
  211. "leave a remittance at Patreon</a>"
  212. tipwidget = '<p><a href="https://www.patreon.com/esr">' \
  213. 'Donate here to support continuing development.</a></p>'
  214. usermail = "gpsd-users@lists.nongnu.org"
  215. webform = "http://www.thyrsus.com/cgi-bin/gps_report.cgi"
  216. website = "https://gpsd.io/"
  217. # Hosting information ends here
  218. PYTHON_SYSCONFIG_IMPORT = 'from distutils import sysconfig'
  219. # Utility productions
  220. def Utility(target, source, action, **kwargs):
  221. targ = env.Command(target=target, source=source, action=action, **kwargs)
  222. # why always build? wasteful?
  223. # when gpsdecode is the source this rebuilds the entire daemon!
  224. # env.AlwaysBuild(targ)
  225. # Why precious?
  226. env.Precious(targ)
  227. # Is pseudo really needed (didn't used to be)?
  228. env.Pseudo(targ)
  229. # Alias to make name work without variantdir prefix
  230. env.Alias(target, targ)
  231. return targ
  232. def UtilityWithHerald(herald, target, source, action, **kwargs):
  233. if not env.GetOption('silent'):
  234. action = ['@echo "%s"' % herald] + action
  235. return Utility(target=target, source=source, action=action, **kwargs)
  236. # Spawn replacement that suppresses non-error stderr
  237. def filtered_spawn(sh, escape, cmd, args, env):
  238. proc = subprocess.Popen([sh, '-c', ' '.join(args)],
  239. env=env, close_fds=True, stderr=subprocess.PIPE)
  240. _, stderr = proc.communicate()
  241. if proc.returncode:
  242. sys.stderr.write(stderr)
  243. return proc.returncode
  244. #
  245. # Build-control options
  246. #
  247. # guess systemd defaults
  248. systemd_dir = '/lib/systemd/system'
  249. systemd = os.path.exists(systemd_dir)
  250. # Set distribution-specific defaults here
  251. imloads = True
  252. boolopts = (
  253. # GPS protocols
  254. # for back compatibility, deprecated Feb 2021
  255. ("ashtech", True, "alias for NMEA0183 support, deprecated"),
  256. ("earthmate", True, "DeLorme EarthMate Zodiac support"),
  257. ("evermore", True, "EverMore binary support"),
  258. ("fury", True, "Jackson Labs Fury and Firefly support"),
  259. ("fv18", True, "San Jose Navigation FV-18 support"),
  260. ("garmin", True, "Garmin kernel driver support"),
  261. ("garmintxt", True, "Garmin Simple Text support"),
  262. ("geostar", True, "Geostar Protocol support"),
  263. ("greis", True, "Javad GREIS support"),
  264. ("itrax", True, "iTrax hardware support"),
  265. ("navcom", True, "Navcom NCT support"),
  266. ("nmea2000", True, "NMEA2000/CAN support"),
  267. ("oncore", True, "Motorola OnCore chipset support"),
  268. ("sirf", True, "SiRF chipset support"),
  269. ("skytraq", True, "Skytraq chipset support"),
  270. ("superstar2", True, "Novatel SuperStarII chipset support"),
  271. ("tnt", True, "True North Technologies support"),
  272. ("tripmate", True, "DeLorme TripMate support"),
  273. ("tsip", True, "Trimble TSIP support"),
  274. ("ublox", True, "u-blox Protocol support"),
  275. # Non-GPS protocols
  276. ("aivdm", True, "AIVDM support"),
  277. ("gpsclock", True, "Furuno GPSClock support"),
  278. ("isync", True, "Spectratime iSync LNRClok/GRCLOK support"),
  279. ("oceanserver", True, "OceanServer support"),
  280. ("rtcm104v2", True, "rtcm104v2 support"),
  281. ("rtcm104v3", True, "rtcm104v3 support"),
  282. # Time service
  283. ("oscillator", True, "Disciplined oscillator support"),
  284. # Export methods
  285. ("dbus_export", True, "enable DBUS export support"),
  286. ("shm_export", True, "export via shared memory"),
  287. ("socket_export", True, "data export over sockets"),
  288. # Communication
  289. ("bluez", True, "BlueZ support for Bluetooth devices"),
  290. ('usb', True, "libusb support for USB devices"),
  291. # Other daemon options
  292. ("control_socket", True, "control socket for hotplug notifications"),
  293. ("systemd", systemd, "systemd socket activation"),
  294. # Client-side options
  295. ("clientdebug", True, "client debugging support"),
  296. ("libgpsmm", True, "build C++ bindings"),
  297. ("ncurses", True, "build with ncurses"),
  298. ("qt", True, "build Qt bindings"),
  299. # Daemon options
  300. ("squelch", False, "squelch gpsd_log/gpsd_hexdump to save cpu"),
  301. # Build control
  302. ("coveraging", False, "build with code coveraging enabled"),
  303. ("debug", False, "add debug information to build, unoptimized"),
  304. ("debug_opt", False, "add debug information to build, optimized"),
  305. ("gpsdclients", True, "gspd client programs"),
  306. ("gpsd", True, "gpsd itself"),
  307. ("implicit_link", imloads, "implicit linkage is supported in shared libs"),
  308. # FIXME: should check for Pi, not for "linux"
  309. ("magic_hat", sys.platform.startswith('linux'),
  310. "special Linux PPS hack for Raspberry Pi et al"),
  311. ("minimal", False, "turn off every option not set on the command line"),
  312. ("nostrip", False, "don't symbol-strip binaries at link time"),
  313. ("profiling", False, "build with profiling enabled"),
  314. ("python", True, "build Python support and modules."),
  315. ("shared", True, "build shared libraries, not static"),
  316. ("timeservice", False, "time-service configuration"),
  317. ("xgps", True, "include xgps and xgpsspeed."),
  318. # Test control
  319. ("slow", False, "run tests with realistic (slow) delays"),
  320. )
  321. # now step on the boolopts just read from '.scons-option-cache'
  322. # Otherwise if no cache, then no boolopts.
  323. for (name, default, helpd) in boolopts:
  324. opts.Add(BoolVariable(name, helpd, default))
  325. # See PEP 394 for why 'python' is the preferred name for Python.
  326. # override with "target_python=XX" on scons command line if want different
  327. # Later there are tests for OS specifics.
  328. def_target_python = "python"
  329. def_python_shebang = "/usr/bin/env %s" % def_target_python
  330. # Gentoo, Fedora, openSUSE systems use uucp for ttyS* and ttyUSB*
  331. if os.path.exists("/etc/gentoo-release"):
  332. def_group = "uucp"
  333. else:
  334. def_group = "dialout"
  335. # darwin and BSDs do not have /run, maybe others.
  336. if os.path.exists("/run"):
  337. rundir = "/run"
  338. else:
  339. rundir = "/var/run"
  340. nonboolopts = (
  341. ("gpsd_group", def_group, "privilege revocation group"),
  342. ("gpsd_user", "nobody", "privilege revocation user",),
  343. ("manbuild", "auto",
  344. "build help in man and HTML formats. No/Auto/Yes."),
  345. ("max_clients", '64', "maximum allowed clients"),
  346. ("max_devices", '12', "maximum allowed devices"),
  347. ("prefix", "/usr/local", "installation directory prefix"),
  348. ("python_coverage", "coverage run", "coverage command for Python progs"),
  349. ("python_libdir", "", "Python module directory prefix"),
  350. ("python_shebang", def_python_shebang, "Python shebang"),
  351. ("qt_versioned", "", "version for versioned Qt"),
  352. ("release", "", "Suffix for gpsd version"),
  353. ("rundir", rundir,
  354. "Directory for run-time variable data"),
  355. ("sysroot", "",
  356. "Logical root directory for headers and libraries.\n"
  357. "For cross-compiling, or building with multiple local toolchains.\n"
  358. "See gcc and ld man pages for more details."),
  359. ("target", "",
  360. "Prefix to the binary tools to use (gcc, ld, etc.)\n"
  361. "For cross-compiling, or building with multiple local toolchains.\n"
  362. ),
  363. # If build and target platform are different, then redefining target
  364. # platform might be necessary to use better build flags
  365. ("target_platform", sys.platform,
  366. "target platform for cross-compiling (linux, darwin, etc.)"),
  367. ("target_python", def_target_python, "target Python version as command"),
  368. )
  369. # now step on the non boolopts just read from '.scons-option-cache'
  370. # why?
  371. for (name, default, helpd) in nonboolopts:
  372. opts.Add(name, helpd, default)
  373. pathopts = (
  374. ("bindir", "bin", "application binaries directory"),
  375. ("docdir", "share/gpsd/doc", "documents directory"),
  376. ("icondir", "share/gpsd/icons", "icon directory"),
  377. ("includedir", "include", "header file directory"),
  378. ("libdir", "lib", "system libraries"),
  379. ("mandir", "share/man", "manual pages directory"),
  380. ("pkgconfig", "$libdir/pkgconfig", "pkgconfig file directory"),
  381. ("sbindir", "sbin", "system binaries directory"),
  382. ("sharedir", "share/gpsd", "share directory"),
  383. ("sysconfdir", "etc", "system configuration directory"),
  384. ("udevdir", "/lib/udev", "udev rules directory"),
  385. ("unitdir", systemd_dir, "Directory for systemd unit files"),
  386. )
  387. # now step on the path options just read from '.scons-option-cache'
  388. for (name, default, helpd) in pathopts:
  389. opts.Add(PathVariable(name, helpd, default, PathVariable.PathAccept))
  390. #
  391. # Environment creation
  392. #
  393. import_env = (
  394. # Variables used by programs invoked during the build
  395. "DISPLAY", # Required for dia to run under scons
  396. "GROUPS", # Required by gpg
  397. "HOME", # Required by gpg
  398. "LANG", # To avoid Gtk warnings with Python >=3.7
  399. "LOGNAME", # LOGNAME is required for the flocktest production.
  400. 'PATH', # Required for ccache and Coverity scan-build
  401. 'CCACHE_DIR', # Required for ccache
  402. 'CCACHE_RECACHE', # Required for ccache (probably there are more)
  403. # pkg-config (required for crossbuilds at least, and probably pkgsrc)
  404. 'PKG_CONFIG_LIBDIR',
  405. 'PKG_CONFIG_PATH',
  406. 'PKG_CONFIG_SYSROOT_DIR',
  407. # Variables for specific packaging/build systems
  408. "MACOSX_DEPLOYMENT_TARGET", # MacOSX 10.4 (and probably earlier)
  409. 'STAGING_DIR', # OpenWRT and CeroWrt
  410. 'STAGING_PREFIX', # OpenWRT and CeroWrt
  411. 'CWRAPPERS_CONFIG_DIR', # pkgsrc
  412. # Variables used in testing
  413. 'WRITE_PAD', # So we can test WRITE_PAD values on the fly.
  414. )
  415. envs = {}
  416. for var in import_env:
  417. if var in os.environ:
  418. envs[var] = os.environ[var]
  419. envs["GPSD_HOME"] = os.getcwd() + os.sep + 'gpsd'
  420. env = Environment(tools=["default", "tar", "textfile"], options=opts, ENV=envs)
  421. # Release identification begins here.
  422. #
  423. # Actual releases follow the normal X.Y or X.Y.Z scheme. The version
  424. # number in git between releases has the form X.Y~dev, when it is
  425. # expected that X.Y will be the next actual release. As an example,
  426. # when 3.20 is the last release, and 3.20.1 is the expected next
  427. # release, the version in git will be 3.20.1~dev. Note that ~ is used,
  428. # because there is some precedent, ~ is an allowed version number in
  429. # the Debian version rules, and it does not cause confusion with
  430. # whether - separates components of the package name, separates the
  431. # name from the version, or separates version components.
  432. if 'dev' in gpsd_version:
  433. (st, gpsd_revision) = _getstatusoutput('git describe --tags')
  434. if st != 0:
  435. # If git describe failed
  436. # Try to use current commit hash
  437. (st, gpsd_commit) = _getstatusoutput('git rev-parse HEAD')
  438. if st == 0 and gpsd_commit:
  439. # Format output similar to normal revision
  440. gpsd_revision = '%s-g%s' % (gpsd_version, polystr(gpsd_commit[:9]))
  441. else:
  442. # Only if git describe and git rev-parse failed
  443. # Use timestamp from latest relevant file,
  444. # ignoring generated files (../$variantdir)
  445. # from root, not from $variantdir
  446. files = FileList(['../*.c', '../*/*.c', '../*.cpp', '../*/*.cpp',
  447. '../include/*.h', '../*.in', '../*/*.in',
  448. '../SConstruct', '../SConscript'],
  449. '../%s' % variantdir)
  450. timestamps = map(GetMtime, files)
  451. if timestamps:
  452. from datetime import datetime
  453. latest = datetime.fromtimestamp(sorted(timestamps)[-1])
  454. gpsd_revision = '%s-%s' % (gpsd_version, latest.isoformat())
  455. else:
  456. gpsd_revision = gpsd_version # Paranoia
  457. else:
  458. gpsd_revision = gpsd_version
  459. gpsd_revision = polystr(gpsd_revision.strip())
  460. # Distros like to add a suffix to the version. Fedora, and others,
  461. # call it the "rele4ase". It often looks like: r1
  462. if env['release']:
  463. gpsd_revision += "-" + polystr(env['release'])
  464. # SCons 2.3.0 lacks the Pseudo method. If it's missing here, make it a
  465. # dummy and hope for the best.
  466. try:
  467. env.Pseudo
  468. except AttributeError:
  469. env.Pseudo = lambda x: None
  470. # Minimal build turns off every option not set on the command line,
  471. if ARGUMENTS.get('minimal'):
  472. for (name, default, helpd) in boolopts:
  473. # Ensure gpsd and gpsdclients are always enabled unless explicitly
  474. # turned off.
  475. if ((default is True and
  476. not ARGUMENTS.get(name) and
  477. name not in ("gpsd", "gpsdclients"))):
  478. env[name] = False
  479. # Time-service build = stripped-down with some diagnostic tools
  480. if ARGUMENTS.get('timeservice'):
  481. timerelated = ("gpsd",
  482. "ipv6",
  483. "magic_hat",
  484. "ncurses",
  485. "oscillator",
  486. "socket_export",
  487. "ublox", # For the Uputronics board
  488. )
  489. for (name, default, helpd) in boolopts:
  490. if ((default is True and
  491. not ARGUMENTS.get(name) and
  492. name not in timerelated)):
  493. env[name] = False
  494. # iSync uses ublox underneath, so we force to enable it
  495. if env['isync']:
  496. env['ublox'] = True
  497. opts.Save('.scons-option-cache', env)
  498. for (name, default, helpd) in pathopts:
  499. env[name] = env.subst(env[name])
  500. env['VERSION'] = gpsd_version
  501. env['SC_PYTHON'] = sys.executable # Path to SCons Python
  502. # Set defaults from environment. Note that scons doesn't cope well
  503. # with multi-word CPPFLAGS/LDFLAGS/SHLINKFLAGS values; you'll have to
  504. # explicitly quote them or (better yet) use the "=" form of GNU option
  505. # settings.
  506. # Scons also uses different internal names than most other build-systems.
  507. # So we rely on MergeFlags/ParseFlags to do the right thing for us.
  508. env['STRIP'] = "strip"
  509. env['PKG_CONFIG'] = "pkg-config"
  510. for i in ["AR", # linker for static libs, usually "ar"
  511. "CC",
  512. "CXX",
  513. # "LD", # scons does not use LD, usually "ld"
  514. "PKG_CONFIG",
  515. "SHLINK", # linker for shared libs, usually "gcc" or "g++", NOT "ld"
  516. "STRIP",
  517. "TAR"]:
  518. if i in os.environ:
  519. env[i] = os.getenv(i)
  520. for i in ["ARFLAGS",
  521. "CCFLAGS",
  522. "CFLAGS",
  523. "CPPFLAGS",
  524. "CXXFLAGS",
  525. "LDFLAGS",
  526. "LINKFLAGS",
  527. "SHLINKFLAGS",
  528. ]:
  529. if i in os.environ:
  530. env.MergeFlags(Split(os.getenv(i)))
  531. # Keep scan-build options in the environment
  532. for key, value in os.environ.items():
  533. if key.startswith('CCC_'):
  534. env.Append(ENV={key: value})
  535. # Placeholder so we can kluge together something like VPATH builds.
  536. # $SRCDIR replaces occurrences for $(srcdir) in the autotools build.
  537. # scons can get confused if this is not a full path
  538. # FIXME: could get variantdir from SRCDIR
  539. env['SRCDIR'] = os.getcwd()
  540. # We may need to force slow regression tests to get around race
  541. # conditions in the pty layer, especially on a loaded machine.
  542. if env["slow"]:
  543. env['REGRESSOPTS'] = "-S"
  544. else:
  545. env['REGRESSOPTS'] = ""
  546. if env.GetOption("silent"):
  547. env['REGRESSOPTS'] += " -Q"
  548. def announce(msg, end=False):
  549. if not env.GetOption("silent"):
  550. print(msg)
  551. if end:
  552. # duplicate message at exit
  553. atexit.register(lambda: print(msg))
  554. announce("scons version: %s" % SCons.__version__)
  555. announce("scons is running under Python version: %s" %
  556. ".".join(map(str, sys.version_info)))
  557. announce("gpsd version: %s" % polystr(gpsd_revision))
  558. # DESTDIR environment variable means user prefix the installation root.
  559. DESTDIR = os.environ.get('DESTDIR', '')
  560. def installdir(idir, add_destdir=True):
  561. # use os.path.join to handle absolute paths properly.
  562. wrapped = os.path.join(env['prefix'], env[idir])
  563. if add_destdir:
  564. wrapped = os.path.normpath(DESTDIR + os.path.sep + wrapped)
  565. wrapped.replace("/usr/etc", "/etc")
  566. wrapped.replace("/usr/lib/systemd", "/lib/systemd")
  567. return wrapped
  568. # Honor the specified installation prefix in link paths.
  569. if env["sysroot"]:
  570. env.Prepend(LIBPATH=[env["sysroot"] + installdir('libdir',
  571. add_destdir=False)])
  572. # Don't change CCFLAGS if already set by environment.
  573. if 'CCFLAGS' in os.environ:
  574. announce('Warning: CCFLAGS from environment overriding scons settings')
  575. else:
  576. # Should we build with profiling?
  577. if env['profiling']:
  578. env.Append(CCFLAGS=['-pg'])
  579. env.Append(LDFLAGS=['-pg'])
  580. # Should we build with coveraging?
  581. if env['coveraging']:
  582. env.Append(CFLAGS=['-coverage'])
  583. env.Append(LDFLAGS=['-coverage'])
  584. env.Append(LINKFLAGS=['-coverage'])
  585. # Should we build with debug symbols?
  586. if env['debug'] or env['debug_opt']:
  587. env.Append(CCFLAGS=['-g3'])
  588. # Should we build with optimisation?
  589. if env['debug'] or env['coveraging']:
  590. env.Append(CCFLAGS=['-O0'])
  591. else:
  592. env.Append(CCFLAGS=['-O2'])
  593. # Cross-development
  594. devenv = (("ADDR2LINE", "addr2line"),
  595. ("AR", "ar"),
  596. ("AS", "as"),
  597. ("CC", "gcc"),
  598. ("CPP", "cpp"),
  599. ("CXX", "c++"),
  600. ("CXXFILT", "c++filt"),
  601. ("GCCBUG", "gccbug"),
  602. ("GCOV", "gcov"),
  603. ("GPROF", "gprof"),
  604. ("GXX", "g++"),
  605. # ("LD", "ld"), # scons does not use LD
  606. ("NM", "nm"),
  607. ("OBJCOPY", "objcopy"),
  608. ("OBJDUMP", "objdump"),
  609. ("RANLIB", "ranlib"),
  610. ("READELF", "readelf"),
  611. ("SIZE", "size"),
  612. ("STRINGS", "strings"),
  613. ("STRIP", "strip"),
  614. )
  615. if env['target']:
  616. for (name, toolname) in devenv:
  617. env[name] = env['target'] + '-' + toolname
  618. if env['sysroot']:
  619. env.MergeFlags({"CFLAGS": ["--sysroot=%s" % env['sysroot']]})
  620. env.MergeFlags({"LINKFLAGS": ["--sysroot=%s" % env['sysroot']]})
  621. # Build help
  622. def cmp(a, b):
  623. return (a > b) - (a < b)
  624. # FIXME: include __doc__ in help
  625. Help("""Arguments may be a mixture of switches and targets in any order.
  626. Switches apply to the entire build regardless of where they are in the order.
  627. Important switches include:
  628. prefix=/usr probably what packagers want
  629. Options are cached in a file named .scons-option-cache and persist to later
  630. invocations. The file is editable. Delete it to start fresh. Current option
  631. values can be listed with 'scons -h'.
  632. """ + opts.GenerateHelpText(env, sort=cmp))
  633. # Configuration
  634. def CheckPKG(context, name):
  635. context.Message('Checking pkg-config for %s... ' % name)
  636. ret = context.TryAction('%s --exists \'%s\''
  637. % (context.env['PKG_CONFIG'], name))[0]
  638. context.Result(ret)
  639. return ret
  640. def CheckTime_t(context):
  641. context.Message('Checking if sizeof(time_t) is 64 bits... ')
  642. ret = context.TryLink("""
  643. #include <time.h>
  644. int main(int argc, char **argv) {
  645. static int test_array[1 - 2 * ((long int) sizeof(time_t) < 8 )];
  646. test_array[0] = 0;
  647. (void) argc; (void) argv;
  648. return 0;
  649. }
  650. """, '.c')
  651. context.Result(ret)
  652. return ret
  653. def CheckCompilerOption(context, option):
  654. context.Message('Checking if compiler accepts %s... ' % (option,))
  655. old_CFLAGS = context.env['CFLAGS'][:] # Get a *copy* of the old list
  656. context.env.Append(CFLAGS=option)
  657. ret = context.TryLink("""
  658. int main(int argc, char **argv) {
  659. (void) argc; (void) argv;
  660. return 0;
  661. }
  662. """, '.c')
  663. if not ret:
  664. context.env.Replace(CFLAGS=old_CFLAGS)
  665. context.Result(ret)
  666. return ret
  667. # Check if this compiler is C11 or better
  668. def CheckC11(context):
  669. context.Message('Checking if compiler is C11... ')
  670. ret = context.TryLink("""
  671. #if (__STDC_VERSION__ < 201112L)
  672. #error Not C11
  673. #endif
  674. int main(int argc, char **argv) {
  675. (void) argc; (void) argv;
  676. return 0;
  677. }
  678. """, '.c')
  679. context.Result(ret)
  680. return ret
  681. def GetPythonValue(context, name, imp, expr, brief=False):
  682. """Get a value from the target python, not the running one."""
  683. context.Message('Checking Python %s... ' % name)
  684. if context.env['target_python']:
  685. command = (context.env['target_python'] + " $SOURCE > $TARGET")
  686. text = "%s; print(%s)" % (imp, expr)
  687. # TryAction returns (1, outputStr), or (0, '') on fail
  688. (status, value) = context.TryAction(command, text, '.py')
  689. # do not disable python because this failed
  690. # maybe testing for newer python feature
  691. else:
  692. # FIXME: this ignores imp
  693. status = 1
  694. value = str(eval(expr))
  695. if 1 == status:
  696. # we could convert to str(), but caching turns it into bytes anyway
  697. value = value.strip()
  698. if brief is True:
  699. context.did_show_result = 1
  700. print("ok")
  701. context.Result(value)
  702. # return value
  703. return value
  704. def GetLoadPath(context):
  705. context.Message("Getting system load path... ")
  706. cleaning = env.GetOption('clean')
  707. helping = env.GetOption('help')
  708. # Always set up LIBPATH so that cleaning works properly.
  709. # FIXME: use $SRCDIR?
  710. env.Prepend(LIBPATH=[os.path.realpath(os.curdir)])
  711. # from scons 3.0.5, any changes to env after this, until after
  712. # config.Finish(), will be lost. Use config.env until then.
  713. config = Configure(env, custom_tests={
  714. 'CheckC11': CheckC11,
  715. 'CheckCompilerOption': CheckCompilerOption,
  716. 'CheckPKG': CheckPKG,
  717. 'CheckTime_t': CheckTime_t,
  718. 'GetPythonValue': GetPythonValue,
  719. })
  720. # Use print, rather than announce, so we see it in -s mode.
  721. print("This system is: %s" % sys.platform)
  722. libgps_flags = []
  723. rtlibs = []
  724. bluezflags = []
  725. confdefs = []
  726. dbusflags = []
  727. adoc_prog = False
  728. ncurseslibs = []
  729. mathlibs = []
  730. xtlibs = []
  731. tiocmiwait = True # For cleaning, which works on any OS
  732. usbflags = []
  733. have_dia = False
  734. # canplayer is part of can-utils, required for NMEA 2000 tests
  735. have_canplayer = False
  736. have_coverage = False
  737. have_cppcheck = False
  738. have_flake8 = False
  739. have_pycodestyle = False
  740. have_pylint = False
  741. have_scan_build = False
  742. have_tar = False
  743. have_valgrind = False
  744. # skip config part if cleaning or helping.
  745. # per SCons 4.0.1 doc: Section 23.9. Not Configuring When Cleaning Targets
  746. if not cleaning and not helping:
  747. # OS X aliases gcc to clang
  748. if (sys.platform != config.env['target_platform']):
  749. announce("Target system is: %s" % config.env['target_platform'])
  750. announce("cc is %s, version %s" % (env['CC'], env['CCVERSION']))
  751. # clang accepts -pthread, then warns it is unused.
  752. if not config.CheckCC():
  753. announce("ERROR: CC doesn't work")
  754. if ((config.CheckCompilerOption("-pthread") and
  755. not config.env['target_platform'].startswith('darwin'))):
  756. config.env.MergeFlags("-pthread")
  757. confdefs = ["/* gpsd_config.h generated by scons, do not hand-hack. */\n"]
  758. confdefs.append('#ifndef GPSD_CONFIG_H\n')
  759. confdefs.append('#define VERSION "%s"' % gpsd_version)
  760. confdefs.append('#define REVISION "%s"' % gpsd_revision)
  761. confdefs.append('#define GPSD_PROTO_VERSION_MAJOR %u' % api_version_major)
  762. confdefs.append('#define GPSD_PROTO_VERSION_MINOR %u' % api_version_minor)
  763. confdefs.append('#define GPSD_URL "%s"\n' % website)
  764. # TODO: Move these into an if block only on systems with glibc.
  765. # needed for isfinite(), pselect(), etc.
  766. # for strnlen() before glibc 2.10
  767. # glibc 2.10+ needs 200908L (or XOPEN 700+) for strnlen()
  768. # on newer glibc _DEFAULT_SOURCE resets _POSIX_C_SOURCE
  769. # we set it just in case
  770. confdefs.append('#if !defined(_POSIX_C_SOURCE)')
  771. confdefs.append('#define _POSIX_C_SOURCE 200809L')
  772. confdefs.append('#endif\n')
  773. # for daemon(), cfmakeraw(), strsep() and setgroups()
  774. # on glibc 2.19+
  775. # may also be added by pkg_config
  776. # on linux this eventually sets _USE_XOPEN
  777. confdefs.append('#if !defined(_DEFAULT_SOURCE)')
  778. confdefs.append('#define _DEFAULT_SOURCE')
  779. confdefs.append('#endif\n')
  780. # sys/un.h, and more, needs __USE_MISC with glibc and osX
  781. # __USE_MISC is set by _DEFAULT_SOURCE or _BSD_SOURCE
  782. # TODO: Many of these are now specified by POSIX. Check if
  783. # defining _XOPEN_SOURCE is necessary, and limit to systems where
  784. # it is.
  785. # 500 means X/Open 1995
  786. # getsid(), isascii(), nice(), putenv(), strdup(), sys/ipc.h need 500
  787. # 600 means X/Open 2004
  788. # Ubuntu and OpenBSD isfinite() needs 600
  789. # 700 means X/Open 2008
  790. # glibc 2.10+ needs 700+ for strnlen()
  791. # Python.h wants 600 or 700
  792. # removed 2 Jul 2019 to see if anything breaks...
  793. # confdefs.append('#if !defined(_XOPEN_SOURCE)')
  794. # confdefs.append('#define _XOPEN_SOURCE 700')
  795. # confdefs.append('#endif\n')
  796. # Reinstated for FreeBSD (below) 16-Aug-2019
  797. if config.env['target_platform'].startswith('linux'):
  798. # for cfmakeraw(), strsep(), etc. on CentOS 7
  799. # glibc 2.19 and before
  800. # sets __USE_MISC
  801. confdefs.append('#if !defined(_BSD_SOURCE)')
  802. confdefs.append('#define _BSD_SOURCE')
  803. confdefs.append('#endif\n')
  804. # for strnlen() and struct ifreq
  805. # glibc before 2.10, deprecated in 2.10+
  806. confdefs.append('#if !defined(_GNU_SOURCE)')
  807. confdefs.append('#define _GNU_SOURCE 1')
  808. confdefs.append('#endif\n')
  809. elif config.env['target_platform'].startswith('darwin'):
  810. # strlcpy() and SIGWINCH need _DARWIN_C_SOURCE
  811. confdefs.append('#if !defined(_DARWIN_C_SOURCE)')
  812. confdefs.append('#define _DARWIN_C_SOURCE 1\n')
  813. confdefs.append('#endif\n')
  814. # vsnprintf() needs __DARWIN_C_LEVEL >= 200112L
  815. # snprintf() needs __DARWIN_C_LEVEL >= 200112L
  816. # _DARWIN_C_SOURCE forces __DARWIN_C_LEVEL to 900000L
  817. # see <sys/cdefs.h>
  818. # set internal lib versions at link time.
  819. libgps_flags = ["-Wl,-current_version,%s" % libgps_version,
  820. "-Wl,-compatibility_version,%s" % libgps_version,
  821. "-Wl,-install_name,%s/$TARGET.srcpath" %
  822. installdir('libdir', add_destdir=False)]
  823. elif config.env['target_platform'].startswith('freebsd'):
  824. # for isascii(), putenv(), nice(), strptime()
  825. confdefs.append('#if !defined(_XOPEN_SOURCE)')
  826. confdefs.append('#define _XOPEN_SOURCE 700')
  827. confdefs.append('#endif\n')
  828. # required to define u_int in sys/time.h
  829. confdefs.append('#if !defined(_BSD_SOURCE)')
  830. confdefs.append("#define _BSD_SOURCE 1\n")
  831. confdefs.append('#endif\n')
  832. # required to get strlcpy(), and more, from string.h
  833. confdefs.append('#if !defined(__BSD_VISIBLE)')
  834. confdefs.append("#define __BSD_VISIBLE 1\n")
  835. confdefs.append('#endif\n')
  836. elif config.env['target_platform'].startswith('openbsd'):
  837. # required to define u_int in sys/time.h
  838. confdefs.append('#if !defined(_BSD_SOURCE)')
  839. confdefs.append("#define _BSD_SOURCE 1\n")
  840. confdefs.append('#endif\n')
  841. # required to get strlcpy(), and more, from string.h
  842. confdefs.append('#if !defined(__BSD_VISIBLE)')
  843. confdefs.append("#define __BSD_VISIBLE 1\n")
  844. confdefs.append('#endif\n')
  845. elif config.env['target_platform'].startswith('netbsd'):
  846. # required to get strlcpy(), and more, from string.h
  847. confdefs.append('#if !defined(_NETBSD_SOURCE)')
  848. confdefs.append("#define _NETBSD_SOURCE 1\n")
  849. confdefs.append('#endif\n')
  850. elif config.env['target_platform'].startswith('sunos5'):
  851. # tested with gcc-5.5 on slowlaris 10
  852. # required to get isascii(), and more, from ctype.h
  853. confdefs.append('#if !defined(__XPG4_CHAR_CLASS__)')
  854. confdefs.append("#define __XPG4_CHAR_CLASS__ 1\n")
  855. confdefs.append('#endif\n')
  856. confdefs.append('#if !defined(__XPG6)')
  857. confdefs.append('#define _XPG6\n')
  858. confdefs.append('#endif\n')
  859. # for things like strlcat(), strlcpy)
  860. confdefs.append('#if !defined(__EXTENSIONS__)')
  861. confdefs.append('#define __EXTENSIONS__\n')
  862. confdefs.append('#endif\n')
  863. cxx = config.CheckCXX()
  864. if not cxx:
  865. announce("C++ doesn't work, suppressing libgpsmm and Qt build.")
  866. config.env["libgpsmm"] = False
  867. config.env["qt"] = False
  868. # define a helper function for pkg-config - we need to pass
  869. # --static for static linking, too.
  870. #
  871. # Using "--libs-only-L --libs-only-l" instead of "--libs" avoids
  872. # a superfluous "-rpath" option in some FreeBSD cases, and the resulting
  873. # scons crash.
  874. # However, it produces incorrect results for Qt5Network in OSX, so
  875. # it can't be used unconditionally.
  876. def pkg_config(pkg, shared=env['shared'], rpath_hack=False):
  877. libs = '--libs-only-L --libs-only-l' if rpath_hack else '--libs'
  878. if not shared:
  879. libs += ' --static'
  880. return ['!%s --cflags %s %s' % (env['PKG_CONFIG'], libs, pkg)]
  881. # The actual distinction here is whether the platform has ncurses in the
  882. # base system or not. If it does, pkg-config is not likely to tell us
  883. # anything useful. FreeBSD does, Linux doesn't. Most likely other BSDs
  884. # are like FreeBSD.
  885. ncurseslibs = []
  886. if config.env['ncurses']:
  887. if not config.CheckHeader(["curses.h"]):
  888. announce('Turning off ncurses support, curses.h not found.')
  889. config.env['ncurses'] = False
  890. elif config.CheckPKG('ncurses'):
  891. ncurseslibs = pkg_config('ncurses', rpath_hack=True)
  892. if config.CheckPKG('tinfo'):
  893. ncurseslibs += pkg_config('tinfo', rpath_hack=True)
  894. elif config.CheckPKG('ncursesw'):
  895. # One distro in 2022, Void, only ships the ncursesw
  896. # part of ncurses.
  897. ncurseslibs = pkg_config('ncursesw', rpath_hack=True)
  898. if config.CheckPKG('tinfo'):
  899. ncurseslibs += pkg_config('tinfo', rpath_hack=True)
  900. # It's not yet known whether rpath_hack is appropriate for
  901. # ncurses5-config.
  902. elif WhereIs('ncurses5-config'):
  903. ncurseslibs = ['!ncurses5-config --libs --cflags']
  904. elif WhereIs('ncursesw5-config'):
  905. ncurseslibs = ['!ncursesw5-config --libs --cflags']
  906. elif config.env['target_platform'].startswith('freebsd'):
  907. ncurseslibs = ['-lncurses']
  908. elif (config.env['target_platform'].startswith('darwin') or
  909. config.env['target_platform'].startswith('openbsd') or
  910. config.env['target_platform'].startswith('sunos5')):
  911. ncurseslibs = ['-lcurses']
  912. else:
  913. announce('Turning off ncurses support, library not found.')
  914. config.env['ncurses'] = False
  915. if config.env['usb']:
  916. # In FreeBSD except version 7, USB libraries are in the base system
  917. if config.CheckPKG('libusb-1.0'):
  918. confdefs.append("#define HAVE_LIBUSB 1\n")
  919. try:
  920. usbflags = pkg_config('libusb-1.0')
  921. except OSError:
  922. announce("pkg_config is confused about the state "
  923. "of libusb-1.0.")
  924. usbflags = []
  925. elif config.env['target_platform'].startswith('freebsd'):
  926. # FIXME: shold directly test for libusb existence.
  927. confdefs.append("#define HAVE_LIBUSB 1\n")
  928. usbflags = ["-lusb"]
  929. else:
  930. confdefs.append("/* #undef HAVE_LIBUSB */\n")
  931. usbflags = []
  932. else:
  933. confdefs.append("/* #undef HAVE_LIBUSB */\n")
  934. usbflags = []
  935. config.env["usb"] = False
  936. if config.CheckLib('librt'):
  937. confdefs.append("#define HAVE_LIBRT 1\n")
  938. # System library - no special flags
  939. rtlibs = ["-lrt"]
  940. else:
  941. confdefs.append("/* #undef HAVE_LIBRT */\n")
  942. # for slowlaris socket(), bind(), etc.
  943. if config.CheckLib('libnsl'):
  944. confdefs.append("#define HAVE_LIBNSL\n")
  945. # System library - no special flags
  946. rtlibs += ["-lnsl"]
  947. else:
  948. confdefs.append("/* #undef HAVE_LIBNSL */\n")
  949. # for slowlaris socket(), bind(), etc.
  950. if config.CheckLib('libsocket'):
  951. confdefs.append("#define HAVE_LIBSOCKET\n")
  952. # System library - no special flags
  953. rtlibs += ["-lsocket"]
  954. else:
  955. confdefs.append("/* #undef HAVE_LIBNSOCKET */\n")
  956. # The main reason we check for libm explicitly is to set up the config
  957. # environment for CheckFunc for sincos(). But it doesn't hurt to omit
  958. # the '-lm' when it isn't appropriate.
  959. if config.CheckLib('libm'):
  960. mathlibs = ['-lm']
  961. else:
  962. mathlibs = []
  963. # FreeBSD uses -lthr for pthreads
  964. if config.CheckLib('libthr'):
  965. confdefs.append("#define HAVE_LIBTHR 1\n")
  966. # System library - no special flags
  967. rtlibs += ["-lthr"]
  968. else:
  969. confdefs.append("/* #undef HAVE_LIBTHR */\n")
  970. if config.env['dbus_export'] and config.CheckPKG('dbus-1'):
  971. confdefs.append("#define HAVE_DBUS 1\n")
  972. dbusflags = pkg_config("dbus-1")
  973. config.env.MergeFlags(dbusflags)
  974. else:
  975. confdefs.append("/* #undef HAVE_DBUS */\n")
  976. dbusflags = []
  977. if config.env["dbus_export"]:
  978. announce("Turning off dbus-export support, library not found.")
  979. config.env["dbus_export"] = False
  980. if config.env['bluez'] and config.CheckPKG('bluez'):
  981. confdefs.append("#define ENABLE_BLUEZ 1\n")
  982. bluezflags = pkg_config('bluez')
  983. else:
  984. confdefs.append("/* #undef ENABLE_BLUEZ */\n")
  985. bluezflags = []
  986. if config.env["bluez"]:
  987. announce("Turning off Bluetooth support, library not found.")
  988. config.env["bluez"] = False
  989. # in_port_t is not defined on Android
  990. if not config.CheckType("in_port_t", "#include <netinet/in.h>"):
  991. announce("Did not find in_port_t typedef, assuming unsigned short int")
  992. confdefs.append("typedef unsigned short int in_port_t;\n")
  993. # SUN_LEN is not defined on Android
  994. if ((not config.CheckDeclaration("SUN_LEN", "#include <sys/un.h>") and
  995. not config.CheckDeclaration("SUN_LEN", "#include <linux/un.h>"))):
  996. announce("SUN_LEN is not system-defined, using local definition")
  997. confdefs.append("#ifndef SUN_LEN\n")
  998. confdefs.append("#define SUN_LEN(ptr) "
  999. "((size_t) (((struct sockaddr_un *) 0)->sun_path) "
  1000. "+ strlen((ptr)->sun_path))\n")
  1001. confdefs.append("#endif /* SUN_LEN */\n")
  1002. if config.CheckHeader(["linux/can.h"]):
  1003. confdefs.append("#define HAVE_LINUX_CAN_H 1\n")
  1004. announce("You have kernel CANbus available.")
  1005. else:
  1006. confdefs.append("/* #undef HAVE_LINUX_CAN_H */\n")
  1007. announce("You do not have kernel CANbus available.")
  1008. config.env["nmea2000"] = False
  1009. # check for C11 or better, and __STDC__NO_ATOMICS__ is not defined
  1010. # before looking for stdatomic.h
  1011. if ((config.CheckC11() and
  1012. not config.CheckDeclaration("__STDC_NO_ATOMICS__") and
  1013. config.CheckHeader("stdatomic.h"))):
  1014. confdefs.append("#define HAVE_STDATOMIC_H 1\n")
  1015. else:
  1016. confdefs.append("/* #undef HAVE_STDATOMIC_H */\n")
  1017. if config.CheckHeader("libkern/OSAtomic.h"):
  1018. confdefs.append("#define HAVE_OSATOMIC_H 1\n")
  1019. else:
  1020. confdefs.append("/* #undef HAVE_OSATOMIC_H */\n")
  1021. announce("No memory barriers - SHM export and time hinting "
  1022. "may not be reliable.")
  1023. # endian.h is required for rtcm104v2 unless the compiler defines
  1024. # __ORDER_BIG_ENDIAN__, __ORDER_LITTLE_ENDIAN__ and __BYTE_ORDER__
  1025. if ((config.CheckDeclaration("__ORDER_BIG_ENDIAN__") and
  1026. config.CheckDeclaration("__ORDER_LITTLE_ENDIAN__") and
  1027. config.CheckDeclaration("__BYTE_ORDER__"))):
  1028. confdefs.append("#define HAVE_BUILTIN_ENDIANNESS 1\n")
  1029. confdefs.append("/* #undef HAVE_ENDIAN_H */\n")
  1030. confdefs.append("/* #undef HAVE_SYS_ENDIAN_H */\n")
  1031. announce("Your compiler has built-in endianness support.")
  1032. else:
  1033. confdefs.append("/* #undef HAVE_BUILTIN_ENDIANNESS\n */")
  1034. if config.CheckHeader("endian.h"):
  1035. confdefs.append("#define HAVE_ENDIAN_H 1\n")
  1036. confdefs.append("/* #undef HAVE_SYS_ENDIAN_H */\n")
  1037. confdefs.append("/* #undef HAVE_MACHINE_ENDIAN_H */\n")
  1038. elif config.CheckHeader("sys/endian.h"):
  1039. confdefs.append("/* #undef HAVE_ENDIAN_H */\n")
  1040. confdefs.append("#define HAVE_SYS_ENDIAN_H 1\n")
  1041. confdefs.append("/* #undef HAVE_MACHINE_ENDIAN_H */\n")
  1042. elif config.CheckHeader("machine/endian.h"):
  1043. confdefs.append("/* #undef HAVE_ENDIAN_H */\n")
  1044. confdefs.append("/* #undef HAVE_SYS_ENDIAN_H */\n")
  1045. confdefs.append("#define HAVE_MACHINE_ENDIAN_H 1\n")
  1046. else:
  1047. confdefs.append("/* #undef HAVE_ENDIAN_H */\n")
  1048. confdefs.append("/* #undef HAVE_SYS_ENDIAN_H */\n")
  1049. confdefs.append("/* #undef HAVE_MACHINE_ENDIAN_H */\n")
  1050. announce("You do not have the endian.h header file. "
  1051. "RTCM V2 support disabled.")
  1052. config.env["rtcm104v2"] = False
  1053. for hdr in ("arpa/inet",
  1054. "linux/serial", # for serial_icounter_struct
  1055. "netdb",
  1056. "netinet/in",
  1057. "netinet/ip",
  1058. "sys/sysmacros", # for major(), on linux
  1059. "sys/socket",
  1060. "sys/un",
  1061. "syslog",
  1062. "termios",
  1063. "winsock2"
  1064. ):
  1065. if config.CheckHeader(hdr + ".h"):
  1066. confdefs.append("#define HAVE_%s_H 1\n"
  1067. % hdr.replace("/", "_").upper())
  1068. elif "termios" == hdr:
  1069. announce("ERROR: %s.h not found" % hdr)
  1070. else:
  1071. confdefs.append("/* #undef HAVE_%s_H */\n"
  1072. % hdr.replace("/", "_").upper())
  1073. if 0 == config.CheckTime_t():
  1074. announce("WARNING: time_t is too small. It will fail in 2038")
  1075. sizeof_time_t = 4
  1076. else:
  1077. sizeof_time_t = 8
  1078. confdefs.append("#define SIZEOF_TIME_T %s\n" % sizeof_time_t)
  1079. # check function after libraries, because some function require libraries
  1080. # for example clock_gettime() require librt on Linux glibc < 2.17
  1081. for f in ("cfmakeraw", "clock_gettime", "daemon", "fcntl", "fork",
  1082. "getopt_long",
  1083. "gmtime_r", "inet_ntop", "strlcat", "strlcpy", "strptime"):
  1084. if config.CheckFunc(f):
  1085. confdefs.append("#define HAVE_%s 1\n" % f.upper())
  1086. else:
  1087. confdefs.append("/* #undef HAVE_%s */\n" % f.upper())
  1088. # used to check for sincos(), but making that work with -Werror did not work.
  1089. if config.CheckHeader(["sys/types.h", "sys/time.h", "sys/timepps.h"]):
  1090. confdefs.append("#define HAVE_SYS_TIMEPPS_H 1\n")
  1091. kpps = True
  1092. else:
  1093. kpps = False
  1094. if config.env["magic_hat"]:
  1095. announce("Forcing magic_hat=no since RFC2783 API is unavailable")
  1096. config.env["magic_hat"] = False
  1097. tiocmiwait = config.CheckDeclaration("TIOCMIWAIT",
  1098. "#include <sys/ioctl.h>")
  1099. if not tiocmiwait and not kpps:
  1100. announce("WARNING: Neither TIOCMIWAIT (PPS) nor RFC2783 API (KPPS) "
  1101. "is available.", end=True)
  1102. if config.env["timeservice"]:
  1103. announce("ERROR: timeservice specified, but no PPS available")
  1104. Exit(1)
  1105. # Map options to libraries required to support them that might be absent.
  1106. optionrequires = {
  1107. "bluez": ["libbluetooth"],
  1108. "dbus_export": ["libdbus-1"],
  1109. }
  1110. keys = list(map(lambda x: (x[0], x[2]), boolopts)) \
  1111. + list(map(lambda x: (x[0], x[2]), nonboolopts)) \
  1112. + list(map(lambda x: (x[0], x[2]), pathopts))
  1113. keys.sort()
  1114. for (key, helpd) in keys:
  1115. value = config.env[key]
  1116. if value and key in optionrequires:
  1117. for required in optionrequires[key]:
  1118. if not config.CheckLib(required):
  1119. announce("%s not found, %s cannot be enabled."
  1120. % (required, key))
  1121. value = False
  1122. break
  1123. confdefs.append("/* %s */" % helpd)
  1124. if isinstance(value, bool):
  1125. if value:
  1126. confdefs.append("#define %s_ENABLE 1\n" % key.upper())
  1127. else:
  1128. confdefs.append("/* #undef %s_ENABLE */\n" % key.upper())
  1129. elif value in (0, "", "(undefined)"):
  1130. confdefs.append("/* #undef %s */\n" % key.upper())
  1131. else:
  1132. if value.isdigit():
  1133. confdefs.append("#define %s %s\n" % (key.upper(), value))
  1134. else:
  1135. confdefs.append("#define %s \"%s\"\n" % (key.upper(), value))
  1136. # Simplifies life on hackerboards like the Raspberry Pi
  1137. if config.env['magic_hat']:
  1138. confdefs.append('''\
  1139. /* Magic device which, if present, means to grab a static /dev/pps0 for KPPS */
  1140. #define MAGIC_HAT_GPS "/dev/ttyAMA0"
  1141. /* Generic device which, if present, means: */
  1142. /* to grab a static /dev/pps0 for KPPS */
  1143. #define MAGIC_LINK_GPS "/dev/gpsd0"
  1144. ''')
  1145. confdefs.append('''\
  1146. #define GPSD_CONFIG_H
  1147. #endif /* GPSD_CONFIG_H */
  1148. ''')
  1149. # handle manbuild = no/auto/yes
  1150. # do we have asciidoctor, perhaps versioned?
  1151. adoc_prog = env.WhereIs('asciidoctor')
  1152. if (not adoc_prog):
  1153. adoc_prog = env.WhereIs('asciidoctor27')
  1154. if (not adoc_prog):
  1155. adoc_prog = env.WhereIs('asciidoctor26')
  1156. config.env['manbuild'] = config.env['manbuild'].lower()
  1157. if ((not config.env['manbuild'] or
  1158. 'auto' == config.env['manbuild'])):
  1159. if adoc_prog:
  1160. config.env['manbuild'] = 1
  1161. announce("Build of man and HTML documentation enabled.")
  1162. else:
  1163. config.env['manbuild'] = 0
  1164. announce("WARNING: AsciiDoctor not found.\n"
  1165. "WARNING: Some documentation and html will not be built.",
  1166. end=True)
  1167. else:
  1168. try:
  1169. config.env['manbuild'] = strtobool(
  1170. config.env['manbuild'])
  1171. except ValueError:
  1172. announce("ERROR: manbuild must be no/auto/yes.")
  1173. sys.exit(1)
  1174. if 0 == config.env['manbuild']:
  1175. adoc_prog = None
  1176. announce("Build of man and HTML documentation disabled.")
  1177. elif 1 == config.env['manbuild'] and not adoc_prog:
  1178. announce("ERROR: manbuild=True, but AsciiDoctor not found.\n")
  1179. sys.exit(1)
  1180. else:
  1181. announce("Build of man and HTML documentation enabled.")
  1182. # end handle manbuild = no/auto/yes
  1183. # Determine if Qt network libraries are present, and
  1184. # if not, force qt to off
  1185. if config.env["qt"]:
  1186. qt_net_name = 'Qt%sNetwork' % config.env["qt_versioned"]
  1187. qt_network = config.CheckPKG(qt_net_name)
  1188. if not qt_network:
  1189. config.env["qt"] = False
  1190. announce('Turning off Qt support, library not found.')
  1191. # If supported by the compiler, enable all warnings except uninitialized
  1192. # and missing-field-initializers, which we can't help triggering because
  1193. # of the way some of the JSON-parsing code is generated.
  1194. # Also not including -Wcast-qual and -Wimplicit-function-declaration,
  1195. # because we can't seem to keep scons from passing these to g++.
  1196. #
  1197. # Do this after the other config checks, to keep warnings out of them.
  1198. for option in ('-Wall',
  1199. '-Wcast-align',
  1200. '-Wextra',
  1201. # -Wimplicit-fallthrough same as
  1202. # -Wimplicit-fallthrough=3, except osX hates the
  1203. # second flavor
  1204. '-Wimplicit-fallthrough',
  1205. '-Wmissing-declarations',
  1206. '-Wmissing-prototypes',
  1207. '-Wno-missing-field-initializers',
  1208. '-Wno-uninitialized',
  1209. '-Wpointer-arith',
  1210. '-Wreturn-type',
  1211. '-Wstrict-prototypes',
  1212. '-Wvla',
  1213. ):
  1214. if option not in config.env['CFLAGS']:
  1215. config.CheckCompilerOption(option)
  1216. # check for misc audit programs
  1217. try:
  1218. have_canplayer = config.CheckProg('canplayer')
  1219. have_coverage = config.CheckProg('coverage')
  1220. have_cppcheck = config.CheckProg('cppcheck')
  1221. have_dia = config.CheckProg('dia')
  1222. have_flake8 = config.CheckProg('flake8')
  1223. have_pycodestyle = config.CheckProg('pycodestyle')
  1224. have_pylint = config.CheckProg('pylint')
  1225. have_scan_build = config.CheckProg('scan-build')
  1226. have_tar = config.CheckProg(env['TAR'])
  1227. have_valgrind = config.CheckProg('valgrind')
  1228. except AttributeError:
  1229. # scons versions before Sep 2015 (2.4.0) don't have CheckProg
  1230. # gpsd only asks for 2.3.0 or higher
  1231. announce("scons CheckProg() failed..")
  1232. if not have_canplayer:
  1233. announce("Program canplayer not found -- skipping NMEA 2000 tests")
  1234. if not have_coverage:
  1235. announce("Program coverage not found -- skipping Python coverage")
  1236. if not have_cppcheck:
  1237. announce("Program cppcheck not found -- skipping cppcheck checks")
  1238. if not have_dia:
  1239. announce("Program dia not found -- not rebuiding cycle.svg.")
  1240. if not have_flake8:
  1241. announce("Program flake8 not found -- skipping flake8 checks")
  1242. if not have_pycodestyle:
  1243. announce("Program pycodestyle not found -- "
  1244. "skipping pycodestyle checks")
  1245. if not have_pylint:
  1246. announce("Program pylint not found -- skipping pylint checks")
  1247. if not have_scan_build:
  1248. announce("Program scan-build not found -- skipping scan-build checks")
  1249. if not have_tar:
  1250. announce('WARNING: %s not found. Can not build tar files.' %
  1251. env['TAR'])
  1252. if not have_valgrind:
  1253. announce("Program valgrind not found -- skipping valgrind checks")
  1254. # Set up configuration for target Python
  1255. PYTHON_LIBDIR_CALL = 'sysconfig.get_python_lib()'
  1256. PYTHON_CONFIG_NAMES = ['SO'] # Now a fairly degenerate list
  1257. PYTHON_CONFIG_QUOTED = ["'%s'" % s for s in PYTHON_CONFIG_NAMES]
  1258. PYTHON_CONFIG_CALL = ('sysconfig.get_config_vars(%s)'
  1259. % ', '.join(PYTHON_CONFIG_QUOTED))
  1260. python_config = {} # Dummy for all non-Python-build cases
  1261. target_python_path = ''
  1262. py_config_text = str(eval(PYTHON_CONFIG_CALL))
  1263. python_libdir = str(eval(PYTHON_LIBDIR_CALL))
  1264. # flag if we have xgps* dependencies, so xgps* should run OK
  1265. config.env['xgps_deps'] = False
  1266. if not cleaning and not helping and config.env['python']:
  1267. if config.env['target_python']:
  1268. try:
  1269. config.CheckProg
  1270. except AttributeError:
  1271. # scons versions before Nov 2015 (2.4.1) don't have CheckProg
  1272. # gpsd only asks for 2.3.0 or higher
  1273. target_python_path = config.env['target_python']
  1274. else:
  1275. target_python_path = config.CheckProg(config.env['target_python'])
  1276. if ((not target_python_path and
  1277. 'python' == config.env['target_python'])):
  1278. # some distros don't install a python target, only python3
  1279. announce("Target Python '%s' doesn't exist. "
  1280. "Trying 'python3'." %
  1281. config.env['target_python'])
  1282. config.env['target_python'] = 'python3'
  1283. python_shebang = "/usr/bin/env %s" % def_target_python
  1284. try:
  1285. config.CheckProg
  1286. except AttributeError:
  1287. # FIXME: duplicates code above
  1288. # scons versions before Nov 2015 (2.4.1) don't
  1289. # have CheckProg # gpsd only asks for 2.3.0 or higher
  1290. target_python_path = config.env['target_python']
  1291. else:
  1292. target_python_path = config.CheckProg(
  1293. config.env['target_python'])
  1294. if not target_python_path:
  1295. announce("Target Python '%s' doesn't exist. Disabling Python." %
  1296. config.env['target_python'])
  1297. announce("Use the target_python=XX configuration option if you "
  1298. "have a working python target.")
  1299. config.env['python'] = False
  1300. if config.env['python']:
  1301. if not target_python_path:
  1302. # Avoid double testing for target_python_path
  1303. # Maximize consistency by using the reported sys.executable
  1304. target_python_path = config.GetPythonValue('exe path',
  1305. 'import sys',
  1306. 'sys.executable')
  1307. target_python_path = polystr(target_python_path)
  1308. # python module directory
  1309. if config.env['python_libdir']:
  1310. python_libdir = config.env['python_libdir']
  1311. else:
  1312. python_libdir = config.GetPythonValue('lib dir',
  1313. PYTHON_SYSCONFIG_IMPORT,
  1314. PYTHON_LIBDIR_CALL)
  1315. # follow FHS, put in /usr/local/libXX, not /usr/libXX
  1316. # may be lib, lib32 or lib64
  1317. python_libdir = polystr(python_libdir)
  1318. python_libdir = python_libdir.replace("/usr/lib",
  1319. "/usr/local/lib")
  1320. python_module_dir = str(python_libdir) + os.sep
  1321. # Many systems can have a problem with the Python path
  1322. if 'PYTHONPATH' in os.environ:
  1323. announce("System PYTHONPATH='%s'" % os.environ['PYTHONPATH'])
  1324. else:
  1325. announce("System PYTHONPATH is empty")
  1326. announce("Ensure your PYTHONPATH includes %s" % python_module_dir,
  1327. end=True)
  1328. python_module_dir += 'gps'
  1329. py_config_text = config.GetPythonValue('config vars',
  1330. PYTHON_SYSCONFIG_IMPORT,
  1331. PYTHON_CONFIG_CALL,
  1332. brief=True)
  1333. py_config_text = polystr(py_config_text)
  1334. py_config_vars = ast.literal_eval(py_config_text)
  1335. py_config_vars = [[] if x is None else x for x in py_config_vars]
  1336. python_config = dict(zip(PYTHON_CONFIG_NAMES, py_config_vars))
  1337. # debug
  1338. # announce(python_config)
  1339. # aiogps is only available on Python >= 3.6
  1340. sysver = config.GetPythonValue('target version',
  1341. 'import sys',
  1342. '"%d.%d" % sys.version_info[0:2]')
  1343. if tuple(map(int, sysver.split("."))) < (3, 6):
  1344. config.env['aiogps'] = False
  1345. announce("WARNING: Python%s too old (need 3.6): "
  1346. "gps/aiogps.py will not be installed" %
  1347. (sysver), end=True)
  1348. else:
  1349. config.env['aiogps'] = True
  1350. # check for pyserial
  1351. if not config.GetPythonValue('module serial (pyserial)',
  1352. 'import serial', '"found"'):
  1353. # no pyserial, used by ubxtool and zerk
  1354. announce("WARNING: ubxtool and zerk are missing optional "
  1355. "runtime module serial", end=True)
  1356. config.env['xgps_deps'] = True
  1357. # check for pycairo
  1358. if not config.GetPythonValue('module cairo (pycairo)',
  1359. 'import cairo', '"found"'):
  1360. # no pycairo, used by xgps, xgpsspeed
  1361. config.env['xgps_deps'] = False
  1362. announce("WARNING: Python module cairo (pycairo) not found.")
  1363. # check for pygobject
  1364. if not config.GetPythonValue('module gi (pygobject)',
  1365. 'import gi', '"found"'):
  1366. # no pygobject, used by xgps, xgpsspeed
  1367. config.env['xgps_deps'] = False
  1368. announce("WARNING: Python module gi (pygobject) not found.")
  1369. # gtk+ needed by pygobject
  1370. if not config.CheckPKG('gtk+-3.0'):
  1371. config.env['xgps_deps'] = False
  1372. announce("WARNING: gtk+-3.0 not found.")
  1373. if not config.env['xgps_deps']:
  1374. announce("WARNING: xgps and xgpsspeed are missing runtime "
  1375. "dependencies", end=True)
  1376. if not env['xgps']:
  1377. # xgps* turned off by option
  1378. config.env['xgps_deps'] = False
  1379. # check for matplotlib
  1380. if not config.GetPythonValue('module matplotlib',
  1381. 'import matplotlib', '"found"'):
  1382. # no matplotlib, used by gpsplot
  1383. announce("WARNING: gpsplot is missing required "
  1384. "runtime module matplotlib", end=True)
  1385. config.env['PYTHON'] = target_python_path
  1386. # For regress-driver
  1387. config.env['ENV']['PYTHON'] = target_python_path
  1388. # get a list of the files from git, so they can go in distribution zip/tar
  1389. distfiles = config.TryAction("git ls-files > $TARGET")[1]
  1390. distfiles = polystr(distfiles).split()
  1391. # add in the built man pages, zip and tar files must contain man pages.
  1392. distfiles += all_manpages.keys()
  1393. env = config.Finish()
  1394. # All configuration should be finished. env can now be modified.
  1395. # NO CONFIG TESTS AFTER THIS POINT!
  1396. qt_env = None
  1397. if not (cleaning or helping):
  1398. # Be explicit about what we're doing.
  1399. changelatch = False
  1400. for (name, default, helpd) in boolopts + nonboolopts + pathopts:
  1401. if env[name] != env.subst(default):
  1402. if not changelatch:
  1403. announce("Altered configuration variables:")
  1404. changelatch = True
  1405. announce("%s = %s (default %s): %s"
  1406. % (name, env[name], env.subst(default), helpd))
  1407. if not changelatch:
  1408. announce("All configuration flags are defaulted.")
  1409. # Should we build the Qt binding?
  1410. if env["qt"] and env["shared"]:
  1411. qt_env = env.Clone()
  1412. qt_env.MergeFlags('-DUSE_QT')
  1413. qt_env.Append(OBJPREFIX='qt-')
  1414. try:
  1415. qt_env.MergeFlags(pkg_config(qt_net_name))
  1416. except OSError:
  1417. announce("pkg_config is confused about the state of %s."
  1418. % qt_net_name)
  1419. qt_env = None
  1420. # Set up for Python coveraging if needed
  1421. pycov_path = None
  1422. if have_coverage and env['coveraging'] and env['python_coverage']:
  1423. pycov_default = opts.options[opts.keys().index('python_coverage')].default
  1424. pycov_current = env['python_coverage']
  1425. pycov_list = pycov_current.split()
  1426. if env.GetOption('num_jobs') > 1 and pycov_current == pycov_default:
  1427. pycov_list.append('--parallel-mode')
  1428. # May need absolute path to coveraging tool if 'PythonXX' is prefixed
  1429. pycov_path = env.WhereIs(pycov_list[0])
  1430. if pycov_path:
  1431. pycov_list[0] = pycov_path
  1432. env['PYTHON_COVERAGE'] = ' '.join(pycov_list)
  1433. env['ENV']['PYTHON_COVERAGE'] = ' '.join(pycov_list)
  1434. else:
  1435. env['python_coverage'] = '' # So we see it in the options
  1436. # Two shared libraries provide most of the code for the C programs
  1437. # gpsd client library
  1438. libgps_sources = [
  1439. "libgps/ais_json.c",
  1440. "libgps/bits.c",
  1441. "libgps/gpsdclient.c",
  1442. "libgps/gps_maskdump.c", # generated
  1443. "libgps/gpsutils.c",
  1444. "libgps/hex.c",
  1445. "libgps/json.c",
  1446. "libgps/libgps_core.c",
  1447. "libgps/libgps_dbus.c",
  1448. "libgps/libgps_json.c",
  1449. "libgps/libgps_shm.c",
  1450. "libgps/libgps_sock.c",
  1451. "libgps/netlib.c",
  1452. "libgps/ntpshmread.c",
  1453. "libgps/os_compat.c",
  1454. "libgps/rtcm2_json.c",
  1455. "libgps/rtcm3_json.c",
  1456. "libgps/shared_json.c",
  1457. "libgps/timespec_str.c",
  1458. ]
  1459. # Client sources not to be built as C++ when building the Qt library.
  1460. libgps_c_only = set([
  1461. "libgps/ais_json.c",
  1462. "libgps/json.c",
  1463. "libgps/libgps_json.c",
  1464. "libgps/os_compat.c",
  1465. "libgps/rtcm2_json.c",
  1466. "libgps/rtcm3_json.c",
  1467. "libgps/shared_json.c",
  1468. "libgps/timespec_str.c",
  1469. ])
  1470. if env['libgpsmm']:
  1471. libgps_sources.append("libgps/libgpsmm.cpp")
  1472. # gpsd server library
  1473. libgpsd_sources = [
  1474. "gpsd/bsd_base64.c",
  1475. "gpsd/crc24q.c",
  1476. "drivers/driver_ais.c",
  1477. "drivers/driver_evermore.c",
  1478. "drivers/driver_garmin.c",
  1479. "drivers/driver_garmin_txt.c",
  1480. "drivers/driver_geostar.c",
  1481. "drivers/driver_greis.c",
  1482. "drivers/driver_greis_checksum.c",
  1483. "drivers/driver_italk.c",
  1484. "drivers/driver_navcom.c",
  1485. "drivers/driver_nmea0183.c",
  1486. "drivers/driver_nmea2000.c",
  1487. "drivers/driver_oncore.c",
  1488. "drivers/driver_rtcm2.c",
  1489. "drivers/driver_rtcm3.c",
  1490. "drivers/drivers.c",
  1491. "drivers/driver_sirf.c",
  1492. "drivers/driver_skytraq.c",
  1493. "drivers/driver_superstar2.c",
  1494. "drivers/driver_tsip.c",
  1495. "drivers/driver_ubx.c",
  1496. "drivers/driver_zodiac.c",
  1497. "gpsd/geoid.c",
  1498. "gpsd/gpsd_json.c",
  1499. "gpsd/isgps.c",
  1500. "gpsd/libgpsd_core.c",
  1501. "gpsd/matrix.c",
  1502. "gpsd/net_dgpsip.c",
  1503. "gpsd/net_gnss_dispatch.c",
  1504. "gpsd/net_ntrip.c",
  1505. "gpsd/ntpshmwrite.c",
  1506. "gpsd/packet.c",
  1507. "gpsd/ppsthread.c",
  1508. "gpsd/pseudoais.c",
  1509. "gpsd/pseudonmea.c",
  1510. "gpsd/serial.c",
  1511. "gpsd/subframe.c",
  1512. "gpsd/timebase.c",
  1513. ]
  1514. # Build ffi binding
  1515. #
  1516. packet_ffi_extension = [
  1517. "gpsd/crc24q.c",
  1518. "drivers/driver_greis_checksum.c",
  1519. "drivers/driver_rtcm2.c",
  1520. "libgps/gpspacket.c",
  1521. "gpsd/isgps.c",
  1522. "libgps/hex.c",
  1523. "libgps/os_compat.c",
  1524. "gpsd/packet.c",
  1525. ]
  1526. if env["shared"]:
  1527. def GPSLibrary(env, target, source, version, parse_flags=None):
  1528. # Note: We have a possibility of getting either Object or file
  1529. # list for sources, so we run through the sources and try to make
  1530. # them into SharedObject instances.
  1531. obj_list = []
  1532. for s in Flatten(source):
  1533. if isinstance(s, str):
  1534. obj_list.append(env.SharedObject(s))
  1535. else:
  1536. obj_list.append(s)
  1537. return env.SharedLibrary(target=target,
  1538. source=obj_list,
  1539. parse_flags=parse_flags,
  1540. SHLIBVERSION=version)
  1541. def GPSLibraryInstall(env, libdir, source, version):
  1542. # note: osX lib name s/b libgps.VV.dylib
  1543. # where VV is libgps_version_current
  1544. inst = env.InstallVersionedLib(libdir, source, SHLIBVERSION=version)
  1545. return inst
  1546. else:
  1547. def GPSLibrary(env, target, source, version, parse_flags=None):
  1548. return env.StaticLibrary(target,
  1549. [env.StaticObject(s) for s in source],
  1550. parse_flags=parse_flags)
  1551. def GPSLibraryInstall(env, libdir, source, version):
  1552. return env.Install(libdir, source)
  1553. libgps_shared = GPSLibrary(env=env,
  1554. target="gps",
  1555. source=libgps_sources,
  1556. version=libgps_version,
  1557. parse_flags=rtlibs + libgps_flags)
  1558. libgps_static = env.StaticLibrary(
  1559. target="gps_static",
  1560. source=[env.StaticObject(s) for s in libgps_sources],
  1561. parse_flags=rtlibs)
  1562. libgpsd_static = env.StaticLibrary(
  1563. target="gpsd",
  1564. source=[env.StaticObject(s, parse_flags=usbflags + bluezflags)
  1565. for s in libgpsd_sources],
  1566. parse_flags=usbflags + bluezflags)
  1567. # FFI library must always be shared, even with shared=no.
  1568. packet_ffi_objects = [env.SharedObject(s) for s in packet_ffi_extension]
  1569. packet_ffi_shared = env.SharedLibrary(target="gpsdpacket",
  1570. source=packet_ffi_objects,
  1571. SHLIBVERSION=libgps_version,
  1572. parse_flags=rtlibs + libgps_flags)
  1573. libraries = [libgps_shared, packet_ffi_shared]
  1574. # Only attempt to create the qt library if we have shared turned on
  1575. # otherwise we have a mismash of objects in library
  1576. if qt_env:
  1577. qtobjects = []
  1578. qt_flags = qt_env['CFLAGS']
  1579. for c_only in ('-Wmissing-prototypes', '-Wstrict-prototypes',
  1580. '-Wmissing-declarations'):
  1581. if c_only in qt_flags:
  1582. qt_flags.remove(c_only)
  1583. # Qt binding object files have to be renamed as they're built to avoid
  1584. # name clashes with the plain non-Qt object files. This prevents the
  1585. # infamous "Two environments with different actions were specified
  1586. # for the same target" error.
  1587. for src in libgps_sources:
  1588. if src not in libgps_c_only:
  1589. compile_with = qt_env['CXX']
  1590. compile_flags = qt_flags
  1591. else:
  1592. compile_with = qt_env['CC']
  1593. compile_flags = qt_env['CFLAGS']
  1594. qtobjects.append(qt_env.SharedObject(src,
  1595. CC=compile_with,
  1596. CFLAGS=compile_flags))
  1597. compiled_qgpsmmlib = GPSLibrary(env=qt_env,
  1598. target="Qgpsmm",
  1599. source=qtobjects,
  1600. version=libgps_version,
  1601. parse_flags=libgps_flags)
  1602. libraries.append(compiled_qgpsmmlib)
  1603. # The libraries have dependencies on system libraries
  1604. # libdbus appears multiple times because the linker only does one pass.
  1605. gpsflags = mathlibs + rtlibs + dbusflags
  1606. gpsdflags = usbflags + bluezflags + gpsflags
  1607. # Source groups
  1608. gpsd_sources = [
  1609. 'gpsd/dbusexport.c',
  1610. 'gpsd/gpsd.c',
  1611. 'gpsd/shmexport.c',
  1612. 'gpsd/timehint.c'
  1613. ]
  1614. if env['systemd']:
  1615. gpsd_sources.append("gpsd/sd_socket.c")
  1616. gpsmon_sources = [
  1617. 'gpsmon/gpsmon.c',
  1618. 'gpsmon/monitor_garmin.c',
  1619. 'gpsmon/monitor_italk.c',
  1620. 'gpsmon/monitor_nmea0183.c',
  1621. 'gpsmon/monitor_oncore.c',
  1622. 'gpsmon/monitor_sirf.c',
  1623. 'gpsmon/monitor_superstar2.c',
  1624. 'gpsmon/monitor_tnt.c',
  1625. 'gpsmon/monitor_ubx.c',
  1626. ]
  1627. # Python dependencies
  1628. # For generated dependencies, this causes them to be generated as needed.
  1629. # For non-generated dependencies, it causes them to be duplicated into
  1630. # the build tree as needed.
  1631. # Symlink creator for uplevel access to the 'gps' package
  1632. def PylibLink(target, source, env):
  1633. _ = source, env
  1634. os.symlink('../gps', target[0].get_path())
  1635. # All installed python programs
  1636. # All are templated
  1637. python_progs = [
  1638. "clients/gegps",
  1639. "clients/gpscat",
  1640. "clients/gpscsv",
  1641. "clients/gpsplot",
  1642. "clients/gpsprof",
  1643. "clients/gpssubframe",
  1644. "clients/ubxtool",
  1645. "clients/zerk",
  1646. "gpsfake",
  1647. ]
  1648. if env['xgps']:
  1649. python_progs.append("clients/xgps")
  1650. python_progs.append("clients/xgpsspeed")
  1651. # Import dependencies
  1652. # Update these whenever the imports change
  1653. # Internal imports within 'gps' package
  1654. env.Depends('gps/__init__.py', ['gps/gps.py', 'gps/misc.py'])
  1655. env.Depends('gps/aiogps.py', ['gps/client.py', 'gps/gps.py', 'gps/misc.py'])
  1656. env.Depends('gps/client.py', ['gps/misc.py', 'gps/watch_options.py'])
  1657. env.Depends('gps/gps.py',
  1658. ['gps/client.py', 'gps/misc.py', 'gps/watch_options.py'])
  1659. env.Depends('gps/fake.py', 'gps/packet.py')
  1660. env.Depends('gps/packet.py', 'gps/misc.py')
  1661. # All Python programs import the 'gps' package
  1662. env.Depends(python_progs, 'gps/__init__.py')
  1663. # Additional specific import cases
  1664. env.Depends('clients/gpscat', ['gps/packet.py', 'gps/misc.py'])
  1665. env.Depends('clients/gpsplot', 'gps/clienthelpers.py')
  1666. env.Depends('clients/gpsprof', 'gps/clienthelpers.py')
  1667. env.Depends('clients/ubxtool', 'gps/ubx.py')
  1668. env.Depends('clients/xgps', 'gps/clienthelpers.py')
  1669. env.Depends('clients/xgpsspeed', 'gps/clienthelpers.py')
  1670. env.Depends('clients/zerk', 'gps/misc.py')
  1671. env.Depends('gpsfake', ['gps/fake.py', 'gps/misc.py'])
  1672. # Symlink for the clients to find the 'gps' package in the build tree
  1673. env.Depends(python_progs, env.Command('clients/gps', '', PylibLink))
  1674. # Non-import dependencies
  1675. # Dependency on program
  1676. env.Depends('regress-driver', 'gpsfake')
  1677. # Dependency on FFI packet library
  1678. env.Depends('gps/packet.py', packet_ffi_shared)
  1679. # Production programs
  1680. cgps = env.Program('clients/cgps', ['clients/cgps.c'],
  1681. LIBS=[libgps_static],
  1682. parse_flags=gpsflags + ncurseslibs)
  1683. gps2udp = env.Program('clients/gps2udp', ['clients/gps2udp.c'],
  1684. LIBS=[libgps_static],
  1685. parse_flags=gpsflags)
  1686. gpsctl = env.Program('gpsctl', ['gpsctl.c'],
  1687. LIBS=[libgpsd_static, libgps_static],
  1688. parse_flags=gpsdflags + gpsflags)
  1689. gpsd = env.Program('gpsd/gpsd', gpsd_sources,
  1690. LIBS=[libgpsd_static, libgps_static],
  1691. parse_flags=gpsdflags + gpsflags)
  1692. gpsdctl = env.Program('clients/gpsdctl', ['clients/gpsdctl.c'],
  1693. LIBS=[libgps_static],
  1694. parse_flags=gpsflags)
  1695. gpsdecode = env.Program('clients/gpsdecode', ['clients/gpsdecode.c'],
  1696. LIBS=[libgpsd_static, libgps_static],
  1697. parse_flags=gpsdflags + gpsflags)
  1698. # FIXME: gpsmon should not link to gpsd server sources!
  1699. gpsmon = env.Program('gpsmon/gpsmon', gpsmon_sources,
  1700. LIBS=[libgpsd_static, libgps_static],
  1701. parse_flags=gpsdflags + gpsflags + ncurseslibs)
  1702. gpspipe = env.Program('clients/gpspipe', ['clients/gpspipe.c'],
  1703. LIBS=[libgps_static],
  1704. parse_flags=gpsflags)
  1705. gpsrinex = env.Program('clients/gpsrinex', ['clients/gpsrinex.c'],
  1706. LIBS=[libgps_static],
  1707. parse_flags=gpsflags)
  1708. gpssnmp = env.Program('clients/gpssnmp', ['clients/gpssnmp.c'],
  1709. LIBS=[libgps_static],
  1710. parse_flags=gpsflags)
  1711. gpxlogger = env.Program('clients/gpxlogger', ['clients/gpxlogger.c'],
  1712. LIBS=[libgps_static],
  1713. parse_flags=gpsflags)
  1714. lcdgps = env.Program('clients/lcdgps', ['clients/lcdgps.c'],
  1715. LIBS=[libgps_static],
  1716. parse_flags=gpsflags)
  1717. ntpshmmon = env.Program('clients/ntpshmmon', ['clients/ntpshmmon.c'],
  1718. LIBS=[libgps_static],
  1719. parse_flags=gpsflags)
  1720. ppscheck = env.Program('clients/ppscheck', ['clients/ppscheck.c'],
  1721. LIBS=[libgps_static],
  1722. parse_flags=gpsflags)
  1723. bin_binaries = []
  1724. bin_scripts = []
  1725. sbin_binaries = []
  1726. if env["gpsd"]:
  1727. sbin_binaries += [gpsd]
  1728. if env["gpsdclients"]:
  1729. sbin_binaries += [gpsdctl]
  1730. bin_binaries += [
  1731. gps2udp,
  1732. gpsctl,
  1733. gpsdecode,
  1734. gpspipe,
  1735. gpsrinex,
  1736. gpssnmp,
  1737. gpxlogger,
  1738. lcdgps
  1739. ]
  1740. bin_scripts += [
  1741. 'clients/gpsdebuginfo',
  1742. ]
  1743. if env["timeservice"] or env["gpsdclients"]:
  1744. bin_binaries += [ntpshmmon]
  1745. if tiocmiwait:
  1746. bin_binaries += [ppscheck]
  1747. if env["ncurses"]:
  1748. bin_binaries += [cgps, gpsmon]
  1749. else:
  1750. announce("WARNING: ncurses not found, not building cgps or gpsmon.",
  1751. end=True)
  1752. # Test programs - always link locally and statically
  1753. test_bits = env.Program('tests/test_bits',
  1754. [libgps_static, 'tests/test_bits.c'],
  1755. LIBS=[libgps_static])
  1756. test_float = env.Program('tests/test_float', ['tests/test_float.c'])
  1757. test_geoid = env.Program('tests/test_geoid',
  1758. [libgpsd_static, libgps_static, 'tests/test_geoid.c'],
  1759. LIBS=[libgpsd_static, libgps_static],
  1760. parse_flags=gpsdflags)
  1761. test_gpsdclient = env.Program('tests/test_gpsdclient',
  1762. [libgps_static, 'tests/test_gpsdclient.c'],
  1763. LIBS=[libgps_static, 'm'])
  1764. test_matrix = env.Program('tests/test_matrix',
  1765. [libgpsd_static, libgps_static, 'tests/test_matrix.c'],
  1766. LIBS=[libgpsd_static, libgps_static],
  1767. parse_flags=gpsdflags)
  1768. test_mktime = env.Program('tests/test_mktime',
  1769. [libgps_static, 'tests/test_mktime.c'],
  1770. LIBS=[libgps_static], parse_flags=mathlibs + rtlibs)
  1771. test_packet = env.Program('tests/test_packet',
  1772. [libgpsd_static, libgps_static,'tests/test_packet.c'],
  1773. LIBS=[libgpsd_static, libgps_static],
  1774. parse_flags=gpsdflags)
  1775. test_timespec = env.Program('tests/test_timespec', ['tests/test_timespec.c'],
  1776. LIBS=[libgpsd_static, libgps_static],
  1777. parse_flags=gpsdflags)
  1778. test_trig = env.Program('tests/test_trig', ['tests/test_trig.c'],
  1779. parse_flags=mathlibs)
  1780. # test_libgps for glibc older than 2.17
  1781. test_libgps = env.Program('tests/test_libgps',
  1782. [libgps_static, 'tests/test_libgps.c'],
  1783. LIBS=[libgps_static],
  1784. parse_flags=mathlibs + rtlibs + dbusflags)
  1785. if env['socket_export']:
  1786. test_json = env.Program(
  1787. 'tests/test_json',
  1788. [libgps_static, 'tests/test_json.c'],
  1789. LIBS=[libgps_static],
  1790. parse_flags=mathlibs + rtlibs + usbflags + dbusflags)
  1791. else:
  1792. announce("test_json not building because socket_export is disabled")
  1793. test_json = None
  1794. # duplicate below?
  1795. test_gpsmm = env.Program('tests/test_gpsmm',
  1796. [libgps_static, 'tests/test_gpsmm.cpp'],
  1797. LIBS=[libgps_static],
  1798. parse_flags=mathlibs + rtlibs + dbusflags)
  1799. testprogs = [test_bits,
  1800. test_float,
  1801. test_geoid,
  1802. test_gpsdclient,
  1803. test_libgps,
  1804. test_matrix,
  1805. test_mktime,
  1806. test_packet,
  1807. test_timespec,
  1808. test_trig]
  1809. if env['socket_export'] or cleaning:
  1810. testprogs.append(test_json)
  1811. if env["libgpsmm"] or cleaning:
  1812. testprogs.append(test_gpsmm)
  1813. # Python programs
  1814. # python misc helpers and stuff, not to be installed
  1815. python_misc = [
  1816. "libgps/jsongen.py",
  1817. "maskaudit.py",
  1818. "tests/test_clienthelpers.py",
  1819. "tests/test_misc.py",
  1820. "tests/test_xgps_deps.py",
  1821. "www/gpscap.py",
  1822. "valgrind-audit.py"
  1823. ]
  1824. # Dependencies for imports in test programs
  1825. env.Depends('tests/test_clienthelpers.py',
  1826. ['gps/__init__.py', 'gps/clienthelpers.py', 'gps/misc.py'])
  1827. env.Depends('tests/test_misc.py', ['gps/__init__.py', 'gps/misc.py'])
  1828. env.Depends('valgrind-audit.py', ['gps/__init__.py', 'gps/fake.py'])
  1829. # Symlink for the programs to find the 'gps' package in the build tree
  1830. env.Depends(['tests/test_clienthelpers.py', 'tests/test_misc.py'],
  1831. env.Command('tests/gps', '', PylibLink))
  1832. # Glob() has to be run after all buildable objects defined.
  1833. # Glob(), by default, looks in the file tree, and current buildable objects.
  1834. python_modules = Glob('gps/*.py', strings=True) + ['gps/__init__.py',
  1835. 'gps/gps.py',
  1836. 'gps/packet.py']
  1837. # Remove the aiogps module if not configured
  1838. # Don't use Glob's exclude option, since it may not be available
  1839. if 'aiogps' in env and env['aiogps']:
  1840. python_misc.extend(["example_aiogps.py", "example_aiogps_run"])
  1841. else:
  1842. try:
  1843. python_modules.remove('gps/aiogps.py')
  1844. except ValueError:
  1845. pass
  1846. # Make PEP 241 Metadata 1.0.
  1847. # Why not PEP 314 (V1.1) or PEP 345 (V1.2)?
  1848. # V1.1 and V1.2 require a Download-URL to an installable binary
  1849. python_egg_info_source = """Metadata-Version: 1.0
  1850. Name: gps
  1851. Version: %s
  1852. Summary: Python libraries for the gpsd service daemon
  1853. Home-page: %s
  1854. Author: the GPSD project
  1855. Author-email: %s
  1856. License: BSD
  1857. Keywords: GPS
  1858. Description: The gpsd service daemon can monitor one or more GPS devices \
  1859. connected to a host computer, making all data on the location and movements \
  1860. of the sensors available to be queried on TCP port 2947.
  1861. Platform: UNKNOWN
  1862. """ % (gpsd_version, website, devmail)
  1863. python_egg_info = env.Textfile(target="gps-%s.egg-info" % (gpsd_version, ),
  1864. source=python_egg_info_source)
  1865. python_targets = ([python_egg_info] + python_progs + python_modules)
  1866. env.Command(target="include/packet_names.h", source="include/packet_states.h",
  1867. action="""
  1868. rm -f $TARGET &&\
  1869. sed -e '/^ *\\([A-Z][A-Z0-9_]*\\),/s// \"\\1\",/' <$SOURCE >$TARGET &&\
  1870. chmod a-w $TARGET""")
  1871. env.Textfile(target="include/gpsd_config.h", source=confdefs)
  1872. env.Command(target="libgps/gps_maskdump.c",
  1873. source=["maskaudit.py", "include/gps.h", "include/gpsd.h"],
  1874. action='''
  1875. rm -f $TARGET &&\
  1876. $SC_PYTHON $SOURCE -c $SRCDIR > $TARGET &&\
  1877. chmod a-w $TARGET''')
  1878. env.Command(target="libgps/ais_json.i", source="libgps/jsongen.py",
  1879. action='''\
  1880. rm -f $TARGET &&\
  1881. $SC_PYTHON $SOURCE --ais --target=parser >$TARGET &&\
  1882. chmod a-w $TARGET''')
  1883. if env['systemd']:
  1884. udevcommand = 'TAG+="systemd", ENV{SYSTEMD_WANTS}="gpsdctl@%k.service"'
  1885. else:
  1886. udevcommand = 'RUN+="%s/gpsd.hotplug"' % (env['udevdir'], )
  1887. # FIXME: why do this every time scons is called?
  1888. # $variantdir may not exist when this is run.
  1889. pythonize_header_match = re.compile(r'\s*#define\s+(\w+)\s+(\w+)\s*.*$[^\\]')
  1890. pythonized_header = ''
  1891. with open(env['SRCDIR'] + '/../include/gpsd.h') as sfp:
  1892. for content in sfp:
  1893. _match3 = pythonize_header_match.match(content)
  1894. if _match3:
  1895. if 'LOG' in content or 'PACKET' in content:
  1896. pythonized_header += ('%s = %s\n' %
  1897. (_match3.group(1), _match3.group(2)))
  1898. if ((env['python'] and
  1899. not cleaning and
  1900. not helping and
  1901. def_target_python != env['target_python'])):
  1902. # non-default target python.
  1903. if def_python_shebang == env['python_shebang']:
  1904. # default python shebang, update to match target python
  1905. if os.sep == env['target_python'][0]:
  1906. # full path, no need for env
  1907. env['python_shebang'] = env['target_python']
  1908. else:
  1909. # partial path, need env
  1910. env['python_shebang'] = "/usr/bin/env %s" % env['target_python']
  1911. announce("Setting python_shebang to %s" % env['python_shebang'])
  1912. # tuples for Substfile. To convert .in files to generated files.
  1913. substmap = (
  1914. ('@ANNOUNCE@', annmail),
  1915. ('@BUGTRACKER@', bugtracker),
  1916. ('@CGIUPLOAD@', cgiupload),
  1917. ('@CLONEREPO@', clonerepo),
  1918. ('@DEVMAIL@', devmail),
  1919. ('@DOWNLOAD@', download),
  1920. ('@FORMSERVER@', formserver),
  1921. ('@GENERATED@', "This code is generated by scons. Do not hand-hack it!"),
  1922. ('@GITREPO@', gitrepo),
  1923. ('@GPSAPIVERMAJ@', api_version_major),
  1924. ('@GPSAPIVERMIN@', api_version_minor),
  1925. ('@GPSPACKET@', packet_ffi_shared[0].get_path()),
  1926. ('@ICONPATH@', installdir('icondir', add_destdir=False)),
  1927. ('@INCLUDEDIR@', installdir('includedir', add_destdir=False)),
  1928. ('@IRCCHAN@', ircchan),
  1929. ('@ISSUES@', bugtracker),
  1930. ('@LIBDIR@', installdir('libdir', add_destdir=False)),
  1931. ('@LIBGPSVERSION@', libgps_version),
  1932. ('@MAILMAN@', mailman),
  1933. ('@MAINPAGE@', mainpage),
  1934. ('@MASTER@', 'DO NOT HAND_HACK! THIS FILE IS GENERATED'),
  1935. ('@PREFIX@', env['prefix']),
  1936. ('@PROJECTPAGE@', projectpage),
  1937. # PEP 394 and 397 python shebang
  1938. ('@PYSHEBANG@', env['python_shebang']),
  1939. ('@PYPACKETH@', pythonized_header),
  1940. ('@QTVERSIONED@', env['qt_versioned']),
  1941. ('@RUNDIR@', env['rundir']),
  1942. ('@SBINDIR@', installdir('sbindir', add_destdir=False)),
  1943. ('@SCPUPLOAD@', scpupload),
  1944. ('@SHAREPATH@', installdir('sharedir', add_destdir=False)),
  1945. ('@SITENAME@', sitename),
  1946. ('@SITESEARCH@', sitesearch),
  1947. ('@SUPPORT@', 'https://gpsd.io/SUPPORT.html'),
  1948. ('@TIPLINK@', tiplink),
  1949. ('@TIPWIDGET@', tipwidget),
  1950. ('@UDEVCOMMAND@', udevcommand),
  1951. ('@USERMAIL@', usermail),
  1952. ('@VERSION@', gpsd_version),
  1953. ('@WEBSITE@', website),
  1954. )
  1955. # Keep time-dependent version separate
  1956. # FIXME: Come up with a better approach with reproducible builds
  1957. substmap_dated = substmap + (('@DATE@', time.asctime()),)
  1958. # explicit templated files
  1959. templated = {
  1960. "android/gpsd_config": "android/gpsd_config.in",
  1961. "clients/gegps": "clients/gegps.py.in",
  1962. "clients/gpscat": "clients/gpscat.py.in",
  1963. "clients/gpscsv": "clients/gpscsv.py.in",
  1964. "clients/gpsd.php": "clients/gpsd.php.in",
  1965. "clients/gpsplot": "clients/gpsplot.py.in",
  1966. "clients/gpsprof": "clients/gpsprof.py.in",
  1967. "clients/gpssubframe": "clients/gpssubframe.py.in",
  1968. "clients/ubxtool": "clients/ubxtool.py.in",
  1969. "clients/xgps": "clients/xgps.py.in",
  1970. "clients/xgpsspeed": "clients/xgpsspeed.py.in",
  1971. "clients/zerk": "clients/zerk.py.in",
  1972. "contrib/ntpshmviz": "contrib/ntpshmviz.py.in",
  1973. "contrib/skyview2svg.py": "contrib/skyview2svg.py.in",
  1974. "contrib/webgps": "contrib/webgps.py.in",
  1975. "control": "control.in",
  1976. "gpsd.rules": "gpsd.rules.in",
  1977. "gpsfake": "gpsfake.py.in",
  1978. "gps/gps.py": "gps/gps.py.in",
  1979. "gps/__init__.py": "gps/__init__.py.in",
  1980. "gps/packet.py": "gps/packet.py.in",
  1981. "libgps.pc": "libgps.pc.in",
  1982. "libQgpsmm.prl": "libQgpsmm.prl.in",
  1983. "packaging/deb/etc_default_gpsd": "packaging/deb/etc_default_gpsd.in",
  1984. "packaging/deb/etc_init.d_gpsd": "packaging/deb/etc_init.d_gpsd.in",
  1985. "packaging/gpsd-setup.py": "packaging/gpsd-setup.py.in",
  1986. "packaging/rpm/gpsd.init": "packaging/rpm/gpsd.init.in",
  1987. "packaging/rpm/gpsd.spec": "packaging/rpm/gpsd.spec.in",
  1988. "packaging/X11/xgps.desktop": "packaging/X11/xgps.desktop.in",
  1989. "packaging/X11/xgpsspeed.desktop": "packaging/X11/xgpsspeed.desktop.in",
  1990. "Qgpsmm.pc": "Qgpsmm.pc.in",
  1991. "systemd/gpsdctl@.service": "systemd/gpsdctl@.service.in",
  1992. "systemd/gpsd.service": "systemd/gpsd.service.in",
  1993. "systemd/gpsd.socket": "systemd/gpsd.socket.in",
  1994. "www/faq.html": "www/faq.html.in",
  1995. "www/gps_report.cgi": "www/gps_report.cgi.in",
  1996. "www/gpscap.py": "www/gpscap.py.in",
  1997. "www/hacking.html": "www/hacking.html.in",
  1998. "www/hardware-head.html": "www/hardware-head.html.in",
  1999. "www/index.html": "www/index.html.in",
  2000. "www/troubleshooting.html": "www/troubleshooting.html.in",
  2001. }
  2002. for (tgt, src) in templated.items():
  2003. iswww = tgt.startswith('www/')
  2004. # Only www pages need @DATE@ expansion, which forces rebuild every time
  2005. subst = substmap_dated if iswww else substmap
  2006. # use scons built-in Substfile()
  2007. builder = env.Substfile(target=tgt, source=src, SUBST_DICT=subst)
  2008. # default to building all built targets, except www
  2009. # FIXME: Render this unnecessary
  2010. if not iswww:
  2011. env.Default(builder)
  2012. # set read-only to alert people trying to edit the files.
  2013. env.AddPostAction(builder, 'chmod -w $TARGET')
  2014. if ((src.endswith(".py.in") or
  2015. tgt in python_progs or
  2016. tgt in ['contrib/ntpshmviz', 'contrib/webgps'])):
  2017. # set python files to executable
  2018. env.AddPostAction(builder, 'chmod +x $TARGET')
  2019. # When the URL declarations change, so must the generated web pages
  2020. for fn in glob.glob("www/*.in"):
  2021. env.Depends(fn[:-3], ["SConstruct", "SConscript"])
  2022. # asciidoc documents
  2023. asciidocs = []
  2024. man_env = env.Clone()
  2025. if man_env.GetOption('silent'):
  2026. man_env['SPAWN'] = filtered_spawn # Suppress stderr chatter
  2027. manpage_targets = []
  2028. maninstall = []
  2029. if adoc_prog:
  2030. adoc_args_m = ('-v -a gpsdweb=%s -a gpsdver=%s' % (website, gpsd_version))
  2031. adoc_args = (adoc_args_m + ' -a docinfo=shared')
  2032. for (man, src) in all_manpages.items():
  2033. # build it
  2034. # make nroff man page
  2035. asciidocs.append(man)
  2036. env.Command(man, src,
  2037. '%s -b manpage %s -o $TARGET $SOURCE' %
  2038. (adoc_prog, adoc_args_m))
  2039. # install nroff man page
  2040. section = man.split(".")[1]
  2041. dest = os.path.join(installdir('mandir'), "man" + section)
  2042. maninstall.append(env.Install(target=dest, source=man))
  2043. # make html man page
  2044. target = 'www/%s.html' % os.path.basename(man[:-2])
  2045. env.Depends(src, ['www/docinfo.html', 'www/inc-menu.adoc'])
  2046. tgt = env.Command(target, src,
  2047. '%s -b html5 %s -a docinfodir=../www/ -o $TARGET $SOURCE' %
  2048. (adoc_prog, adoc_args))
  2049. asciidocs.append(tgt)
  2050. else:
  2051. # can't build man pages, maybe we have pre-built ones?
  2052. for man in Glob('man/*.?', strings=True):
  2053. section = man.split(".")[1]
  2054. dest = os.path.join(installdir('mandir'), "man" + section)
  2055. maninstall.append(env.Install(target=dest, source=man))
  2056. # The hardware page
  2057. env.Command('www/hardware.html',
  2058. ['www/gpscap.py',
  2059. 'www/hardware-head.html',
  2060. 'www/gpscap.ini',
  2061. 'www/hardware-tail.html'],
  2062. ['cd %s/www; (cat hardware-head.html && PYTHONIOENCODING=utf-8 '
  2063. '$SC_PYTHON gpscap.py && cat hardware-tail.html) '
  2064. '> hardware.html' % variantdir])
  2065. # doc to install in 'docdir'
  2066. docinstall = env.Install(target=installdir('docdir'), source=doc_files)
  2067. if adoc_prog:
  2068. adocfiles = (('build', 'www/building'),
  2069. ('INSTALL', 'www/installation'),
  2070. ('README', 'www/README'),
  2071. ('SUPPORT', 'www/SUPPORT'),
  2072. ('www/AIVDM', 'www/AIVDM'),
  2073. ('www/client-howto', 'www/client-howto'),
  2074. ('www/gpsd-numbers-matter',
  2075. 'www/gpsd-numbers-matter'),
  2076. ('www/gpsd-client-example-code',
  2077. 'www/gpsd-client-example-code'),
  2078. ('www/gpsd-time-service-howto',
  2079. 'www/gpsd-time-service-howto'),
  2080. ('www/internals', 'www/internals'),
  2081. ('www/NMEA', 'www/NMEA'),
  2082. ('www/ppp-howto', 'www/ppp-howto'),
  2083. ('www/protocol-evolution', 'www/protocol-evolution'),
  2084. ('www/protocol-transition', 'www/protocol-transition'),
  2085. ('www/replacing-nmea', 'www/replacing-nmea'),
  2086. ('www/time-service-intro', 'www/time-service-intro'),
  2087. ('www/ubxtool-examples', 'www/ubxtool-examples'),
  2088. ('www/writing-a-driver', 'www/writing-a-driver'),
  2089. ('www/performance/performance',
  2090. 'www/performance/performance'),
  2091. )
  2092. for src, tgt in adocfiles:
  2093. target = '%s.html' % tgt
  2094. env.Depends(src, ['www/docinfo.html', 'www/inc-menu.adoc'])
  2095. tgt = env.Command(target, '%s.adoc' % src,
  2096. '%s -b html5 %s -o $TARGET $SOURCE' %
  2097. (adoc_prog, adoc_args))
  2098. asciidocs.append(tgt)
  2099. # Non-asciidoc, plain html webpages only
  2100. # example1.c has a .txt extension to avoid an scons bug where the
  2101. # install rule for gps.h is invoked during the build step when there
  2102. # is a CPPFLAGS matching gpsd's install prefix.
  2103. htmlpages = [
  2104. 'www/bt.html',
  2105. 'www/bu_303b.html',
  2106. 'www/example1.c.txt',
  2107. 'www/excellence.html',
  2108. 'www/for-vendors.html',
  2109. 'www/future.html',
  2110. 'www/gps-hacking.html',
  2111. 'www/gypsy.html',
  2112. 'www/hall-of-shame.html',
  2113. 'www/hardware.html', # built above
  2114. 'www/history.html',
  2115. 'www/references.html',
  2116. 'www/reliability.html',
  2117. 'www/upstream-bugs.html',
  2118. 'www/wishlist.html',
  2119. 'www/xgps-sample.html',
  2120. ]
  2121. wwwpage_targets = []
  2122. # webapges from .in files
  2123. webpages_in = list(map(lambda f: f[3:-3], glob.glob("../www/*.in")))
  2124. webpages_in_not = ('www/hardware-tail.html')
  2125. for fn in webpages_in_not:
  2126. if fn in webpages_in:
  2127. webpages_in.remove(fn)
  2128. # webapges extras: images, css, js
  2129. webpages_x_list = ('../www/*.css',
  2130. '../www/*.gif',
  2131. '../www/*.ico',
  2132. '../www/*.js',
  2133. '../www/*.png',
  2134. '../www/*.svg',
  2135. '../www/performance/*css',
  2136. '../www/performance/*png',
  2137. '../www/performance/*txt',
  2138. )
  2139. webpages_x = []
  2140. for glb in webpages_x_list:
  2141. webpages_x += list(map(lambda f: f[3:], glob.glob(glb)))
  2142. webpages_static = [('www/NEWS', 'NEWS'),
  2143. ('www/TODO', 'TODO'),
  2144. ('www/gpsdebuginfo', 'clients/gpsdebuginfo'),
  2145. ]
  2146. for page in webpages_static:
  2147. targ = env.Command(page[0], page[1], 'cp $SOURCE $TARGET')
  2148. webpages_x += targ
  2149. webpages = htmlpages + asciidocs + wwwpage_targets + webpages_in + webpages_x
  2150. www = env.Alias('www', webpages)
  2151. # On the Mac (at least), some X11 programs launch the X11 server even when
  2152. # they're not actually using the display. Clearing DISPLAY in the
  2153. # environment avoids this. We leave the main environment untouched just in
  2154. # case it might be needed.
  2155. nox11_env = env['ENV'].copy()
  2156. nox11_env['DISPLAY'] = ''
  2157. # The diagram editor dia is required in order to edit the diagram masters
  2158. if have_dia:
  2159. env.Command("www/cycle.svg", ["www/cycle.dia"],
  2160. ["cd %s; dia -e www/cycle.svg www/cycle.dia" % variantdir],
  2161. ENV=nox11_env)
  2162. packing = [
  2163. 'packaging/deb/etc_default_gpsd',
  2164. 'packaging/deb/etc_init.d_gpsd',
  2165. 'packaging/gpsd-setup.py',
  2166. 'packaging/README.PACKAGERS',
  2167. 'packaging/rpm/gpsd.init',
  2168. 'packaging/rpm/gpsd.spec',
  2169. 'packaging/rpm/gpsd.sysconfig',
  2170. 'packaging/X11/xgps.desktop',
  2171. 'packaging/X11/xgpsspeed.desktop',
  2172. ]
  2173. # Where it all comes together
  2174. build_src = [
  2175. bin_binaries,
  2176. bin_scripts,
  2177. "clients/gpsd.php",
  2178. "gpsd.rules",
  2179. icon_files,
  2180. "libgps.pc",
  2181. libraries,
  2182. manpage_targets,
  2183. packing,
  2184. sbin_binaries,
  2185. webpages,
  2186. ]
  2187. if env['python']:
  2188. build_src.append(python_targets)
  2189. build = env.Alias('build', build_src)
  2190. if [] == COMMAND_LINE_TARGETS:
  2191. # 'build' is default target
  2192. Default('build')
  2193. if qt_env:
  2194. # duplicate above?
  2195. test_qgpsmm = env.Program('tests/test_qgpsmm', ['tests/test_gpsmm.cpp'],
  2196. LIBPATH=['.'],
  2197. OBJPREFIX='qt-',
  2198. LIBS=['Qgpsmm'])
  2199. build_qt = qt_env.Alias('build', [compiled_qgpsmmlib, test_qgpsmm])
  2200. qt_env.Default(*build_qt)
  2201. testprogs.append(test_qgpsmm)
  2202. # Installation and deinstallation
  2203. # Not here because too distro-specific: udev rules, desktop files, init scripts
  2204. # It's deliberate that we don't install gpsd.h. It's full of internals that
  2205. # third-party client programs should not see.
  2206. headerinstall = [env.Install(installdir('includedir'), x)
  2207. for x in ("include/libgpsmm.h", "include/gps.h")]
  2208. binaryinstall = []
  2209. binaryinstall.append(env.Install(installdir('sbindir'), sbin_binaries))
  2210. binaryinstall.append(env.Install(installdir('bindir'), bin_binaries))
  2211. binaryinstall.append(GPSLibraryInstall(env, installdir('libdir'),
  2212. libgps_shared,
  2213. libgps_version))
  2214. # FFI library is always shared
  2215. binaryinstall.append(env.InstallVersionedLib(installdir('libdir'),
  2216. packet_ffi_shared,
  2217. SHLIBVERSION=libgps_version))
  2218. if qt_env:
  2219. binaryinstall.append(GPSLibraryInstall(qt_env, installdir('libdir'),
  2220. compiled_qgpsmmlib, libgps_version))
  2221. if ((not env['debug'] and
  2222. not env['debug_opt'] and
  2223. not env['profiling'] and
  2224. not env['nostrip'] and
  2225. not env['target_platform'].startswith('darwin'))):
  2226. env.AddPostAction(binaryinstall, '$STRIP $TARGET')
  2227. binaryinstall.append(env.Install(installdir('bindir'), bin_scripts))
  2228. python_module_dir = str(python_libdir) + os.sep + 'gps'
  2229. python_modules_install = env.Install(DESTDIR + python_module_dir,
  2230. python_modules)
  2231. python_progs_install = env.Install(installdir('bindir'), python_progs)
  2232. python_egg_info_install = env.Install(DESTDIR + str(python_libdir),
  2233. python_egg_info)
  2234. python_install = [python_modules_install,
  2235. python_progs_install,
  2236. python_egg_info_install,
  2237. # We don't need the directory explicitly for the
  2238. # install, but we do need it for the uninstall
  2239. Dir(DESTDIR + python_module_dir)]
  2240. python_lint = (python_misc + python_modules + python_progs +
  2241. ['SConstruct', 'SConscript'])
  2242. if env['python']:
  2243. # Check that Python modules compile properly
  2244. # FIXME: why not install some of the .pyc?
  2245. check_compile = []
  2246. for p in python_lint:
  2247. # split in two lines for readability
  2248. check_compile.append(
  2249. 'cp %s/%s tmp.py; %s -tt -m py_compile tmp.py;' %
  2250. (variantdir, p, target_python_path))
  2251. # tmp.py may have inherited non-writable permissions
  2252. check_compile.append('rm -f tmp.py*')
  2253. python_compilation_regress = Utility('python-compilation-regress',
  2254. python_lint, check_compile)
  2255. # get version from each python prog
  2256. # this ensures they can run and gps_versions match
  2257. vchk = ''
  2258. pp = []
  2259. for p in python_progs:
  2260. if not env['xgps_deps']:
  2261. if p in ['clients/xgps', 'clients/xgpsspeed']:
  2262. # do not have xgps* dependencies, don't test
  2263. # FIXME: make these do -V w/o dependencies.
  2264. continue
  2265. # need to run in variantdir to find libgpsdpacket
  2266. tgt = Utility(
  2267. 'version-%s' % p, p,
  2268. 'cd %s; $PYTHON %s -V' % (variantdir, p),
  2269. ENV=nox11_env)
  2270. pp.append(tgt)
  2271. python_versions = env.Alias('python-versions', pp)
  2272. else:
  2273. python_install = []
  2274. pc_install = [env.Install(installdir('pkgconfig'), 'libgps.pc')]
  2275. if qt_env:
  2276. pc_install.append(qt_env.Install(installdir('pkgconfig'), 'Qgpsmm.pc'))
  2277. pc_install.append(qt_env.Install(installdir('libdir'), 'libQgpsmm.prl'))
  2278. # icons to install
  2279. docinstall += env.Install(target=installdir('icondir'), source=icon_files)
  2280. # and now we know everything to install
  2281. install_src = (binaryinstall +
  2282. docinstall +
  2283. headerinstall +
  2284. maninstall +
  2285. pc_install +
  2286. python_install)
  2287. install = env.Alias('install', install_src)
  2288. def Uninstall(nodes):
  2289. deletes = []
  2290. for node in nodes:
  2291. if node.__class__ == install[0].__class__:
  2292. deletes.append(Uninstall(node.sources))
  2293. else:
  2294. deletes.append(Delete(str(node)))
  2295. return deletes
  2296. uninstall = env.Command('uninstall', '',
  2297. Flatten(Uninstall(Alias("install"))) or "")
  2298. env.AlwaysBuild(uninstall)
  2299. env.Precious(uninstall)
  2300. env.Alias('uninstall', uninstall)
  2301. # Target selection for '.' is badly broken. This is a general scons problem,
  2302. # not a glitch in this particular recipe. Avoid triggering the bug.
  2303. def error_action(target, source, env):
  2304. raise SCons.Error.UserError("Target selection for '.' is broken.")
  2305. AlwaysBuild(Alias(".", [], error_action))
  2306. #
  2307. # start audit checks section
  2308. #
  2309. # Putting in all these -U flags speeds up cppcheck and allows it to look
  2310. # at configurations we actually care about.
  2311. # https://github.com/danmar/cppcheck
  2312. cppcheck = Utility("cppcheck",
  2313. ['build', "include/gpsd.h", "include/packet_names.h"],
  2314. "cppcheck -U__UNUSED__ -UUSE_QT -U__COVERITY__ "
  2315. "-U__future__ "
  2316. "-ULIMITED_MAX_CLIENTS -ULIMITED_MAX_DEVICES -UAF_UNSPEC "
  2317. "-UINADDR_ANY -U_WIN32 -U__CYGWIN__ "
  2318. "-UPATH_MAX -UHAVE_STRLCAT -UHAVE_STRLCPY -UIPTOS_LOWDELAY "
  2319. "-UIPV6_TCLASS -UTCP_NODELAY -UTIOCMIWAIT --template gcc "
  2320. "--enable=all --inline-suppr "
  2321. "--suppress='*:drivers/driver_proto.c' "
  2322. "--force $SRCDIR")
  2323. # Conflicts with pycodestyle:
  2324. # E121 continuation line under-indented for hanging indent
  2325. # E123 closing bracket does not match indentation of opening bracket's line
  2326. # Conflist with gpsd style
  2327. # W504 line break after binary operator
  2328. # --exit-zero always return success, so later audits will run
  2329. flake8 = Utility("flake8", python_lint,
  2330. ['flake8 --ignore=E121,E122,E123,E241,E401,E501,W504,W602 '
  2331. '--exit-zero $SOURCES'])
  2332. # Additional Python readability style checks
  2333. # Oddly these only happen when called this way?
  2334. # E121 continuation line under-indented for hanging indent
  2335. # E123 closing bracket does not match indentation of opening bracket's line
  2336. # Conflicts with gpsd style
  2337. # W504 line break after binary operator
  2338. # exit 0 so the rest of the audit runs
  2339. pycodestyle = Utility("pep8", python_lint,
  2340. ['pycodestyle --ignore=E121,E122,E123,E241,W504,W602 '
  2341. '$SOURCES; exit 0'])
  2342. # pep8 was renamed to pycodestyle, same thing
  2343. env.Alias('pycodestyle', pycodestyle)
  2344. # Sanity-check Python code.
  2345. # Bletch. We don't really want to suppress W0231 E0602 E0611 E1123,
  2346. # but Python 3 syntax confuses a pylint running under Python 2.
  2347. # There's an internal error in astroid that requires we disable some
  2348. # auditing. This is irritating as hell but there's no help for it short
  2349. # of an upstream fix.
  2350. # --exit-zero always return success, so later audits will run
  2351. pylint = Utility("pylint", python_lint, [
  2352. 'pylint --rcfile=/dev/null --dummy-variables-rgx=\'^_\' '
  2353. '--exit-zero --msg-template='
  2354. '"{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" '
  2355. '--reports=n --disable=F0001,C0103,C0111,C1001,C0301,C0122,C0302,'
  2356. 'C0322,C0324,C0323,C0321,C0330,C0411,C0413,E1136,R0201,R0204,'
  2357. 'R0801,'
  2358. 'R0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,W0110,W0201,'
  2359. 'W0121,W0123,W0231,W0232,W0234,W0401,W0403,W0141,W0142,W0603,'
  2360. 'W0614,W0640,W0621,W1504,E0602,E0611,E1101,E1102,E1103,E1123,'
  2361. 'F0401,I0011 $SOURCES'])
  2362. # Try to make "scan-build" call the same scons
  2363. # executable that is currently executing this SConstruct.
  2364. # Check with scan-build, an analyzer, part of clang
  2365. scan_build = Utility("scan-build",
  2366. ["include/gpsd.h", "include/packet_names.h"],
  2367. "scan-build " + scons_executable_name)
  2368. env.Alias('scan_build', scan_build) # For '_' vs. '-'
  2369. # Run a valgrind audit on the daemon - not in normal tests
  2370. valgrind = Utility('valgrind', [
  2371. 'valgrind-audit.py', gpsd],
  2372. '$PYTHON $SRCDIR/valgrind-audit.py'
  2373. )
  2374. # Perform all (possible) local code-sanity checks (but not the Coverity scan).
  2375. audits = []
  2376. if have_cppcheck:
  2377. audits.append(cppcheck)
  2378. if have_flake8:
  2379. audits.append(flake8)
  2380. if have_pycodestyle:
  2381. audits.append(pycodestyle)
  2382. if have_pylint:
  2383. audits.append(pylint)
  2384. if have_scan_build:
  2385. audits.append(scan_build)
  2386. if have_valgrind:
  2387. audits.append(valgrind)
  2388. env.Alias('audit', audits)
  2389. #
  2390. # end audit checks section
  2391. #
  2392. # Regression tests begin here
  2393. #
  2394. # Note that the *-makeregress targets re-create the *.log.chk source
  2395. # files from the *.log source files.
  2396. # Unit-test the bitfield extractor
  2397. bits_regress = Utility('bits-regress', [test_bits], [
  2398. '$SRCDIR/tests/test_bits --quiet'
  2399. ])
  2400. # Unit-test the deg_to_str() converter
  2401. deg_regress = Utility('deg-regress', [test_gpsdclient], [
  2402. '$SRCDIR/tests/test_gpsdclient'
  2403. ])
  2404. # Unit-test the bitfield extractor
  2405. matrix_regress = Utility('matrix-regress', [test_matrix], [
  2406. '$SRCDIR/tests/test_matrix --quiet'
  2407. ])
  2408. # Regression-test NMEA 2000
  2409. if ((env["nmea2000"] and
  2410. have_canplayer)):
  2411. # the log files must be dependencies so they get copied into variant_dir
  2412. nmea2000_logs = Glob("test/nmea2000/*.log", strings=True)
  2413. nmea2000_tests = []
  2414. for nmea2000_log in nmea2000_logs:
  2415. # oddly this runs in build root, but needs to run in variant_dir
  2416. tgt = Utility(
  2417. 'nmea2000-regress-' + nmea2000_log[:-4],
  2418. ['tests/test_nmea2000', nmea2000_log, nmea2000_log + '.chk'],
  2419. ' cd %s; $SRCDIR/tests/test_nmea2000 %s' %
  2420. (variantdir, nmea2000_log))
  2421. nmea2000_tests.append(tgt)
  2422. nmea2000_regress = env.Alias('nmea2000-regress', nmea2000_tests)
  2423. else:
  2424. nmea2000_regress = None
  2425. if not cleaning and not helping:
  2426. announce("NMEA2000 regression tests suppressed because rtcm104v2 is off "
  2427. "or canplayer is missing.")
  2428. # using regress-drivers requires socket_export being enabled and Python
  2429. if env['socket_export'] and env['python']:
  2430. # Regression-test the daemon.
  2431. # But first dump the platform and its delay parameters.
  2432. gps_herald = Utility(
  2433. 'gps-herald', [gpsd, gpsctl, '$SRCDIR/gpsfake'],
  2434. 'cd %s; $PYTHON $PYTHON_COVERAGE $SRCDIR/gpsfake -T' % variantdir)
  2435. gps_log_pattern = "test/daemon/*.log"
  2436. gps_logs = Glob(gps_log_pattern, strings=True)
  2437. gps_tests = []
  2438. for gps_log in gps_logs:
  2439. # oddly this runs in build root, but needs to run in variant_dir
  2440. tgt = Utility(
  2441. 'gps-regress-' + gps_log[:-4],
  2442. [gps_herald, gps_log],
  2443. 'cd %s; ./regress-driver -q -o -t $REGRESSOPTS %s' %
  2444. (variantdir, gps_log))
  2445. gps_tests.append(tgt)
  2446. gps_regress = env.Alias('gps-regress', gps_tests)
  2447. # the log files must be dependencies so they get copied into variant_dir
  2448. gpsfake_logs = Glob('test/daemon/*')
  2449. # Run the passthrough log in all transport modes for better coverage
  2450. gpsfake_tests = []
  2451. for name, opts in [['pty', ''], ['udp', '-u'], ['tcp', '-o -t']]:
  2452. # oddly this runs in build root, but needs to run in variant_dir
  2453. tgt = Utility(
  2454. 'gpsfake-' + name,
  2455. [gps_herald, gpsfake_logs],
  2456. 'cd %s; ./regress-driver $REGRESSOPTS -q %s %s' %
  2457. (variantdir, opts, 'test/daemon/passthrough.log'))
  2458. gpsfake_tests.append(tgt)
  2459. env.Alias('gpsfake-tests', gpsfake_tests)
  2460. # Build the regression tests for the daemon.
  2461. # Note: You'll have to do this whenever the default leap second
  2462. # changes in gpsd.h. Many drivers rely on the default until they
  2463. # get the current leap second.
  2464. gps_rebuilds = []
  2465. for gps_log in gps_logs:
  2466. # oddly this runs in build root, but needs to run in variant_dir
  2467. gps_rebuilds.append(Utility(
  2468. 'gps-makeregress-' + gps_log[:-4],
  2469. [gps_herald, gps_log],
  2470. 'cd %s; ./regress-driver -bq -o -t '
  2471. '$REGRESSOPTS %s ' % (variantdir, gps_log)))
  2472. if GetOption('num_jobs') <= 1:
  2473. Utility('gps-makeregress', gps_herald,
  2474. 'cd %s; ./regress-driver -b $REGRESSOPTS %s' %
  2475. (variantdir, gps_log_pattern))
  2476. else:
  2477. env.Alias('gps-makeregress', gps_rebuilds)
  2478. else:
  2479. announce("GPS regression tests suppressed because socket_export "
  2480. "or python is off.")
  2481. gps_regress = None
  2482. gpsfake_tests = None
  2483. # To build an individual test for a load named foo.log, put it in
  2484. # test/daemon and do this:
  2485. # regress-driver -b test/daemon/foo.log
  2486. # Regression-test the RTCM decoder.
  2487. if env["rtcm104v2"]:
  2488. rtcm2_logs = ['test/sample.rtcm2', 'test/sample.rtcm2.chk']
  2489. # the log files must be dependencies so they get copied into variant_dir
  2490. rtcm_regress = Utility('rtcm-regress', [gpsdecode, rtcm2_logs], [
  2491. '@echo "Testing RTCM decoding..."',
  2492. '@for f in $SRCDIR/test/*.rtcm2; do '
  2493. ' echo "\tTesting $${f}..."; '
  2494. ' TMPFILE=`mktemp -t gpsd-test.chk-XXXXXXXXXXXXXX`; '
  2495. ' $SRCDIR/clients/gpsdecode -u -j <$${f} >$${TMPFILE}; '
  2496. ' diff -ub $${f}.chk $${TMPFILE} || echo "Test FAILED!"; '
  2497. ' rm -f $${TMPFILE}; '
  2498. 'done;',
  2499. '@echo "Testing idempotency of JSON dump/decode for RTCM2"',
  2500. '@TMPFILE=`mktemp -t gpsd-test.chk-XXXXXXXXXXXXXX`; '
  2501. '$SRCDIR/clients/gpsdecode -u -e -j <test/synthetic-rtcm2.json '
  2502. ' >$${TMPFILE}; '
  2503. ' grep -v "^#" test/synthetic-rtcm2.json | diff -ub - $${TMPFILE} '
  2504. ' || echo "Test FAILED!"; '
  2505. ' rm -f $${TMPFILE}; ',
  2506. ])
  2507. else:
  2508. announce("RTCM2 regression tests suppressed because rtcm104v2 is off.")
  2509. rtcm_regress = None
  2510. # Rebuild the RTCM regression tests.
  2511. Utility('rtcm-makeregress', [gpsdecode], [
  2512. 'for f in $SRCDIR/test/*.rtcm2; do '
  2513. ' $SRCDIR/clients/gpsdecode -j <$${f} >$${f}.chk; '
  2514. 'done'
  2515. ])
  2516. # Regression-test the AIVDM decoder.
  2517. aivdm_logs = ['test/sample.aivdm', 'test/sample.aivdm.chk',
  2518. 'test/sample.aivdm.js.chk', 'test/sample.aivdm.ju.chk']
  2519. if env["aivdm"]:
  2520. # the log files must be dependencies so they get copied into variant_dir
  2521. # FIXME! Does not return a proper fail code
  2522. aivdm_regress = Utility('aivdm-regress', [gpsdecode, aivdm_logs], [
  2523. '@echo "Testing AIVDM decoding w/ CSV format..."',
  2524. '@for f in $SRCDIR/test/*.aivdm; do '
  2525. ' echo "\tTesting $${f}..."; '
  2526. ' TMPFILE=`mktemp -t gpsd-test.chk-XXXXXXXXXXXXXX`; '
  2527. ' $SRCDIR/clients/gpsdecode -u -c <$${f} >$${TMPFILE}; '
  2528. ' diff -ub $${f}.chk $${TMPFILE} || echo "Test FAILED!"; '
  2529. ' rm -f $${TMPFILE}; '
  2530. 'done;',
  2531. '@echo "Testing AIVDM decoding w/ JSON unscaled format..."',
  2532. '@for f in $SRCDIR/test/*.aivdm; do '
  2533. ' echo "\tTesting $${f}..."; '
  2534. ' TMPFILE=`mktemp -t gpsd-test.chk-XXXXXXXXXXXXXX`; '
  2535. ' $SRCDIR/clients/gpsdecode -u -j <$${f} >$${TMPFILE}; '
  2536. ' diff -ub $${f}.ju.chk $${TMPFILE} || echo "Test FAILED!"; '
  2537. ' rm -f $${TMPFILE}; '
  2538. 'done;',
  2539. '@echo "Testing AIVDM decoding w/ JSON scaled format..."',
  2540. '@for f in $SRCDIR/test/*.aivdm; do '
  2541. ' echo "\tTesting $${f}..."; '
  2542. ' TMPFILE=`mktemp -t gpsd-test.chk-XXXXXXXXXXXXXX`; '
  2543. ' $SRCDIR/clients/gpsdecode -j <$${f} >$${TMPFILE}; '
  2544. ' diff -ub $${f}.js.chk $${TMPFILE} || echo "Test FAILED!"; '
  2545. ' rm -f $${TMPFILE}; '
  2546. 'done;',
  2547. '@echo "Testing idempotency of unscaled JSON dump/decode for AIS"',
  2548. '@TMPFILE=`mktemp -t gpsd-test.chk-XXXXXXXXXXXXXX`; '
  2549. '$SRCDIR/clients/gpsdecode -u -e -j <$SRCDIR/test/sample.aivdm.ju.chk '
  2550. ' >$${TMPFILE}; '
  2551. ' grep -v "^#" $SRCDIR/test/sample.aivdm.ju.chk '
  2552. ' | diff -ub - $${TMPFILE} || echo "Test FAILED!"; '
  2553. ' rm -f $${TMPFILE}; ',
  2554. # Parse the unscaled json reference, dump it as scaled json,
  2555. # and finally compare it with the scaled json reference
  2556. '@echo "Testing idempotency of scaled JSON dump/decode for AIS"',
  2557. '@TMPFILE=`mktemp -t gpsd-test.chk-XXXXXXXXXXXXXX`; '
  2558. '$SRCDIR/clients/gpsdecode -e -j <$SRCDIR/test/sample.aivdm.ju.chk '
  2559. ' >$${TMPFILE};'
  2560. ' grep -v "^#" $SRCDIR/test/sample.aivdm.js.chk '
  2561. ' | diff -ub - $${TMPFILE} || echo "Test FAILED!"; '
  2562. ' rm -f $${TMPFILE}; ',
  2563. ])
  2564. else:
  2565. announce("AIVDM regression tests suppressed because aivdm is off.")
  2566. aivdm_regress = None
  2567. # Rebuild the AIVDM regression tests.
  2568. # Use root dir copies so the new .chk is back into root.
  2569. Utility('aivdm-makeregress', [gpsdecode], [
  2570. 'for f in $SRCDIR/../test/*.aivdm; do '
  2571. ' $SRCDIR/clients/gpsdecode -u -c <$${f} > $${f}.chk; '
  2572. ' $SRCDIR/clients/gpsdecode -u -j <$${f} > $${f}.ju.chk; '
  2573. ' $SRCDIR/clients/gpsdecode -j <$${f} > $${f}.js.chk; '
  2574. 'done', ])
  2575. # Regression-test the packet getter.
  2576. packet_regress = UtilityWithHerald(
  2577. 'Testing detection of invalid packets...',
  2578. 'packet-regress', [test_packet],
  2579. ['$SRCDIR/tests/test_packet | diff -u test/packet.test.chk -', ])
  2580. # Rebuild the packet-getter regression test
  2581. Utility('packet-makeregress', [test_packet], [
  2582. '$SRCDIR/tests/test_packet > test/packet.test.chk', ])
  2583. # Regression-test the geoid and variation tester.
  2584. geoid_regress = UtilityWithHerald(
  2585. 'Testing the geoid and variation models...',
  2586. 'geoid-regress', [test_geoid], ['$SRCDIR/tests/test_geoid'])
  2587. # Regression-test the calendar functions
  2588. time_regress = Utility('time-regress', [test_mktime], [
  2589. '$SRCDIR/tests/test_mktime'
  2590. ])
  2591. # Regression test the unpacking code in libgps
  2592. # the log files must be dependencies so they get copied into variant_dir
  2593. clientlib_logs = ['test/clientlib/multipacket.log',
  2594. 'test/clientlib/multipacket.log.chk']
  2595. unpack_regress = UtilityWithHerald(
  2596. 'Testing the client-library sentence decoder...',
  2597. 'unpack-regress', [test_libgps, 'regress-driver', clientlib_logs], [
  2598. '$SRCDIR/regress-driver $REGRESSOPTS -c'
  2599. ' $SRCDIR/test/clientlib/*.log', ])
  2600. # Unit-test the bitfield extractor
  2601. misc_regress = Utility('misc-regress', [
  2602. 'tests/test_clienthelpers.py',
  2603. 'tests/test_misc.py', ],
  2604. [
  2605. 'cd %s; %s tests/test_clienthelpers.py' %
  2606. (variantdir, target_python_path),
  2607. 'cd %s; %s tests/test_misc.py' % (variantdir, target_python_path), ])
  2608. # Build the regression test for the sentence unpacker
  2609. Utility('unpack-makeregress', [test_libgps], [
  2610. '@echo "Rebuilding the client sentence-unpacker tests..."',
  2611. '$SRCDIR/regress-driver $REGRESSOPTS -c -b $SRCDIR/test/clientlib/*.log'
  2612. ])
  2613. # Unit-test the JSON parsing
  2614. if env['socket_export']:
  2615. json_regress = Utility('json-regress', [test_json],
  2616. ['$SRCDIR/tests/test_json'])
  2617. else:
  2618. json_regress = None
  2619. # Unit-test timespec math
  2620. timespec_regress = Utility('timespec-regress', [test_timespec], [
  2621. '$SRCDIR/tests/test_timespec'
  2622. ])
  2623. # Unit-test float math
  2624. float_regress = Utility('float-regress', [test_float], [
  2625. '$SRCDIR/tests/test_float'
  2626. ])
  2627. # Unit-test trig math
  2628. trig_regress = Utility('trig-regress', [test_trig], [
  2629. '$SRCDIR/tests/test_trig'
  2630. ])
  2631. # consistency-check the driver methods
  2632. method_regress = UtilityWithHerald(
  2633. 'Consistency-checking driver methods...',
  2634. 'method-regress', [test_packet], [
  2635. '$SRCDIR/tests/test_packet -c >/dev/null', ])
  2636. # Test the xgps/xgpsspeed dependencies
  2637. if env['xgps_deps']:
  2638. test_xgps_deps = UtilityWithHerald(
  2639. 'Testing xgps/xgpsspeed dependencies (since xgps=yes)...',
  2640. 'test-xgps-deps', ['$SRCDIR/tests/test_xgps_deps.py'], [
  2641. '$PYTHON $SRCDIR/tests/test_xgps_deps.py'])
  2642. else:
  2643. test_xgps_deps = None
  2644. # Run test builds on remote machines
  2645. flocktest = Utility("flocktest", [], "cd devtools; ./flocktest " + gitrepo)
  2646. # Run all normal regression tests
  2647. describe = UtilityWithHerald(
  2648. 'Run normal regression tests for %s...' % gpsd_revision.strip(),
  2649. 'describe', [], [])
  2650. # Delete all test programs
  2651. testclean = Utility('testclean', [], 'rm -fr %s/tests' % variantdir)
  2652. test_nondaemon = [
  2653. aivdm_regress,
  2654. bits_regress,
  2655. deg_regress,
  2656. describe,
  2657. float_regress,
  2658. geoid_regress,
  2659. json_regress,
  2660. matrix_regress,
  2661. method_regress,
  2662. packet_regress,
  2663. rtcm_regress,
  2664. test_xgps_deps,
  2665. time_regress,
  2666. timespec_regress,
  2667. # trig_regress, # not ready
  2668. ]
  2669. if env['python']:
  2670. test_nondaemon.append(misc_regress)
  2671. test_nondaemon.append(python_compilation_regress)
  2672. test_nondaemon.append(python_versions)
  2673. test_nondaemon.append(unpack_regress)
  2674. if env['socket_export']:
  2675. test_nondaemon.append(test_json)
  2676. if env['libgpsmm']:
  2677. test_nondaemon.append(test_gpsmm)
  2678. if qt_env:
  2679. test_nondaemon.append(test_qgpsmm)
  2680. test_quick = test_nondaemon + [gpsfake_tests]
  2681. test_noclean = test_quick + [nmea2000_regress, gps_regress]
  2682. env.Alias('test-nondaemon', test_nondaemon)
  2683. env.Alias('test-quick', test_quick)
  2684. check = env.Alias('check', test_noclean)
  2685. env.Alias('testregress', check)
  2686. env.Alias('build-tests', testprogs)
  2687. build_all = env.Alias('build-all', build + testprogs)
  2688. # Remove all shared-memory segments. Normally only needs to be run
  2689. # when a segment size changes.
  2690. shmclean = Utility('shmclean', [], ["ipcrm -M 0x4e545030;"
  2691. "ipcrm -M 0x4e545031;"
  2692. "ipcrm -M 0x4e545032;"
  2693. "ipcrm -M 0x4e545033;"
  2694. "ipcrm -M 0x4e545034;"
  2695. "ipcrm -M 0x4e545035;"
  2696. "ipcrm -M 0x4e545036;"
  2697. "ipcrm -M 0x47505345;"
  2698. ])
  2699. # The website directory
  2700. #
  2701. # None of these productions are fired by default.
  2702. # The content they handle is the GPSD website, not included in
  2703. # release tarballs.
  2704. # Documentation
  2705. # Paste 'scons --quiet validation-list' to a batch validator such as
  2706. # http://htmlhelp.com/tools/validator/batch.html.en
  2707. def validation_list(target, source, env):
  2708. for page in glob.glob("www/*.html"):
  2709. if '-head' not in page:
  2710. fp = open(page)
  2711. if "Valid HTML" in fp.read():
  2712. print(os.path.join(website, os.path.basename(page)))
  2713. fp.close()
  2714. Utility("validation-list", [www], validation_list)
  2715. # Experimenting with pydoc. Not yet fired by any other productions.
  2716. # scons www/ dies with this
  2717. # # if env['python']:
  2718. # # env.Alias('pydoc', "www/pydoc/index.html")
  2719. # #
  2720. # # # We need to run epydoc with the Python version the modules built for.
  2721. # # # So we define our own epydoc instead of using /usr/bin/epydoc
  2722. # # EPYDOC = "python -c 'from epydoc.cli import cli; cli()'"
  2723. # # env.Command('www/pydoc/index.html', python_progs + glob.glob("*.py")
  2724. # # + glob.glob("gps/*.py"), [
  2725. # # 'mkdir -p www/pydoc',
  2726. # # EPYDOC + " -v --html --graph all -n GPSD $SOURCES -o www/pydoc",
  2727. # # ])
  2728. # Productions for setting up and performing udev tests.
  2729. #
  2730. # Requires root. Do "udev-install", then "tail -f /var/log/syslog" in
  2731. # another window, then run 'scons udev-test', then plug and unplug the
  2732. # GPS ad libitum. All is well when you get fix reports each time a GPS
  2733. # is plugged in.
  2734. #
  2735. # In case you are a systemd user you might also need to watch the
  2736. # journalctl output. Instead of the hotplug script the gpsdctl@.service
  2737. # unit will handle hotplugging together with the udev rules.
  2738. #
  2739. # Note that a udev event can be triggered with an invocation like:
  2740. # udevadm trigger --sysname-match=ttyUSB0 --action add
  2741. if env['systemd']:
  2742. systemdinstall_target = [env.Install(DESTDIR + env['unitdir'],
  2743. "systemd/%s" % (x,)) for x in
  2744. ("gpsdctl@.service", "gpsd.service",
  2745. "gpsd.socket")]
  2746. systemd_install = env.Alias('systemd_install', systemdinstall_target)
  2747. systemd_uninstall = env.Command(
  2748. 'systemd_uninstall', '',
  2749. Flatten(Uninstall(Alias("systemd_install"))) or "")
  2750. env.AlwaysBuild(systemd_uninstall)
  2751. env.Precious(systemd_uninstall)
  2752. hotplug_wrapper_install = []
  2753. else:
  2754. hotplug_wrapper_install = [
  2755. 'cp $SRCDIR/../gpsd.hotplug ' + DESTDIR + env['udevdir'],
  2756. 'chmod a+x ' + DESTDIR + env['udevdir'] + '/gpsd.hotplug'
  2757. ]
  2758. udev_install = Utility('udev-install', 'install', [
  2759. 'mkdir -p ' + DESTDIR + env['udevdir'] + '/rules.d',
  2760. 'cp $SRCDIR/gpsd.rules ' + DESTDIR + env['udevdir'] +
  2761. '/rules.d/25-gpsd.rules', ] + hotplug_wrapper_install)
  2762. if env['systemd']:
  2763. env.Requires(udev_install, systemd_install)
  2764. if not env["sysroot"]:
  2765. systemctl_daemon_reload = Utility('systemctl-daemon-reload', '',
  2766. ['systemctl daemon-reload || true'])
  2767. env.AlwaysBuild(systemctl_daemon_reload)
  2768. env.Precious(systemctl_daemon_reload)
  2769. env.Requires(systemctl_daemon_reload, systemd_install)
  2770. env.Requires(udev_install, systemctl_daemon_reload)
  2771. Utility('udev-uninstall', '', [
  2772. 'rm -f %s/gpsd.hotplug' % env['udevdir'],
  2773. 'rm -f %s/rules.d/25-gpsd.rules' % env['udevdir'],
  2774. ])
  2775. Utility('udev-test', '',
  2776. ['$SRCDIR/gpsd/gpsd -N -n -F /var/run/gpsd.sock -D 5', ])
  2777. # Default targets
  2778. if not cleaning:
  2779. # FIXME: redundant?
  2780. env.Default(build)
  2781. # Tags for Emacs and vi
  2782. misc_sources = ['clients/cgps.c',
  2783. 'clients/gps2udp.c',
  2784. 'clients/gpsdctl.c',
  2785. 'clients/gpsdecode.c',
  2786. 'clients/gpspipe.c',
  2787. 'clients/gpxlogger.c',
  2788. 'clients/ntpshmmon.c',
  2789. 'clients/ppscheck.c',
  2790. 'gpsctl.c',
  2791. ]
  2792. sources = libgpsd_sources + libgps_sources + gpsd_sources + gpsmon_sources + \
  2793. misc_sources
  2794. env.Command('#TAGS', sources, ['etags ' + " ".join(sources)])
  2795. # Release machinery begins here
  2796. #
  2797. # We need to be in the actual project repo (i.e. not doing a -Y build)
  2798. # for these productions to work.
  2799. distfiles.sort()
  2800. # remove git and CI stuff from files to tar/zip
  2801. distfiles_ignore = [
  2802. ".ci-build/build.sh",
  2803. ".ci-build/test_options.sh",
  2804. ".gitignore",
  2805. ".gitlab-ci.yml",
  2806. ".travis.yml",
  2807. # remove contrib/ais-samples
  2808. "contrib/ais-samples/ais-nmea-sample.log",
  2809. "contrib/ais-samples/ais-nmea-sample.log.chk", ]
  2810. for fn in distfiles_ignore:
  2811. if fn in distfiles:
  2812. distfiles.remove(fn)
  2813. # tar balls do not need all generated files
  2814. # tar balls do need packaging
  2815. for f in packing:
  2816. if f not in distfiles:
  2817. # should not be in git, generated file, we need it
  2818. distfiles.append(f)
  2819. # zip archive
  2820. target = '#gpsd-${VERSION}.zip'
  2821. dozip = env.Zip(target, distfiles)
  2822. ziptgt = env.Alias('zip', dozip)
  2823. if have_tar:
  2824. target = '#gpsd-%s.tar' % gpsd_version
  2825. # .tar.gz archive
  2826. gzenv = Environment(TARFLAGS='-c -z')
  2827. targz = gzenv.Tar(target + '.gz', distfiles)
  2828. # .tar.xz archive
  2829. xzenv = Environment(TARFLAGS='-c -J')
  2830. tarxz = xzenv.Tar(target + '.xz', distfiles)
  2831. env.Alias('tar', [targz, tarxz])
  2832. env.Alias('dist', [ziptgt, targz, tarxz])
  2833. Clean('build', [targz, tarxz, dozip])
  2834. # Make sure build-from-tarball works.
  2835. # Use possibly nonstandard name for scons
  2836. scons_cmd = [scons_executable_name]
  2837. # Inherit selected options from this scons run
  2838. if GetOption('silent'):
  2839. scons_cmd.append('-s')
  2840. if GetOption('no_progress'): # Undocumented name
  2841. scons_cmd.append('-Q')
  2842. njobs = GetOption('num_jobs')
  2843. if njobs != 1:
  2844. scons_cmd.append('-j%d' % njobs)
  2845. testbuild = Utility('testbuild', [targz], [
  2846. 'rm -Rf testbuild',
  2847. 'mkdir testbuild',
  2848. 'cd testbuild;'
  2849. 'pwd;'
  2850. '${TAR} -xzvf ../gpsd-${VERSION}.tar.gz;'
  2851. 'cd gpsd-${VERSION}; %s;' % ' '.join(scons_cmd),
  2852. ])
  2853. releasecheck = env.Alias('releasecheck', [
  2854. testbuild,
  2855. check,
  2856. audits,
  2857. flocktest,
  2858. ])
  2859. # The chmod copes with the fact that scp will give a
  2860. # replacement the permissions of the *original*...
  2861. upload_release = Utility('upload-release', ['dist'], [
  2862. 'rm -f gpsd-*.sig',
  2863. 'gpg -b gpsd-${VERSION}.tar.gz',
  2864. 'gpg -b gpsd-${VERSION}.tar.xz',
  2865. 'gpg -b gpsd-${VERSION}.zip',
  2866. 'chmod ug=rw,o=r gpsd-${VERSION}.tar.* gpsd-${VERSION}.zip',
  2867. 'scp gpsd-${VERSION}.tar.* gpsd-${VERSION}.zip* ' + scpupload,
  2868. ])
  2869. env.Alias('upload_release', upload_release) # For '_' vs. '-'
  2870. # How to tag a release
  2871. tag_release = Utility('tag-release', [], [
  2872. 'git tag -s -m "Tagged for external release ${VERSION}" \
  2873. release-${VERSION}'])
  2874. env.Alias('tag_release', tag_release) # For '_' vs. '-'
  2875. upload_tags = Utility('upload-tags', [], ['git push --tags'])
  2876. env.Alias('upload_tags', upload_tags) # For '_' vs. '-'
  2877. # Local release preparation. This production will require Internet access,
  2878. # but it doesn't do any uploads or public repo mods.
  2879. #
  2880. # Note that tag_release has to fire early, otherwise the value of REVISION
  2881. # won't be right when gpsd_config.h is generated for the tarball.
  2882. # FIXME: this is confused
  2883. releaseprep = env.Alias("releaseprep",
  2884. [Utility("distclean", [],
  2885. ["rm -f include/gpsd_config.h"]),
  2886. tag_release,
  2887. 'dist'])
  2888. # How to update the website. Assumes a local GitLab pages setup.
  2889. # See "pages:" in .gitlab-ci.yml
  2890. www_dest = os.environ.get('WEBSITE', '.public')
  2891. website = Utility("website", www,
  2892. 'rsync --exclude="*.in" -avz buildtmp/www/ %s ' % www_dest)
  2893. # All a buildup to this.
  2894. env.Alias("release", [releaseprep,
  2895. upload_release,
  2896. upload_tags,
  2897. website])
  2898. # Undo local release preparation
  2899. undoprep = Utility("undoprep", [],
  2900. ['rm -f gpsd-${VERSION}.tar.?z',
  2901. 'rm -f gpsd-$VERSION}.zip',
  2902. 'git tag -d release-${VERSION};'])
  2903. #######
  2904. # start Debian stuff
  2905. #######
  2906. # Make RPM from the specfile in packaging
  2907. # untested
  2908. dist_rpm = Utility('dist-rpm', 'dist', 'rpmbuild -ta gpsd-${VERSION}.tar.gz')
  2909. env.Pseudo(dist_rpm) # mark as fake target.
  2910. env.Alias('distrpm', dist_rpm) # For '_' vs. '-'
  2911. # Experimental release mechanics using shipper
  2912. # This will ship a freecode metadata update
  2913. # untested
  2914. ship = Utility("ship", ['dist', "control"],
  2915. ['cd %s; shipper version=%s | sh -e -x' %
  2916. (variantdir, gpsd_version)])
  2917. #######
  2918. # end Debian stuff
  2919. #######
  2920. # Release machinery ends here
  2921. # The following sets edit modes for GNU EMACS
  2922. # Local Variables:
  2923. # mode:python
  2924. # End:
  2925. # vim: set expandtab shiftwidth=4