jolt 0.9.123__py3-none-any.whl → 0.9.435__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. jolt/__init__.py +80 -7
  2. jolt/__main__.py +9 -1
  3. jolt/bin/fstree-darwin-x86_64 +0 -0
  4. jolt/bin/fstree-linux-x86_64 +0 -0
  5. jolt/cache.py +832 -362
  6. jolt/chroot.py +156 -0
  7. jolt/cli.py +281 -162
  8. jolt/common_pb2.py +63 -0
  9. jolt/common_pb2_grpc.py +4 -0
  10. jolt/config.py +98 -41
  11. jolt/error.py +19 -4
  12. jolt/filesystem.py +2 -6
  13. jolt/graph.py +705 -117
  14. jolt/hooks.py +43 -0
  15. jolt/influence.py +122 -3
  16. jolt/loader.py +369 -121
  17. jolt/log.py +225 -63
  18. jolt/manifest.py +28 -38
  19. jolt/options.py +35 -10
  20. jolt/pkgs/abseil.py +42 -0
  21. jolt/pkgs/asio.py +25 -0
  22. jolt/pkgs/autoconf.py +41 -0
  23. jolt/pkgs/automake.py +41 -0
  24. jolt/pkgs/b2.py +31 -0
  25. jolt/pkgs/boost.py +111 -0
  26. jolt/pkgs/boringssl.py +32 -0
  27. jolt/pkgs/busybox.py +39 -0
  28. jolt/pkgs/bzip2.py +43 -0
  29. jolt/pkgs/cares.py +29 -0
  30. jolt/pkgs/catch2.py +36 -0
  31. jolt/pkgs/cbindgen.py +17 -0
  32. jolt/pkgs/cista.py +19 -0
  33. jolt/pkgs/clang.py +44 -0
  34. jolt/pkgs/cli11.py +24 -0
  35. jolt/pkgs/cmake.py +48 -0
  36. jolt/pkgs/cpython.py +196 -0
  37. jolt/pkgs/crun.py +29 -0
  38. jolt/pkgs/curl.py +38 -0
  39. jolt/pkgs/dbus.py +18 -0
  40. jolt/pkgs/double_conversion.py +24 -0
  41. jolt/pkgs/fastfloat.py +21 -0
  42. jolt/pkgs/ffmpeg.py +28 -0
  43. jolt/pkgs/flatbuffers.py +29 -0
  44. jolt/pkgs/fmt.py +27 -0
  45. jolt/pkgs/fstree.py +20 -0
  46. jolt/pkgs/gflags.py +18 -0
  47. jolt/pkgs/glib.py +18 -0
  48. jolt/pkgs/glog.py +25 -0
  49. jolt/pkgs/glslang.py +21 -0
  50. jolt/pkgs/golang.py +16 -11
  51. jolt/pkgs/googlebenchmark.py +18 -0
  52. jolt/pkgs/googletest.py +46 -0
  53. jolt/pkgs/gperf.py +15 -0
  54. jolt/pkgs/grpc.py +73 -0
  55. jolt/pkgs/hdf5.py +19 -0
  56. jolt/pkgs/help2man.py +14 -0
  57. jolt/pkgs/inja.py +28 -0
  58. jolt/pkgs/jsoncpp.py +31 -0
  59. jolt/pkgs/libarchive.py +43 -0
  60. jolt/pkgs/libcap.py +44 -0
  61. jolt/pkgs/libdrm.py +44 -0
  62. jolt/pkgs/libedit.py +42 -0
  63. jolt/pkgs/libevent.py +31 -0
  64. jolt/pkgs/libexpat.py +27 -0
  65. jolt/pkgs/libfastjson.py +21 -0
  66. jolt/pkgs/libffi.py +16 -0
  67. jolt/pkgs/libglvnd.py +30 -0
  68. jolt/pkgs/libogg.py +28 -0
  69. jolt/pkgs/libpciaccess.py +18 -0
  70. jolt/pkgs/libseccomp.py +21 -0
  71. jolt/pkgs/libtirpc.py +24 -0
  72. jolt/pkgs/libtool.py +42 -0
  73. jolt/pkgs/libunwind.py +35 -0
  74. jolt/pkgs/libva.py +18 -0
  75. jolt/pkgs/libvorbis.py +33 -0
  76. jolt/pkgs/libxml2.py +35 -0
  77. jolt/pkgs/libxslt.py +17 -0
  78. jolt/pkgs/libyajl.py +16 -0
  79. jolt/pkgs/llvm.py +81 -0
  80. jolt/pkgs/lua.py +54 -0
  81. jolt/pkgs/lz4.py +26 -0
  82. jolt/pkgs/m4.py +14 -0
  83. jolt/pkgs/make.py +17 -0
  84. jolt/pkgs/mesa.py +81 -0
  85. jolt/pkgs/meson.py +17 -0
  86. jolt/pkgs/mstch.py +28 -0
  87. jolt/pkgs/mysql.py +60 -0
  88. jolt/pkgs/nasm.py +49 -0
  89. jolt/pkgs/ncurses.py +30 -0
  90. jolt/pkgs/ng_log.py +25 -0
  91. jolt/pkgs/ninja.py +45 -0
  92. jolt/pkgs/nlohmann_json.py +25 -0
  93. jolt/pkgs/nodejs.py +19 -11
  94. jolt/pkgs/opencv.py +24 -0
  95. jolt/pkgs/openjdk.py +26 -0
  96. jolt/pkgs/openssl.py +103 -0
  97. jolt/pkgs/paho.py +76 -0
  98. jolt/pkgs/patchelf.py +16 -0
  99. jolt/pkgs/perl.py +42 -0
  100. jolt/pkgs/pkgconfig.py +64 -0
  101. jolt/pkgs/poco.py +39 -0
  102. jolt/pkgs/protobuf.py +77 -0
  103. jolt/pkgs/pugixml.py +27 -0
  104. jolt/pkgs/python.py +19 -0
  105. jolt/pkgs/qt.py +35 -0
  106. jolt/pkgs/rapidjson.py +26 -0
  107. jolt/pkgs/rapidyaml.py +28 -0
  108. jolt/pkgs/re2.py +30 -0
  109. jolt/pkgs/re2c.py +17 -0
  110. jolt/pkgs/readline.py +15 -0
  111. jolt/pkgs/rust.py +41 -0
  112. jolt/pkgs/sdl.py +28 -0
  113. jolt/pkgs/simdjson.py +27 -0
  114. jolt/pkgs/soci.py +46 -0
  115. jolt/pkgs/spdlog.py +29 -0
  116. jolt/pkgs/spirv_llvm.py +21 -0
  117. jolt/pkgs/spirv_tools.py +24 -0
  118. jolt/pkgs/sqlite.py +83 -0
  119. jolt/pkgs/ssl.py +12 -0
  120. jolt/pkgs/texinfo.py +15 -0
  121. jolt/pkgs/tomlplusplus.py +22 -0
  122. jolt/pkgs/wayland.py +26 -0
  123. jolt/pkgs/x11.py +58 -0
  124. jolt/pkgs/xerces_c.py +20 -0
  125. jolt/pkgs/xorg.py +360 -0
  126. jolt/pkgs/xz.py +29 -0
  127. jolt/pkgs/yamlcpp.py +30 -0
  128. jolt/pkgs/zeromq.py +47 -0
  129. jolt/pkgs/zlib.py +87 -0
  130. jolt/pkgs/zstd.py +33 -0
  131. jolt/plugins/alias.py +3 -0
  132. jolt/plugins/allure.py +5 -2
  133. jolt/plugins/autotools.py +66 -0
  134. jolt/plugins/cache.py +133 -0
  135. jolt/plugins/cmake.py +74 -6
  136. jolt/plugins/conan.py +238 -0
  137. jolt/plugins/cxx.py +698 -0
  138. jolt/plugins/cxxinfo.py +7 -0
  139. jolt/plugins/dashboard.py +1 -1
  140. jolt/plugins/docker.py +80 -23
  141. jolt/plugins/email.py +2 -2
  142. jolt/plugins/email.xslt +144 -101
  143. jolt/plugins/environ.py +11 -0
  144. jolt/plugins/fetch.py +141 -0
  145. jolt/plugins/gdb.py +39 -19
  146. jolt/plugins/gerrit.py +1 -14
  147. jolt/plugins/git.py +283 -85
  148. jolt/plugins/googletest.py +2 -1
  149. jolt/plugins/http.py +36 -38
  150. jolt/plugins/libtool.py +63 -0
  151. jolt/plugins/linux.py +990 -0
  152. jolt/plugins/logstash.py +4 -4
  153. jolt/plugins/meson.py +61 -0
  154. jolt/plugins/ninja-compdb.py +99 -30
  155. jolt/plugins/ninja.py +468 -166
  156. jolt/plugins/paths.py +11 -1
  157. jolt/plugins/pkgconfig.py +219 -0
  158. jolt/plugins/podman.py +136 -92
  159. jolt/plugins/python.py +137 -0
  160. jolt/plugins/remote_execution/__init__.py +0 -0
  161. jolt/plugins/remote_execution/administration_pb2.py +46 -0
  162. jolt/plugins/remote_execution/administration_pb2_grpc.py +170 -0
  163. jolt/plugins/remote_execution/log_pb2.py +32 -0
  164. jolt/plugins/remote_execution/log_pb2_grpc.py +68 -0
  165. jolt/plugins/remote_execution/scheduler_pb2.py +41 -0
  166. jolt/plugins/remote_execution/scheduler_pb2_grpc.py +141 -0
  167. jolt/plugins/remote_execution/worker_pb2.py +38 -0
  168. jolt/plugins/remote_execution/worker_pb2_grpc.py +112 -0
  169. jolt/plugins/report.py +12 -2
  170. jolt/plugins/rust.py +25 -0
  171. jolt/plugins/scheduler.py +710 -0
  172. jolt/plugins/selfdeploy/setup.py +8 -4
  173. jolt/plugins/selfdeploy.py +138 -88
  174. jolt/plugins/strings.py +35 -22
  175. jolt/plugins/symlinks.py +26 -11
  176. jolt/plugins/telemetry.py +5 -2
  177. jolt/plugins/timeline.py +13 -3
  178. jolt/plugins/volume.py +46 -48
  179. jolt/scheduler.py +589 -192
  180. jolt/tasks.py +625 -121
  181. jolt/templates/timeline.html.template +44 -47
  182. jolt/timer.py +22 -0
  183. jolt/tools.py +638 -282
  184. jolt/utils.py +211 -7
  185. jolt/version.py +1 -1
  186. jolt/xmldom.py +12 -2
  187. {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/METADATA +97 -38
  188. jolt-0.9.435.dist-info/RECORD +207 -0
  189. {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/WHEEL +1 -1
  190. jolt/plugins/amqp.py +0 -834
  191. jolt/plugins/debian.py +0 -338
  192. jolt/plugins/ftp.py +0 -181
  193. jolt/plugins/repo.py +0 -253
  194. jolt-0.9.123.dist-info/RECORD +0 -77
  195. {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/entry_points.txt +0 -0
  196. {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/top_level.txt +0 -0
jolt/plugins/ninja.py CHANGED
@@ -1,8 +1,11 @@
1
+ from collections import OrderedDict
1
2
  import contextlib
2
3
  import copy
3
4
  import functools
4
- import ninja_syntax as ninja
5
+ from ninja import ninja_syntax as ninja
5
6
  import os
7
+ import platform
8
+ import re
6
9
  import sys
7
10
 
8
11
  from jolt.tasks import Task, attributes as task_attributes
@@ -10,7 +13,6 @@ from jolt import config
10
13
  from jolt.influence import attribute as influence_attribute
11
14
  from jolt.influence import DirectoryInfluence, FileInfluence
12
15
  from jolt.influence import HashInfluenceProvider, TaskAttributeInfluence
13
- from jolt.loader import get_workspacedir
14
16
  from jolt.config import get_cachedir
15
17
  from jolt import log
16
18
  from jolt import utils
@@ -20,9 +22,18 @@ from jolt.error import raise_task_error_if
20
22
  from jolt.error import JoltError, JoltCommandError
21
23
 
22
24
 
25
+ c_standard_default = None
26
+ cxx_standard_default = None
27
+ c_standards_list = [90, 99, 11, 17, 23]
28
+ cxx_standards_list = [98, 11, 14, 17, 20, 23, 26]
29
+
30
+
23
31
  class CompileError(JoltError):
24
- def __init__(self):
25
- super().__init__("Compilation failed")
32
+ def __init__(self, error):
33
+ if error:
34
+ super().__init__(f"{error.type}: {error.location}: {error.message}")
35
+ else:
36
+ super().__init__("Compilation failed")
26
37
 
27
38
 
28
39
  class attributes:
@@ -96,13 +107,13 @@ class attributes:
96
107
  self.info("Collecting coverage data into {covdatadir}")
97
108
 
98
109
  for _, artifact in deps.items():
99
- if str(artifact.paths.coverage_data):
100
- tools.copy(artifact.paths.coverage_data, self.covdatadir)
110
+ if artifact.paths.coverage_data:
111
+ tools.copy(str(artifact.paths.coverage_data), self.covdatadir)
101
112
 
102
113
  with tools.environ(GCOV_PREFIX=self.covdatadir):
103
114
  yield
104
115
 
105
- @functools.cache
116
+ @utils.cached.instance
106
117
  def publish_coverage_data(self, artifact, tools):
107
118
  if not publish:
108
119
  return
@@ -149,9 +160,9 @@ class attributes:
149
160
  The following class attributes may be set to control the behavior
150
161
  of lcov:
151
162
 
152
- - ``gcov_branches`` - Enable branch coverage.
163
+ - ``gcov_branches`` - Enable branch coverage.
153
164
  Passed as ``-b`` on the command line. Default: ``True``
154
- - ``gcov_demangle`` - Display demangled function names in output.
165
+ - ``gcov_demangle`` - Display demangled function names in output.
155
166
  Passed as ``-m`` on the command line. Default: ``True``
156
167
 
157
168
  The task artifact is annotated with
@@ -192,34 +203,33 @@ class attributes:
192
203
  if not hasattr(self, "covdatadir"):
193
204
  self.covdatadir = tools.builddir("coverage-data")
194
205
  for _, artifact in deps.items():
195
- if str(artifact.paths.coverage_data):
196
- tools.copy(artifact.paths.coverage_data, self.covdatadir)
206
+ if artifact.paths.coverage_data:
207
+ tools.copy(str(artifact.paths.coverage_data), self.covdatadir)
197
208
 
198
209
  with tools.cwd(self.covdatadir):
199
210
  datafiles = tools.glob("**/*.gcda")
200
211
 
201
- with tools.cwd(tools.builddir("coverage-report-gcov")):
212
+ reportdir = tools.builddir("coverage-report-gcov")
213
+
214
+ with tools.cwd(tools.wsroot):
202
215
  for file in datafiles:
203
216
  infile = os.path.join(self.covdatadir, file)
204
217
  branchflag = "-b " if bool(getattr(self, "gcov_branches", branches)) else ""
205
218
  demangleflag = "-m " if bool(getattr(self, "gcov_demangle", demangle)) else ""
206
- tools.run("{} {}{}-p -s {covdatadir} {}",
207
- gcov, branchflag, demangleflag, infile, output_on_error=True)
208
-
209
- wsdir = get_workspacedir()
210
-
211
- for file in tools.glob("^#^#*.gcov"):
212
- # Patch paths in gcov files.
213
- tools.replace_in_file(file, "Source:../../", "Source:")
214
- tools.replace_in_file(file, self.covdatadir, "")
215
-
216
- # Demangle and rename files into a tree directory structure.
217
- newfile = file.replace("^", "..").replace("#", os.sep)
218
- newfile = tools.expand_relpath(newfile, wsdir)
219
- tools.mkdirname(newfile)
220
- tools.move(file, newfile)
221
-
222
- @functools.cache
219
+ output = tools.run(
220
+ "{} {}{}-t -p -s {covdatadir} {}",
221
+ gcov, branchflag, demangleflag, infile, output_on_error=True)
222
+ output = output.replace(self.covdatadir, "")
223
+ source = re.search(r"0:Source:(.*)", output)
224
+ if not source:
225
+ self.warning(f"No source file found in gcov output for {file}")
226
+ continue
227
+ report = source.group(1) + ".gcov"
228
+ report_path = os.path.join(reportdir, report)
229
+ tools.mkdirname(report_path)
230
+ tools.write_file(report_path, output, expand=False)
231
+
232
+ @utils.cached.instance
223
233
  def publish_coverage_data(self, artifact, tools):
224
234
  if not bool(getattr(self, "gcov_coverage_data", coverage_data)):
225
235
  return
@@ -324,6 +334,19 @@ class attributes:
324
334
 
325
335
  def decorate(cls):
326
336
  class CoverageReportLcovMixin(object):
337
+ def _cov_report_has_trace_data(self, report):
338
+ """
339
+ Check if a coverage report has trace data.
340
+
341
+ LCOV argument ``--ignore-errors=empty`` is not working correctly.
342
+
343
+ """
344
+ with open(report, "r") as f:
345
+ for line in f:
346
+ if line.startswith("DA:") or line.startswith("BRDA:"):
347
+ return True
348
+ return False
349
+
327
350
  def run_coverage_report_lcov(self, deps, tools):
328
351
  lcov = tools.getenv("LCOV", "lcov")
329
352
  if not tools.which(lcov):
@@ -343,13 +366,12 @@ class attributes:
343
366
  if not hasattr(self, "covdatadir"):
344
367
  self.covdatadir = tools.builddir("coverage-data")
345
368
  for _, artifact in deps.items():
346
- if str(artifact.paths.coverage_data):
347
- tools.copy(artifact.paths.coverage_data, self.covdatadir)
369
+ if artifact.paths.coverage_data:
370
+ tools.copy(str(artifact.paths.coverage_data), self.covdatadir)
348
371
 
349
372
  reportdir = tools.builddir("coverage-report-lcov")
350
373
  htmldir = tools.builddir("coverage-report-lcov-html")
351
374
  cachedir = get_cachedir()
352
- wsdir = get_workspacedir()
353
375
 
354
376
  lcov_configs = list(getattr(self, "lcov_configs", []))
355
377
  lcov_excludes = list(getattr(self, "lcov_excludes", []))
@@ -357,7 +379,7 @@ class attributes:
357
379
  lcov_ignore_errors = list(getattr(self, "lcov_ignore_errors", ["empty", "mismatch", "source", "source", "unused"]))
358
380
  lcov_substitutes = list(getattr(self, "lcov_substitutes", []))
359
381
 
360
- lcov_html_flags = ["-p", wsdir]
382
+ lcov_html_flags = ["-p", tools.wsroot]
361
383
  if bool(getattr(self, "lcov_demangle", demangle)):
362
384
  lcov_html_flags.append("--demangle-cpp")
363
385
  lcov_html_flags.append(f"--rc demangle_cpp={cxxfilt}")
@@ -370,7 +392,7 @@ class attributes:
370
392
  lcov_configs.append("no_exception_branch=1")
371
393
 
372
394
  if lcov_excludes:
373
- lcov_excludes = ["--exclude " + exclude for exclude in lcov_excludes]
395
+ lcov_excludes = ["--exclude " + f"'{exclude}'" for exclude in lcov_excludes]
374
396
  lcov_info_flags.extend(lcov_excludes)
375
397
 
376
398
  if lcov_filters:
@@ -392,13 +414,22 @@ class attributes:
392
414
  lcov_html_flags.extend(lcov_substitutes)
393
415
 
394
416
  self.info("Generating LCOV code coverage report")
395
- tools.run("{} -c -d {covdatadir} -o {}/coverage.info --gcov-tool={} {}",
396
- lcov, reportdir, gcov, " ".join(lcov_info_flags), output_on_error=True)
417
+ tools.run("{} -b {} -c -d {covdatadir} -o {}/coverage.info --gcov-tool={} {}",
418
+ lcov, tools.wsroot, reportdir, gcov, " ".join(lcov_info_flags), output_on_error=True)
397
419
 
398
420
  reports = []
399
421
  for _, artifact in deps.items():
400
- if str(artifact.paths.coverage_report_lcov):
422
+ if artifact.paths.coverage_report_lcov:
401
423
  reports.append(str(artifact.paths.coverage_report_lcov))
424
+ if reports:
425
+ filtered_reports = []
426
+ for report in reports:
427
+ if not self._cov_report_has_trace_data(report):
428
+ self.warning(f"Coverage report {report} has no data records, excluding from merge")
429
+ else:
430
+ filtered_reports.append(report)
431
+ reports = filtered_reports
432
+
402
433
  if reports:
403
434
  reports = ["-a " + report for report in reports]
404
435
  self.info("Merging LCOV code coverage reports")
@@ -406,13 +437,14 @@ class attributes:
406
437
  lcov, " ".join(reports), reportdir, gcov, " ".join(lcov_info_flags), output_on_error=True)
407
438
 
408
439
  with tools.cwd(reportdir):
409
- tools.replace_in_file("coverage.info", f"{wsdir}/", "")
440
+ tools.replace_in_file("coverage.info", f"{tools.wsroot}/", "")
410
441
  tools.replace_in_file("coverage.info", f"{cachedir}/", "{{cachedir}}/")
411
442
  tools.copy("coverage.info", "coverage.info.abs")
412
- tools.replace_in_file("coverage.info.abs", "SF:", f"SF:{wsdir}/")
413
- tools.replace_in_file("coverage.info.abs", f"SF:{wsdir}/" + "{{cachedir}}/", f"SF:{cachedir}/")
443
+ tools.replace_in_file("coverage.info.abs", "SF:", f"SF:{tools.wsroot}/")
444
+ tools.replace_in_file("coverage.info.abs", f"SF:{tools.wsroot}/" + "{{cachedir}}/", f"SF:{cachedir}/")
414
445
 
415
- if tools.file_size("coverage.info") <= 0:
446
+ if tools.file_size("coverage.info") <= 0 or not self._cov_report_has_trace_data(tools.expand_path("coverage.info")):
447
+ tools.unlink("coverage.info")
416
448
  self.warning("No coverage data records available, skipping HTML report generation")
417
449
  return
418
450
 
@@ -421,7 +453,7 @@ class attributes:
421
453
  tools.run("genhtml {}/coverage.info.abs --output-directory {} --title '{short_qualified_name}' {}",
422
454
  reportdir, htmldir, " ".join(lcov_html_flags), output_on_error=True)
423
455
 
424
- @functools.cache
456
+ @utils.cached.instance
425
457
  def publish_coverage_data(self, artifact, tools):
426
458
  if not bool(getattr(self, "lcov_coverage_data", coverage_data)):
427
459
  return
@@ -434,8 +466,8 @@ class attributes:
434
466
  def publish_coverage_report_lcov_info(self, artifact, tools):
435
467
  reportdir = tools.builddir("coverage-report-lcov")
436
468
  with tools.cwd(reportdir):
437
- artifact.collect("coverage.info", "report/lcov/")
438
- artifact.paths.coverage_report_lcov = "report/lcov/coverage.info"
469
+ if artifact.collect("coverage.info", "report/lcov/"):
470
+ artifact.paths.coverage_report_lcov = "report/lcov/coverage.info"
439
471
 
440
472
  def publish_coverage_report_lcov_html(self, artifact, tools):
441
473
  htmldir = tools.builddir("coverage-report-lcov-html")
@@ -483,6 +515,22 @@ class attributes:
483
515
  """
484
516
  return utils.concat_attributes("cxxflags", attrib, prepend)
485
517
 
518
+ @staticmethod
519
+ def headers(attrib, prepend=False):
520
+ """
521
+ Decorates a task with an alternative ``headers`` attribute.
522
+
523
+ The new attribute will be concatenated with the regular
524
+ ``headers`` attribute.
525
+
526
+ Args:
527
+ attrib (str): Name of alternative attribute.
528
+ Keywords are expanded.
529
+ prepend (boolean): Prepend the value of the alternative
530
+ attribute. Default: false (append).
531
+ """
532
+ return utils.concat_attributes("headers", attrib, prepend)
533
+
486
534
  @staticmethod
487
535
  def incpaths(attrib, prepend=False):
488
536
  """
@@ -600,7 +648,7 @@ class Variable(HashInfluenceProvider):
600
648
 
601
649
  @staticmethod
602
650
  def __get_variables__(obj):
603
- variables = {}
651
+ variables = OrderedDict()
604
652
  for mro in reversed(obj.__class__.__mro__):
605
653
  for key, variable in getattr(mro, "__variable_list", {}).items():
606
654
  attr = getattr(obj.__class__, key)
@@ -611,7 +659,7 @@ class Variable(HashInfluenceProvider):
611
659
  def __set_name__(self, owner, name):
612
660
  self.name = name
613
661
  if "__variable_list" not in owner.__dict__:
614
- setattr(owner, "__variable_list", {})
662
+ setattr(owner, "__variable_list", OrderedDict())
615
663
  getattr(owner, "__variable_list")[name] = self
616
664
 
617
665
  def create(self, project, writer, deps, tools):
@@ -710,6 +758,54 @@ class ProjectVariable(Variable):
710
758
  return "PV: default={},attrib={}".format(self._default, self._attrib)
711
759
 
712
760
 
761
+ class StdVariable(Variable):
762
+ def __init__(self, lang, flagfmt, values, supported=None, default=None):
763
+ self._lang = lang
764
+ self._flagfmt = flagfmt
765
+ self._values = values
766
+ self._supported = supported or values
767
+ self._default = default
768
+
769
+ def create(self, project, writer, deps, tools):
770
+ value = getattr(project, self.name, self._default) or self._default
771
+ if not value:
772
+ return
773
+ raise_task_error_if(
774
+ type(value) is not int,
775
+ project,
776
+ f"Illegal {self._lang} language standard: '{value}'. Expected integer."
777
+ )
778
+ raise_task_error_if(
779
+ value not in self._values,
780
+ project,
781
+ f"Illegal {self._lang} language standard: '{value}'. Expected one of: {', '.join([str(v) for v in self._values])}"
782
+ )
783
+
784
+ if value not in self._supported:
785
+ new_value = min(self._supported, key=lambda n: abs(n - value))
786
+ project.warning(f"{self._lang}{value} is not supported. Using {self._lang}{new_value} instead.")
787
+ value = new_value
788
+
789
+ writer.variable(self.name, self._flagfmt.format(value))
790
+
791
+
792
+ class OptimizationVariable(Variable):
793
+ def __init__(self, default, values, name=None):
794
+ self.name = name
795
+ self._default = default
796
+ self._values = values
797
+ assert type(values) is dict, "Optimization values must be dict with compiler flag mapping"
798
+
799
+ def create(self, project, writer, deps, tools):
800
+ value = str(getattr(project, "optimize", self._default))
801
+ raise_task_error_if(
802
+ value not in self._values,
803
+ project,
804
+ f"Illegal value assigned to 'optimize' ({value}). Expected: 'none', 'debug', 'size', or 'release'."
805
+ )
806
+ writer.variable(self.name, self._values[value])
807
+
808
+
713
809
  class SharedLibraryVariable(Variable):
714
810
  def __init__(self, name=None, default=None):
715
811
  self.name = name
@@ -739,11 +835,11 @@ class GNUPCHVariables(Variable):
739
835
  "multiple precompiled headers found, only one is allowed")
740
836
 
741
837
  if len(pch) <= 0:
742
- writer.variable("pch_out", "$binary.dir/")
838
+ writer.variable("pch_out", tools.expand_relpath("{outdir}/{binary}.dir/", tools.wsroot))
743
839
  return
744
840
 
745
841
  project._pch = fs.path.basename(pch[0])
746
- project._pch_out = "$binary.dir/" + project._pch + self.gch_ext
842
+ project._pch_out = tools.expand_relpath("{outdir}/{binary}.dir/{_pch}" + self.gch_ext, tools.wsroot)
747
843
 
748
844
  writer.variable("pch", project._pch)
749
845
  writer.variable("pch_flags", "")
@@ -865,7 +961,7 @@ class Rule(HashInfluenceProvider):
865
961
  before any C++ file is compiled.
866
962
  """
867
963
  self.command = command
868
- self.variables = variables or {}
964
+ self.variables = OrderedDict([(key, value) for key, value in (variables or {}).items()])
869
965
  self.depfile = depfile
870
966
  self.deps = deps
871
967
  self.infiles = infiles or []
@@ -900,8 +996,8 @@ class Rule(HashInfluenceProvider):
900
996
  in_base, in_ext = fs.path.splitext(in_basename)
901
997
 
902
998
  if in_dirname and fs.path.isabs(in_dirname):
903
- in_dirname_outdir = fs.path.relpath(in_dirname, project.outdir)
904
- in_dirname = fs.path.relpath(in_dirname, project.joltdir)
999
+ in_dirname_outdir = fs.path.relpath(in_dirname, project.tools.wsroot)
1000
+ in_dirname = fs.path.relpath(in_dirname, project.tools.wsroot)
905
1001
 
906
1002
  result_files = []
907
1003
  for outfile in outfiles or self.outfiles:
@@ -918,7 +1014,7 @@ class Rule(HashInfluenceProvider):
918
1014
 
919
1015
  result_files.append(outfile.replace("..", "__"))
920
1016
 
921
- result_vars = {}
1017
+ result_vars = OrderedDict()
922
1018
  for key, val in self.variables.items():
923
1019
  result_vars[key] = project.tools.expand(
924
1020
  val,
@@ -944,17 +1040,17 @@ class Rule(HashInfluenceProvider):
944
1040
  def build(self, project, writer, infiles, implicit=None, implicit_outputs=None, order_only=None):
945
1041
  result = []
946
1042
  infiles = utils.as_list(infiles)
947
- infiles_rel = [fs.path.relpath(infile, project.outdir) for infile in infiles]
1043
+ infiles_rel = [fs.path.relpath(infile, project.tools.wsroot) for infile in infiles]
948
1044
  implicit = (self.implicit or []) + (implicit or [])
949
1045
  implicit_outputs = (self.implicit_outputs or []) + (implicit_outputs or [])
950
1046
  order_only = (self.order_only or []) + (order_only or [])
951
1047
 
952
1048
  if self.aggregate:
953
1049
  outfiles, variables = self._out(project, infiles[0])
954
- outfiles_rel = [fs.path.relpath(outfile, project.outdir) for outfile in outfiles]
1050
+ outfiles_rel = [fs.path.relpath(outfile, project.tools.wsroot) for outfile in outfiles]
955
1051
  if implicit_outputs:
956
1052
  implicit_outfiles, _ = self._out(project, infiles[0], implicit_outputs)
957
- implicit_outfiles_rel = [fs.path.relpath(outfile, project.outdir) for outfile in implicit_outfiles]
1053
+ implicit_outfiles_rel = [fs.path.relpath(outfile, project.tools.wsroot) for outfile in implicit_outfiles]
958
1054
  else:
959
1055
  implicit_outfiles_rel = []
960
1056
  writer.build(outfiles_rel, self.name, infiles_rel, variables=variables, implicit=implicit, implicit_outputs=implicit_outfiles_rel, order_only=self.order_only + order_only)
@@ -962,10 +1058,10 @@ class Rule(HashInfluenceProvider):
962
1058
  else:
963
1059
  for infile, infile_rel in zip(infiles, infiles_rel):
964
1060
  outfiles, variables = self._out(project, infile)
965
- outfiles_rel = [fs.path.relpath(outfile, project.outdir) for outfile in outfiles]
1061
+ outfiles_rel = [fs.path.relpath(outfile, project.tools.wsroot) for outfile in outfiles]
966
1062
  if implicit_outputs:
967
1063
  implicit_outfiles, _ = self._out(project, infile, implicit_outputs)
968
- implicit_outfiles_rel = [fs.path.relpath(outfile, project.outdir) for outfile in implicit_outfiles]
1064
+ implicit_outfiles_rel = [fs.path.relpath(outfile, project.tools.wsroot) for outfile in implicit_outfiles]
969
1065
  else:
970
1066
  implicit_outfiles_rel = []
971
1067
  writer.build(outfiles_rel, self.name, infile_rel, variables=variables, implicit=implicit, implicit_outputs=implicit_outfiles_rel, order_only=order_only)
@@ -998,12 +1094,12 @@ class ProtobufCompiler(Rule):
998
1094
  phony=True,
999
1095
  variables=None,
1000
1096
  **kwargs):
1001
- variables_final = {
1002
- "desc": "[PROTOC] {in_base}{in_ext}",
1003
- "out_depfile": "{binary}.dir/{in_base}.pb.d",
1004
- "outdir_proto": os.path.dirname(outfiles[0]),
1005
- "in_path_outdir": "{in_path_outdir}",
1006
- }
1097
+ variables_final = OrderedDict([
1098
+ ("desc", "[PROTOC] {in_base}{in_ext}"),
1099
+ ("out_depfile", "{outdir_rel}/{binary}.dir/{in_base}.pb.d"),
1100
+ ("outdir_proto", "{outdir_rel}/{binary}.dir"),
1101
+ ("in_path_outdir", "{in_path_outdir}"),
1102
+ ])
1007
1103
  variables_final.update(variables or {})
1008
1104
  super().__init__(
1009
1105
  command=command,
@@ -1044,8 +1140,8 @@ class GRPCProtobufCompiler(ProtobufCompiler):
1044
1140
  variables=None,
1045
1141
  **kwargs):
1046
1142
  variables_final = {
1047
- "out_depfile": "{binary}.dir/{in_base}.pb.d",
1048
- "out_depfile_grpc": "{binary}.dir/{in_base}.grpc.pb.d",
1143
+ "out_depfile": "{outdir_rel}/{binary}.dir/{in_base}.pb.d",
1144
+ "out_depfile_grpc": "{outdir_rel}/{binary}.dir/{in_base}.grpc.pb.d",
1049
1145
  }
1050
1146
  variables_final.update(variables or {})
1051
1147
  super().__init__(command=command, depfile=depfile, outfiles=outfiles, variables=variables_final, **kwargs)
@@ -1112,7 +1208,8 @@ class Skip(Rule):
1112
1208
 
1113
1209
  @task_attributes.system
1114
1210
  class MakeDirectory(Rule):
1115
- command_linux = "mkdir -p $out"
1211
+ command_darwin = "mkdir -p $out"
1212
+ command_linux = command_darwin
1116
1213
  command_windows = "if not exist $out mkdir $out"
1117
1214
 
1118
1215
  def __init__(self, name):
@@ -1187,7 +1284,9 @@ class FileListWriter(Rule):
1187
1284
  def build(self, project, writer, infiles, implicit=None, order_only=None):
1188
1285
  infiles = [fs.as_posix(infile) for infile in infiles] if self.posix else infiles
1189
1286
  file_list_path = fs.path.join(project.outdir, "{0}.list".format(self.name))
1287
+ file_list_path = project.tools.expand_path(file_list_path)
1190
1288
  file_list_hash_path = fs.path.join(project.outdir, "{0}.hash".format(self.name))
1289
+ file_list_hash_path = project.tools.expand_path(file_list_hash_path)
1191
1290
  data, digest = self._data(project, infiles)
1192
1291
  if not self._identical(file_list_path, file_list_hash_path, data, digest):
1193
1292
  self._write(file_list_path, file_list_hash_path, data, digest)
@@ -1270,11 +1369,12 @@ class GNUDepImporter(Rule):
1270
1369
  for name, artifact in deps.items():
1271
1370
  if artifact.cxxinfo.libpaths.items():
1272
1371
  sandbox = project.tools.sandbox(artifact, project.incremental)
1372
+ sandbox = project.tools.expand_relpath(sandbox, project.tools.wsroot)
1273
1373
  for lib in artifact.cxxinfo.libraries.items():
1274
1374
  name = "{0}{1}{2}".format(self.prefix, lib, self.suffix)
1275
1375
  for path in artifact.cxxinfo.libpaths.items():
1276
1376
  archive = fs.path.join(sandbox, path, name)
1277
- if fs.path.exists(archive):
1377
+ if fs.path.exists(os.path.join(project.tools.wsroot, archive)):
1278
1378
  archives.append(archive)
1279
1379
  return archives
1280
1380
 
@@ -1285,7 +1385,9 @@ class GNUDepImporter(Rule):
1285
1385
  if isinstance(project, CXXLibrary):
1286
1386
  imports += self._build_archives(project, writer, deps)
1287
1387
  if not project.shared and project.selfsustained:
1288
- writer.sources.extend(imports)
1388
+ sources = [os.path.join(project.tools.wsroot, source) for source in imports]
1389
+ sources = [os.path.relpath(source, project.joltdir) for source in sources]
1390
+ writer.sources.extend(sources)
1289
1391
  return imports
1290
1392
 
1291
1393
  def get_influence(self, task):
@@ -1298,7 +1400,7 @@ class Toolchain(object):
1298
1400
 
1299
1401
  @staticmethod
1300
1402
  def build_rules_and_vars(obj):
1301
- rule_map = {}
1403
+ rule_map = OrderedDict()
1302
1404
  rules, vars = Toolchain.all_rules_and_vars(obj)
1303
1405
  for name, rule in rules.items():
1304
1406
  rule.name = name
@@ -1370,18 +1472,18 @@ class IncludePaths(Variable):
1370
1472
  return tools.expand(path)
1371
1473
  if path[0] in ['-']:
1372
1474
  path = tools.expand_path(path[1:])
1373
- return tools.expand_relpath(path, project.outdir)
1475
+ return tools.expand_relpath(path, project.tools.wsroot)
1374
1476
 
1375
1477
  def expand_artifact(sandbox, path):
1376
1478
  if path[0] in ['=', fs.sep]:
1377
1479
  return path
1378
1480
  if path[0] in ['-']:
1379
1481
  path = fs.path.join(project.joltdir, path[1:])
1380
- return tools.expand_relpath(fs.path.join(sandbox, path), project.outdir)
1482
+ return tools.expand_relpath(fs.path.join(sandbox, path), project.tools.wsroot)
1381
1483
 
1382
1484
  incpaths = []
1383
1485
  if self.outdir:
1384
- incpaths += ["$binary.dir"]
1486
+ incpaths += [tools.expand_relpath("{outdir}/{binary}.dir", project.tools.wsroot)]
1385
1487
  if self.attrib:
1386
1488
  incpaths += [expand(path) for path in getattr(project, self.attrib)]
1387
1489
  if self.imported:
@@ -1410,12 +1512,13 @@ class LibraryPaths(Variable):
1410
1512
  return
1411
1513
  libpaths = []
1412
1514
  if self.attrib:
1413
- libpaths = [tools.expand_relpath(path, project.outdir) for path in getattr(project, self.attrib)]
1515
+ libpaths = [tools.expand_relpath(path, project.tools.wsroot) for path in getattr(project, self.attrib)]
1414
1516
  if self.imported:
1415
1517
  for _, artifact in deps.items():
1416
1518
  libs = artifact.cxxinfo.libpaths.items()
1417
1519
  if libs:
1418
1520
  sandbox = tools.sandbox(artifact, project.incremental)
1521
+ sandbox = tools.expand_relpath(sandbox, project.tools.wsroot)
1419
1522
  libpaths += [fs.path.join(sandbox, path) for path in libs]
1420
1523
  libpaths = ["{0}{1}".format(self.prefix, path) for path in libpaths]
1421
1524
  writer.variable(self.name, " ".join(libpaths))
@@ -1477,7 +1580,9 @@ class GNUToolchain(Toolchain):
1477
1580
  bin = Skip(infiles=[".dll", ".elf", ".exe", ".out", ".so"])
1478
1581
 
1479
1582
  joltdir = ProjectVariable()
1583
+ builddir = ProjectVariable(attrib="outdir")
1480
1584
  outdir = ProjectVariable()
1585
+ outdir_rel = ProjectVariable()
1481
1586
  binary = ProjectVariable()
1482
1587
 
1483
1588
  ar = ToolEnvironmentVariable(default="ar", abspath=True)
@@ -1490,7 +1595,17 @@ class GNUToolchain(Toolchain):
1490
1595
  cxxwrap = EnvironmentVariable(default="")
1491
1596
  flatc = ToolEnvironmentVariable(default="flatc", envname="FLATC", abspath=True)
1492
1597
  protoc = ToolEnvironmentVariable(default="protoc", envname="PROTOC", abspath=True)
1493
-
1598
+ strip = ToolEnvironmentVariable(default="strip", envname="STRIP", abspath=True)
1599
+
1600
+ cstd = StdVariable("C", "-std=c{}", values=c_standards_list, default=c_standard_default)
1601
+ cxxstd = StdVariable("C++", "-std=c++{}", values=cxx_standards_list, default=cxx_standard_default)
1602
+ optflag = OptimizationVariable(default="release", values={
1603
+ "none": "-O0",
1604
+ "debug": "-Og",
1605
+ "size": "-Os",
1606
+ "release": "-O2",
1607
+ None: "-O0",
1608
+ })
1494
1609
  asflags = EnvironmentVariable(default="")
1495
1610
  cflags = EnvironmentVariable(default="")
1496
1611
  cxxflags = EnvironmentVariable(default="")
@@ -1518,10 +1633,10 @@ class GNUToolchain(Toolchain):
1518
1633
  libpaths = LibraryPaths(prefix="-L")
1519
1634
  libraries = Libraries(prefix="-l")
1520
1635
 
1521
- mkdir_debug = MakeDirectory(name=".debug")
1636
+ mkdir_debug = MakeDirectory(name="$outdir_rel/.debug")
1522
1637
 
1523
1638
  compile_pch = GNUCompiler(
1524
- command="$cxxwrap $cxx -x c++-header $cxxflags $shared_flags $imported_cxxflags $extra_cxxflags $covflags $macros $incpaths -MMD -MF $out.d -c $in -o $out",
1639
+ command="$cxxwrap $cxx -x c++-header $cxxstd $optflag $cxxflags $shared_flags $imported_cxxflags $extra_cxxflags $covflags $macros $incpaths -MMD -MF $out.d -c $in -o $out",
1525
1640
  deps="gcc",
1526
1641
  depfile="$out.d",
1527
1642
  infiles=[GNUPCHVariables.pch_ext],
@@ -1530,7 +1645,7 @@ class GNUToolchain(Toolchain):
1530
1645
  variables={"desc": "[PCH] {in_base}{in_ext}"})
1531
1646
 
1532
1647
  compile_c = GNUCompiler(
1533
- command="$ccwrap $cc -x c $pch_flags $cflags $shared_flags $imported_cflags $extra_cflags $covflags $macros $incpaths -MMD -MF $out.d -c $in -o $out",
1648
+ command="$ccwrap $cc -x c $cstd $pch_flags $optflag $cflags $shared_flags $imported_cflags $extra_cflags $covflags $macros $incpaths -MMD -MF $out.d -c $in -o $out",
1534
1649
  deps="gcc",
1535
1650
  depfile="$out.d",
1536
1651
  infiles=[".c"],
@@ -1540,7 +1655,7 @@ class GNUToolchain(Toolchain):
1540
1655
  implicit=["$cc_path"])
1541
1656
 
1542
1657
  compile_cxx = GNUCompiler(
1543
- command="$cxxwrap $cxx -x c++ $pch_flags $cxxflags $shared_flags $imported_cxxflags $extra_cxxflags $covflags $macros $incpaths -MMD -MF $out.d -c $in -o $out",
1658
+ command="$cxxwrap $cxx -x c++ $cxxstd $pch_flags $optflag $cxxflags $shared_flags $imported_cxxflags $extra_cxxflags $covflags $macros $incpaths -MMD -MF $out.d -c $in -o $out",
1544
1659
  deps="gcc",
1545
1660
  depfile="$out.d",
1546
1661
  infiles=[".cc", ".cpp", ".cxx"],
@@ -1575,51 +1690,100 @@ class GNUToolchain(Toolchain):
1575
1690
 
1576
1691
  linker = GNULinker(
1577
1692
  command=" && ".join([
1578
- "$ld $ldflags $imported_ldflags $extra_ldflags $covflags $libpaths -Wl,--start-group @objects.list -Wl,--end-group -o $out -Wl,--start-group $libraries -Wl,--end-group",
1579
- "$objcopy_path --only-keep-debug $out .debug/$binary",
1693
+ "$ld $ldflags $imported_ldflags $extra_ldflags $covflags $libpaths -Wl,--start-group @$outdir_rel/objects.list -Wl,--end-group -o $out -Wl,--start-group $libraries -Wl,--end-group",
1694
+ "$objcopy_path --only-keep-debug $out $outdir_rel/.debug/$binary",
1580
1695
  "$objcopy_path --strip-all $out",
1581
- "$objcopy_path --add-gnu-debuglink=.debug/$binary $out"
1696
+ "$objcopy_path --add-gnu-debuglink=$outdir_rel/.debug/$binary $out"
1582
1697
  ]),
1583
1698
  infiles=[".o", ".obj", ".a"],
1584
1699
  outfiles=["{outdir}/{binary}"],
1585
1700
  variables={"desc": "[LINK] {binary}"},
1586
- implicit=["$ld_path", "$objcopy_path", ".debug"])
1701
+ implicit=["$ld_path", "$objcopy_path", "$outdir_rel/.debug"])
1587
1702
 
1588
1703
  dynlinker = GNULinker(
1589
1704
  command=" && ".join([
1590
- "$ld $ldflags -shared $imported_ldflags $extra_ldflags $covflags $libpaths -Wl,--start-group @objects.list -Wl,--end-group -o $out -Wl,--start-group $libraries -Wl,--end-group",
1591
- "$objcopy_path --only-keep-debug $out .debug/lib$binary.so",
1705
+ "$ld $ldflags -shared $imported_ldflags $extra_ldflags $covflags $libpaths -Wl,--start-group @$outdir_rel/objects.list -Wl,--end-group -o $out -Wl,--start-group $libraries -Wl,--end-group",
1706
+ "$objcopy_path --only-keep-debug $out $outdir_rel/.debug/lib$binary.so",
1592
1707
  "$objcopy_path --strip-all $out",
1593
- "$objcopy_path --add-gnu-debuglink=.debug/lib$binary.so $out"
1708
+ "$objcopy_path --add-gnu-debuglink=$outdir_rel/.debug/lib$binary.so $out"
1594
1709
  ]),
1595
1710
  infiles=[".o", ".obj", ".a"],
1596
1711
  outfiles=["{outdir}/lib{binary}.so"],
1597
1712
  variables={"desc": "[LINK] {binary}"},
1598
- implicit=["$ld_path", "$objcopy_path", ".debug"])
1713
+ implicit=["$ld_path", "$objcopy_path", "$outdir_rel/.debug"])
1599
1714
 
1600
1715
  archiver = GNUArchiver(
1601
- command="$ar -M < objects.list && $ranlib $out",
1716
+ command="$ar -M < $outdir_rel/objects.list && $ranlib $out",
1602
1717
  infiles=[".o", ".obj", ".a"],
1603
1718
  outfiles=["{outdir}/lib{binary}.a"],
1604
1719
  variables={"desc": "[AR] lib{binary}.a"},
1605
- implicit=["$ld_path", "$ar_path"])
1720
+ implicit=["$ld_path", "$ar_path", "$ranlib_path"])
1606
1721
 
1607
1722
  depimport = GNUDepImporter(
1608
1723
  prefix="lib",
1609
1724
  suffix=".a")
1610
1725
 
1611
1726
 
1727
+ class BSDArchiver(Rule):
1728
+ def __init__(self, *args, **kwargs):
1729
+ super(BSDArchiver, self).__init__(*args, aggregate=True, **kwargs)
1730
+
1731
+ def build(self, project, writer, infiles, implicit=None, order_only=None):
1732
+ writer._objects = infiles
1733
+ project._binaries, _ = self._out(project, project.binary)
1734
+ file_list = FileListWriter("objects", project._binaries)
1735
+ file_list.build(project, writer, infiles)
1736
+ super().build(project, writer, infiles, implicit=writer.depimports, order_only=order_only)
1737
+
1738
+ def get_influence(self, task):
1739
+ return "BSDArchiver" + super().get_influence(task)
1740
+
1741
+
1742
+ class DarwinGNUToolchain(GNUToolchain):
1743
+ libtool = ToolEnvironmentVariable(default="libtool", envname="LIBTOOL", abspath=True)
1744
+ dsymutil = ToolEnvironmentVariable(default="dsymutil", envname="DSYMUTIL", abspath=True)
1745
+
1746
+ linker = GNULinker(
1747
+ command=" && ".join([
1748
+ "$ld $ldflags $imported_ldflags $extra_ldflags $covflags $libpaths @$outdir_rel/objects.list -o $out $libraries",
1749
+ "$dsymutil $out -o $outdir_rel/.debug/$binary.dSYM",
1750
+ "$strip $out",
1751
+ ]),
1752
+ infiles=[".o", ".obj", ".a"],
1753
+ outfiles=["{outdir}/{binary}"],
1754
+ variables={"desc": "[LINK] {binary}"},
1755
+ implicit=["$ld_path", "$dsymutil_path", "$strip_path", "$outdir_rel/.debug"])
1756
+
1757
+ dynlinker = GNULinker(
1758
+ command=" && ".join([
1759
+ "$ld $ldflags -shared $imported_ldflags $extra_ldflags $covflags $libpaths @$outdir_rel/objects.list -o $out $libraries",
1760
+ "$dsymutil $out -o $outdir_rel/.debug/$binary.dSYM",
1761
+ "$strip $out",
1762
+ ]),
1763
+ infiles=[".o", ".obj", ".a"],
1764
+ outfiles=["{outdir}/lib{binary}.so"],
1765
+ variables={"desc": "[LINK] {binary}"},
1766
+ implicit=["$ld_path", "$dsymutil_path", "$strip_path", "$outdir_rel/.debug"])
1767
+
1768
+ archiver = BSDArchiver(
1769
+ command="$libtool -static -o $out @$outdir_rel/objects.list && $ranlib $out",
1770
+ infiles=[".o", ".obj", ".a"],
1771
+ outfiles=["{outdir}/lib{binary}.a"],
1772
+ variables={"desc": "[AR] lib{binary}.a"},
1773
+ implicit=["$libtool_path", "$ranlib_path"])
1774
+
1775
+
1612
1776
  class MinGWToolchain(GNUToolchain):
1613
1777
  linker = GNULinker(
1614
1778
  command=" && ".join([
1615
- "$ld $ldflags $imported_ldflags $extra_ldflags $libpaths -Wl,--start-group @objects.list -Wl,--end-group -o $out -Wl,--start-group $libraries -Wl,--end-group",
1616
- "$objcopy --only-keep-debug $out .debug/$binary.exe",
1779
+ "$ld $ldflags $imported_ldflags $extra_ldflags $libpaths -Wl,--start-group @$outdir_rel/objects.list -Wl,--end-group -o $out -Wl,--start-group $libraries -Wl,--end-group",
1780
+ "$objcopy --only-keep-debug $out $outdir_rel/.debug/$binary.exe",
1617
1781
  "$objcopy --strip-all $out",
1618
- "$objcopy --add-gnu-debuglink=.debug/$binary.exe $out"
1782
+ "$objcopy --add-gnu-debuglink=$outdir_rel/.debug/$binary.exe $out"
1619
1783
  ]),
1620
1784
  outfiles=["{outdir}/{binary}.exe"],
1621
1785
  variables={"desc": "[LINK] {binary}"},
1622
- implicit=["$ld_path", "$objcopy_path", ".debug"])
1786
+ implicit=["$ld_path", "$objcopy_path", "$outdir_rel/.debug"])
1623
1787
 
1624
1788
 
1625
1789
  class MSVCArchiver(Rule):
@@ -1637,6 +1801,62 @@ class MSVCArchiver(Rule):
1637
1801
  return "MSVCArchiver" + super().get_influence(task)
1638
1802
 
1639
1803
 
1804
+ class MSVCCRT(Variable):
1805
+ def __init__(self, default="/MD", flagsfn=None):
1806
+ self.default = default
1807
+ self.flagsfn = flagsfn
1808
+
1809
+ def _combine(self, project, crt1, crt2):
1810
+ if crt1 is None:
1811
+ return crt2
1812
+ if crt2 is None:
1813
+ return crt1
1814
+ raise_task_error_if(crt1 != crt2, project, "Mismatching Windows CRT library types (/MT vs /MD)")
1815
+ return crt1
1816
+
1817
+ def _select_flag(self, flags):
1818
+ """ Look for /Mxx flag in the list of flags """
1819
+ if flags is None:
1820
+ return None
1821
+ if "Static" in flags:
1822
+ return "/MT"
1823
+ if "StaticDebug" in flags:
1824
+ return "/MTd"
1825
+ if "Dynamic" in flags:
1826
+ return "/MD"
1827
+ if "DynamicDebug" in flags:
1828
+ return "/MDd"
1829
+ if "/MT" in flags:
1830
+ return "/MT"
1831
+ if "/MTd" in flags:
1832
+ return "/MTd"
1833
+ if "/MD" in flags:
1834
+ return "/MD"
1835
+ if "/MDd" in flags:
1836
+ return "/MDd"
1837
+ return None
1838
+
1839
+ def _select_dep_flags(self, project, dep):
1840
+ crt = self._select_flag(dep.cxxinfo.asflags.items())
1841
+ if hasattr(dep.cxxinfo, "msvcrt"):
1842
+ crt = self._combine(project, crt, self._select_flag([str(dep.cxxinfo.msvcrt)]))
1843
+ crt = self._combine(project, crt, self._select_flag(dep.cxxinfo.cflags.items()))
1844
+ return self._combine(project, crt, self._select_flag(dep.cxxinfo.cxxflags.items()))
1845
+
1846
+ def create(self, project, writer, deps, tools):
1847
+ crt = self._select_flag(getattr(project, "msvcrt", None))
1848
+ crt = self._combine(project, crt, self._select_flag(project._asflags()))
1849
+ crt = self._combine(project, crt, self._select_flag(project._cflags()))
1850
+ crt = self._combine(project, crt, self._select_flag(project._cxxflags()))
1851
+ for _, artifact in deps.items():
1852
+ crt = self._combine(project, crt, self._select_dep_flags(project, artifact))
1853
+ writer.variable(self.name, crt or self.default)
1854
+
1855
+ @utils.cached.instance
1856
+ def get_influence(self, task):
1857
+ return "MSVC-CRT"
1858
+
1859
+
1640
1860
  MSVCCompiler = GNUCompiler
1641
1861
  MSVCLinker = GNULinker
1642
1862
  MSVCDepImporter = GNUDepImporter
@@ -1647,7 +1867,9 @@ class MSVCToolchain(Toolchain):
1647
1867
  bin = Skip(infiles=[".dll", ".exe"])
1648
1868
 
1649
1869
  joltdir = ProjectVariable()
1870
+ builddir = ProjectVariable(attrib="outdir")
1650
1871
  outdir = ProjectVariable()
1872
+ outdir_rel = ProjectVariable()
1651
1873
  binary = ProjectVariable()
1652
1874
 
1653
1875
  cl = ToolEnvironmentVariable(default="cl", envname="cl_exe", abspath=True)
@@ -1656,13 +1878,25 @@ class MSVCToolchain(Toolchain):
1656
1878
  flatc = ToolEnvironmentVariable(default="flatc", envname="FLATC", abspath=True)
1657
1879
  protoc = ToolEnvironmentVariable(default="protoc", envname="PROTOC", abspath=True)
1658
1880
 
1881
+ cstd = StdVariable("C", "/std:c{}", values=c_standards_list, supported=[11, 17], default=c_standard_default)
1882
+ cxxstd = StdVariable("C++", "/std:c++{}", values=cxx_standards_list, supported=[14, 17, 20], default=cxx_standard_default)
1883
+ optflag = OptimizationVariable(default="speed", values={
1884
+ "none": "/Od",
1885
+ "debug": "/Od /Zi",
1886
+ "size": "/O1",
1887
+ "release": "/O2 /DNDEBUG",
1888
+ None: "/0d",
1889
+ })
1890
+ win32flags = Variable("/DWIN32 /D_WINDOWS /D_WIN32_WINNT=0x0601 /EHsc")
1659
1891
  asflags = EnvironmentVariable(default="")
1660
- cflags = EnvironmentVariable(default="/EHsc")
1661
- cxxflags = EnvironmentVariable(default="/EHsc")
1892
+ cflags = EnvironmentVariable(default="")
1893
+ cxxflags = EnvironmentVariable(default="")
1662
1894
  fbflags = EnvironmentVariable(default="")
1663
1895
  ldflags = EnvironmentVariable(default="")
1664
1896
  protoflags = EnvironmentVariable(default="")
1665
1897
 
1898
+ crt = MSVCCRT()
1899
+
1666
1900
  extra_asflags = ProjectVariable(attrib="asflags")
1667
1901
  extra_cflags = ProjectVariable(attrib="cflags")
1668
1902
  extra_cxxflags = ProjectVariable(attrib="cxxflags")
@@ -1677,26 +1911,26 @@ class MSVCToolchain(Toolchain):
1677
1911
  libraries = Libraries(suffix=".lib")
1678
1912
 
1679
1913
  compile_asm = MSVCCompiler(
1680
- command="$cl /nologo /showIncludes $asflags $extra_asflags $macros $incpaths /c /Tc$in /Fo$out",
1914
+ command="$cl /nologo /showIncludes $crt $win32flags $asflags $extra_asflags $macros $incpaths /c /Tc$in /Fo$out",
1681
1915
  deps="msvc",
1682
1916
  infiles=[".asm", ".s", ".S"],
1683
- outfiles=["{outdir}/{binary}.dir/{in_path}/{in_base}.obj"],
1917
+ outfiles=["{outdir}/{binary}.dir/{in_path}/{in_base}{in_ext}.obj"],
1684
1918
  variables={"desc": "[ASM] {in_base}{in_ext}"},
1685
1919
  implicit=["$cl_path"])
1686
1920
 
1687
1921
  compile_c = MSVCCompiler(
1688
- command="$cl /nologo /showIncludes $cxxflags $extra_cxxflags $macros $incpaths /c /Tc$in /Fo$out",
1922
+ command="$cl /nologo /showIncludes $cstd $crt $win32flags $optflag $cflags $extra_cflags $macros $incpaths /c /Tc$in /Fo$out",
1689
1923
  deps="msvc",
1690
1924
  infiles=[".c"],
1691
- outfiles=["{outdir}/{binary}.dir/{in_path}/{in_base}.obj"],
1925
+ outfiles=["{outdir}/{binary}.dir/{in_path}/{in_base}{in_ext}.obj"],
1692
1926
  variables={"desc": "[C] {in_base}{in_ext}"},
1693
1927
  implicit=["$cl_path"])
1694
1928
 
1695
1929
  compile_cxx = MSVCCompiler(
1696
- command="$cl /nologo /showIncludes $cxxflags $extra_cxxflags $macros $incpaths /c /Tp$in /Fo$out",
1930
+ command="$cl /nologo /showIncludes $cxxstd $crt $win32flags $optflag $cxxflags $extra_cxxflags $macros $incpaths /c /Tp$in /Fo$out",
1697
1931
  deps="msvc",
1698
1932
  infiles=[".cc", ".cpp", ".cxx"],
1699
- outfiles=["{outdir}/{binary}.dir/{in_path}/{in_base}.obj"],
1933
+ outfiles=["{outdir}/{binary}.dir/{in_path}/{in_base}{in_ext}.obj"],
1700
1934
  variables={"desc": "[CXX] {in_base}{in_ext}"},
1701
1935
  implicit=["$cl_path"])
1702
1936
 
@@ -1704,14 +1938,14 @@ class MSVCToolchain(Toolchain):
1704
1938
  compile_proto = ProtobufCompiler(generator="cpp")
1705
1939
 
1706
1940
  linker = MSVCLinker(
1707
- command="$link /nologo $ldflags $extra_ldflags $libpaths @objects.list $libraries /out:$out",
1941
+ command="$link /nologo $ldflags $extra_ldflags $libpaths @$outdir_rel/objects.list $libraries /out:$out",
1708
1942
  infiles=[".o", ".obj", ".lib"],
1709
1943
  outfiles=["{outdir}/{binary}.exe"],
1710
1944
  variables={"desc": "[LINK] {binary}"},
1711
1945
  implicit=["$link_path"])
1712
1946
 
1713
1947
  archiver = MSVCArchiver(
1714
- command="$lib /nologo /out:$out @objects.list",
1948
+ command="$lib /nologo /out:$out @$outdir_rel/objects.list",
1715
1949
  infiles=[".o", ".obj", ".lib"],
1716
1950
  outfiles=["{outdir}/{binary}.lib"],
1717
1951
  variables={"desc": "[LIB] {binary}"},
@@ -1724,11 +1958,15 @@ class MSVCToolchain(Toolchain):
1724
1958
 
1725
1959
  _toolchains = {
1726
1960
  GNUToolchain: GNUToolchain(),
1961
+ DarwinGNUToolchain: DarwinGNUToolchain(),
1727
1962
  MSVCToolchain: MSVCToolchain(),
1728
1963
  }
1729
1964
 
1730
- if os.name == "nt":
1965
+ _system = platform.system()
1966
+ if _system == "Windows":
1731
1967
  toolchain = _toolchains[MSVCToolchain]
1968
+ elif _system == "Darwin":
1969
+ toolchain = _toolchains[DarwinGNUToolchain]
1732
1970
  else:
1733
1971
  toolchain = _toolchains[GNUToolchain]
1734
1972
 
@@ -1768,9 +2006,23 @@ class CXXProject(Task):
1768
2006
  cflags = []
1769
2007
  """ A list of compiler flags used when compiling C files. """
1770
2008
 
2009
+ cstd = None
2010
+ """
2011
+ C language standard to use (int). Default: Compiler default
2012
+
2013
+ If the chosen standard is not supported, the nearest supported standard is selected.
2014
+ """
2015
+
1771
2016
  cxxflags = []
1772
2017
  """ A list of compiler flags used when compiling C++ files. """
1773
2018
 
2019
+ cxxstd = None
2020
+ """
2021
+ C++ language standard to use (int). Default: Compiler default
2022
+
2023
+ If the chosen standard is not supported, the nearest supported standard is selected.
2024
+ """
2025
+
1774
2026
  depimports = []
1775
2027
  """ List of implicit dependencies """
1776
2028
 
@@ -1789,6 +2041,18 @@ class CXXProject(Task):
1789
2041
  macros = []
1790
2042
  """ List of preprocessor macros to set """
1791
2043
 
2044
+ optimize = "release"
2045
+ """
2046
+ Compiler optimization level.
2047
+
2048
+ Supported values are:
2049
+
2050
+ - "none" / None
2051
+ - "debug"
2052
+ - "size"
2053
+ - "release"
2054
+ """
2055
+
1792
2056
  sources = []
1793
2057
  """ A list of sources to compile.
1794
2058
 
@@ -1823,8 +2087,7 @@ class CXXProject(Task):
1823
2087
  """ Name of the target binary (defaults to canonical task name) """
1824
2088
 
1825
2089
  coverage = False
1826
- """
1827
- Enable code coverage instrumentation.
2090
+ """Enable code coverage instrumentation.
1828
2091
 
1829
2092
  Only implemented for GCC/Clang toolchains.
1830
2093
 
@@ -1834,11 +2097,12 @@ class CXXProject(Task):
1834
2097
  task artifact. Upon running the executable, coverage data files are
1835
2098
  generated.
1836
2099
 
1837
- Use the :func:`coverage_data` decorator to automatically collect and
1838
- publish data files in tasks that run instrumented binaries. The
1839
- :func:`coverage_report_gcov" decorator can then be used to process
1840
- the notes and data files into human readable coverage information.
1841
- There is also a :func:`coverage_report_lcov` decorator that will
2100
+ Use the :func:`attributes.coverage_data` decorator to
2101
+ automatically collect and publish data files in tasks that run
2102
+ instrumented binaries. The :func:`attributes.coverage_report_gcov`
2103
+ decorator can then be used to process the notes and data files
2104
+ into human readable coverage information. There is also a
2105
+ :func:`attributes.coverage_report_lcov` decorator that will
1842
2106
  generate and publish an HTML coverage report.
1843
2107
 
1844
2108
  Example:
@@ -1903,7 +2167,7 @@ class CXXProject(Task):
1903
2167
  self._init_rules_and_vars()
1904
2168
 
1905
2169
  def _init_rules_and_vars(self):
1906
- self._rules_by_ext = {}
2170
+ self._rules_by_ext = OrderedDict()
1907
2171
  self._rules = []
1908
2172
  self._variables = []
1909
2173
 
@@ -1941,22 +2205,26 @@ class CXXProject(Task):
1941
2205
  def _verify_influence(self, deps, artifact, tools):
1942
2206
  # Verify that listed sources and their dependencies are influencing
1943
2207
  sources = set(self.sources + getattr(self, "headers", []))
1944
- with tools.cwd(self.outdir):
2208
+ with tools.cwd(tools.wsroot):
1945
2209
  depfiles = [obj + ".d" for obj in getattr(self._writer, "_objects", [])]
1946
2210
  for depfile in depfiles:
1947
2211
  try:
1948
2212
  data = tools.read_file(depfile)
1949
2213
  except Exception:
1950
2214
  continue
1951
- data = data.split(":", 1)
1952
- if len(data) <= 1:
1953
- continue
1954
2215
 
1955
- depsrcs = data[1]
1956
- depsrcs = depsrcs.split()
1957
- depsrcs = [f.rstrip("\\").strip() for f in depsrcs]
1958
- depsrcs = [tools.expand_relpath(dep, self.joltdir) for dep in filter(lambda n: n, depsrcs)]
1959
- sources = sources.union(depsrcs)
2216
+ data = data.replace("\\\n", "")
2217
+ for depline in data.splitlines():
2218
+ depline = depline.strip()
2219
+ depline = depline.split(":", 1)
2220
+ if len(depline) <= 1:
2221
+ continue
2222
+
2223
+ inputs = depline[1]
2224
+ inputs = inputs.replace("\\ ", "\x00")
2225
+ inputs = [dep.strip().replace("\x00", " ") for dep in inputs.split()]
2226
+ inputs = [tools.expand_relpath(input, self.joltdir) for input in filter(lambda n: n, inputs)]
2227
+ sources = sources.union(inputs)
1960
2228
  super()._verify_influence(deps, artifact, tools, sources)
1961
2229
 
1962
2230
  def _expand_headers(self):
@@ -1990,10 +2258,11 @@ class CXXProject(Task):
1990
2258
  self.sources = sources
1991
2259
 
1992
2260
  def _write_ninja_file(self, basedir, deps, tools, filename="build.ninja"):
1993
- with open(fs.path.join(basedir, filename), "w") as fobj:
2261
+ with open(tools.expand_path(fs.path.join(basedir, filename)), "w") as fobj:
1994
2262
  writer = ninja.Writer(fobj)
1995
- writer.depimports = [tools.expand_relpath(dep, self.outdir)
1996
- for dep in self.depimports]
2263
+ writer.depimports = [
2264
+ tools.expand_relpath(dep, tools.wsroot)
2265
+ for dep in self.depimports]
1997
2266
  writer.objects = []
1998
2267
  writer.sources = copy.copy(self.sources)
1999
2268
  self._populate_rules_and_variables(writer, deps, tools)
@@ -2024,7 +2293,7 @@ def main():
2024
2293
  for object in objects:
2025
2294
  print(object)
2026
2295
  elif [arg for arg in sys.argv[1:] if arg == "-a"]:
2027
- subprocess.call(["ninja", "-v"])
2296
+ subprocess.call(["ninja", "-f", "{outdir}/build.ninja", "-v"])
2028
2297
  else:
2029
2298
  targets = []
2030
2299
  for arg in sys.argv[1:]:
@@ -2034,7 +2303,7 @@ def main():
2034
2303
  targets.extend(matches)
2035
2304
  if not targets:
2036
2305
  return
2037
- subprocess.call(["ninja", "-v"] + targets)
2306
+ subprocess.call(["ninja", "-f", "{outdir}/build.ninja", "-v"] + targets)
2038
2307
 
2039
2308
  if __name__ == "__main__":
2040
2309
  main()
@@ -2043,7 +2312,8 @@ if __name__ == "__main__":
2043
2312
  fobj.write(
2044
2313
  data.format(
2045
2314
  executable=sys.executable,
2046
- objects=[fs.path.relpath(o, self.outdir) for o in writer.objects]))
2315
+ objects=[fs.path.relpath(o, tools.wsroot) for o in writer.objects],
2316
+ outdir=self.outdir))
2047
2317
  tools.chmod(filepath, 0o777)
2048
2318
 
2049
2319
  def find_rule(self, ext):
@@ -2088,7 +2358,7 @@ if __name__ == "__main__":
2088
2358
  sources = [(tools.expand_path(source), origin) for source, origin in sources]
2089
2359
 
2090
2360
  # Aggregated list of sources for each rule
2091
- rule_source_list = {}
2361
+ rule_source_list = OrderedDict()
2092
2362
  while sources:
2093
2363
  source, origin = sources.pop()
2094
2364
  _, ext = fs.path.splitext(source)
@@ -2185,25 +2455,39 @@ if __name__ == "__main__":
2185
2455
  incrementally. The behavior can be changed with the ``incremental``
2186
2456
  class attribute.
2187
2457
  """
2188
-
2189
2458
  self.outdir = tools.builddir("ninja", self.incremental)
2459
+ self.outdir_rel = self.tools.expand_relpath(self.outdir, tools.wsroot)
2190
2460
  self._expand_headers()
2191
2461
  self._expand_sources(deps, tools)
2192
2462
  self._writer = self._write_ninja_file(self.outdir, deps, tools)
2193
2463
  verbose = " -v" if log.is_verbose() else ""
2194
2464
  threads = config.get("jolt", "threads", tools.getenv("JOLT_THREADS", None))
2195
2465
  threads = " -j" + threads if threads else ""
2466
+ keep_going = " -k 0" if config.get_keep_going() else ""
2196
2467
  depsfile = self._get_keepdepfile(tools)
2197
2468
  try:
2198
- tools.run("ninja{3}{2} -C {0} {1}", self.outdir, verbose, threads, depsfile)
2469
+ self.buildlog = tools.run(
2470
+ "ninja{3}{2}{5} -C {0} -f {4} {1}",
2471
+ tools.wsroot,
2472
+ verbose,
2473
+ threads,
2474
+ depsfile,
2475
+ fs.path.join(self.outdir, "build.ninja"),
2476
+ keep_going,
2477
+ output=True,
2478
+ )
2479
+ self._report_errors(self.buildlog)
2199
2480
  except JoltCommandError as e:
2200
- with utils.ignore_exception(), self.report() as report:
2201
- self._report_errors(report, "\n".join(e.stdout))
2202
- raise CompileError()
2481
+ self.buildlog = "\n".join(e.stdout)
2482
+ report = self._report_errors(self.buildlog)
2483
+ error = self._first_reported_error(report)
2484
+ if error:
2485
+ raise CompileError(error)
2486
+ raise e
2203
2487
 
2204
2488
  if bool(getattr(self, "coverage", False)):
2205
2489
  self.covdatadir = tools.builddir("coverage-data")
2206
- with tools.cwd(self.outdir):
2490
+ with tools.cwd(tools.wsroot):
2207
2491
  for obj in getattr(self._writer, "_objects", []):
2208
2492
  obj, ext = os.path.splitext(obj)
2209
2493
  obj = obj + ".gcno"
@@ -2213,19 +2497,20 @@ if __name__ == "__main__":
2213
2497
  pass
2214
2498
  if self.selfsustained:
2215
2499
  for _, artifact in deps.items():
2216
- if str(artifact.paths.coverage_data):
2217
- tools.copy(artifact.paths.coverage_data, self.covdatadir)
2500
+ if artifact.paths.coverage_data:
2501
+ tools.copy(str(artifact.paths.coverage_data), self.covdatadir)
2218
2502
 
2219
2503
  def publish(self, artifact, tools):
2220
2504
  if bool(getattr(self, "coverage", False)):
2221
2505
  self.publish_coverage_data(artifact, tools)
2222
2506
 
2223
2507
  def publish_coverage_data(self, artifact, tools):
2508
+ """ Publishes code coverage data files. """
2224
2509
  with tools.cwd(self.covdatadir):
2225
2510
  if artifact.collect("**/*.gcno", "cov/"):
2226
2511
  artifact.paths.coverage_data = "cov"
2227
2512
 
2228
- def shell(self, deps, tools):
2513
+ def debugshell(self, deps, tools):
2229
2514
  """
2230
2515
  Invoked to start a debug shell.
2231
2516
 
@@ -2242,41 +2527,58 @@ if __name__ == "__main__":
2242
2527
  self._expand_headers()
2243
2528
  self._expand_sources(deps, tools)
2244
2529
  self.outdir = tools.builddir("ninja", self.incremental)
2530
+ self.outdir_rel = self.tools.expand_relpath(self.outdir, tools.wsroot)
2245
2531
  writer = self._write_ninja_file(self.outdir, deps, tools)
2246
2532
  self._write_shell_file(self.outdir, deps, tools, writer)
2247
2533
  pathenv = self.outdir + os.pathsep + tools.getenv("PATH")
2248
- with tools.cwd(self.outdir), tools.environ(PATH=pathenv):
2534
+ with tools.cwd(tools.wsroot), tools.environ(PATH=pathenv):
2249
2535
  print()
2250
2536
  print("Use the 'compile' command to build individual compilation targets")
2251
- super(CXXProject, self).shell(deps, tools)
2252
-
2253
- def _report_errors(self, report, logbuffer):
2254
- # GCC style errors
2255
- report.add_regex_errors_with_file(
2256
- "Compiler Error",
2257
- r"^(?P<location>(?P<file>.*?):(?P<line>[0-9]+):(?P<col>[0-9]+)): (?P<message>.*)",
2258
- logbuffer,
2259
- self.outdir,
2260
- lambda err: not err["message"].startswith("note:"))
2261
-
2262
- # MSVC compiler errors
2263
- report.add_regex_errors_with_file(
2264
- "Compiler Error",
2265
- r"^(?P<location>(?P<file>.*?)\((?P<line>[0-9]+)\)): (?P<message>(fatal )?error( C[0-9]*?): .*)",
2266
- logbuffer,
2267
- self.outdir)
2268
-
2269
- # Binutils/MSVC linker errors
2270
- report.add_regex_errors(
2271
- "Linker Error",
2272
- r"^(?P<location>(?P<file>.*?)(:.*?)?)( )?: (?P<message>(( fatal)?error LNK|warning LNK|undefined reference|multiple definition).*)",
2273
- logbuffer)
2274
-
2275
- # LLVM linker errors
2276
- report.add_regex_errors(
2277
- "Linker Error",
2278
- r"^(?P<location>ld): (error|warning): (?P<message>.*)",
2279
- logbuffer)
2537
+ super().debugshell(deps, tools)
2538
+
2539
+ def _report_errors(self, logbuffer):
2540
+ """ Parses the build log and reports errors. """
2541
+ with self.report() as report, utils.ignore_exception():
2542
+ # GCC style errors
2543
+ report.add_regex_errors_with_file(
2544
+ "Compiler Error",
2545
+ r"^(?P<location>(?P<file>.*?):(?P<line>[0-9]+):(?P<col>[0-9]+)): (?P<message>([^ ]*? )?(error:|[A-Z][0-9]*) .*)\n(?P<details>( ( |[0-9])*\| .*\n)+)?",
2546
+ logbuffer,
2547
+ self.outdir)
2548
+
2549
+ report.add_regex_errors_with_file(
2550
+ "Compiler Warning",
2551
+ r"^(?P<location>(?P<file>.*?):(?P<line>[0-9]+):(?P<col>[0-9]+)): (?P<message>([^ ]*? )?warning: .*)\n(?P<details>( ( |[0-9])*\| .*\n)+)?",
2552
+ logbuffer,
2553
+ self.outdir)
2554
+
2555
+ # MSVC compiler errors
2556
+ report.add_regex_errors_with_file(
2557
+ "Compiler Error",
2558
+ r"^(?P<location>(?P<file>.*?)\((?P<line>[0-9]+)\)): (?P<message>(fatal )?error( C[0-9]*?): .*)",
2559
+ logbuffer,
2560
+ self.outdir)
2561
+
2562
+ # Binutils/MSVC linker errors
2563
+ report.add_regex_errors(
2564
+ "Linker Error",
2565
+ r"^(?P<location>(?P<file>.*?)(:.*?)?)( )?: (?P<message>(( fatal)?error LNK|warning LNK|undefined reference|multiple definition).*)",
2566
+ logbuffer)
2567
+
2568
+ # LLVM linker errors
2569
+ report.add_regex_errors(
2570
+ "Linker Error",
2571
+ r"^(?P<location>(.*?)ld(\.lld)?): (error|warning): (?P<message>.*)",
2572
+ logbuffer)
2573
+
2574
+ return report
2575
+
2576
+ def _first_reported_error(self, report):
2577
+ """ Returns the first reported error or None if no errors were reported. """
2578
+ if report is None:
2579
+ return None
2580
+ for error in report.errors:
2581
+ return error
2280
2582
 
2281
2583
 
2282
2584
  class CXXLibrary(CXXProject):