SConstruct 109 KB

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