Repository navigation
Expand file tree
/
Copy pathJenkinsfile
More file actions
1315 lines (1227 loc) · 62 KB
/
Copy pathJenkinsfile
File metadata and controls
1315 lines (1227 loc) · 62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
pipeline {
agent none
parameters {
booleanParam(name: 'OLDLIBS', defaultValue: false, description: 'Also test some outdated libraries')
booleanParam(name: 'v1_26', defaultValue: false, description: 'maintenance/v1.26 branch (ryzen-5950x-1)')
booleanParam(name: 'v1_27', defaultValue: false, description: 'maintenance/v1.27 branch (ryzen-5950x-1)')
booleanParam(name: 'master', defaultValue: false, description: 'master branch, simulated with the solver each model asks for, filling master (ryzen-9950x).')
booleanParam(name: 'fmi_v1_26', defaultValue: false, description: 'maintenance/v1.26 branch with FMI, simulated by OMSimulator, filling v1.26-fmi (ryzen-5950x-2). Ticking it together with fmpy_fmi_v1_26 runs one job that builds every FMU once and simulates it with both.')
booleanParam(name: 'fmi_v1_27', defaultValue: false, description: 'maintenance/v1.27 branch with FMI, simulated by OMSimulator, filling v1.27-fmi (ryzen-5950x-2). Ticking it together with fmpy_fmi_v1_27 runs one job that builds every FMU once and simulates it with both.')
booleanParam(name: 'fmi_master', defaultValue: false, description: 'master branch with FMI, simulated by OMSimulator, filling master-fmi (ryzen-5950x-2). Ticking it together with fmpy_fmi_master runs one job that builds every FMU once and simulates it with both.')
booleanParam(name: 'cs_fmu_cvode_v1_26', defaultValue: false, description: 'maintenance/v1.26 branch with CVODE CS FMUs running OMSimulator (ryzen-5950x-2)')
booleanParam(name: 'cs_fmu_cvode_v1_27', defaultValue: false, description: 'maintenance/v1.27 branch with CVODE CS FMUs running OMSimulator (ryzen-5950x-2)')
booleanParam(name: 'cs_fmu_cvode_master', defaultValue: false, description: 'master branch with CVODE CS FMUs running OMSimulator (ryzen-5950x-2)')
booleanParam(name: 'fmpy_fmi_v1_26', defaultValue: false, description: 'maintenance/v1.26 branch with FMI, simulated by FMPy, filling v1.26-fmi-fmpy (ryzen-5950x-2). Ticking it together with fmi_v1_26 runs one job that builds every FMU once and simulates it with both.')
booleanParam(name: 'fmpy_fmi_v1_27', defaultValue: false, description: 'maintenance/v1.27 branch with FMI, simulated by FMPy, filling v1.27-fmi-fmpy (ryzen-5950x-2). Ticking it together with fmi_v1_27 runs one job that builds every FMU once and simulates it with both.')
booleanParam(name: 'fmpy_fmi_master', defaultValue: false, description: 'master branch with FMI, simulated by FMPy, filling master-fmi-fmpy (ryzen-5950x-2). Ticking it together with fmi_master runs one job that builds every FMU once and simulates it with both.')
booleanParam(name: 'newInst_daeMode', defaultValue: false, description: 'master branch, --daeMode with -d=newInst (ryzen-5950x-2)')
booleanParam(name: 'newInst_newBackend', defaultValue: false, description: 'master branch, -d=newInst --newBackend, (ryzen-5950x-1)')
booleanParam(name: 'newBackend_daeMode', defaultValue: false, description: 'master branch, --newBackend --daeMode with -d=newInst (ryzen-5950x-2). This is an experimental job that does not run on a fixed schedule.')
booleanParam(name: 'oldInst', defaultValue: false, description: 'master branch, with -d=nonewInst (ryzen-5950x-2)')
booleanParam(name: 'cpp_v1_26', defaultValue: false, description: 'maintenance/v1.26 branch, with --simCodeTarget=Cpp (ryzen-5950x-2).')
booleanParam(name: 'cpp_v1_27', defaultValue: false, description: 'maintenance/v1.27 branch, with --simCodeTarget=Cpp (ryzen-5950x-2).')
booleanParam(name: 'cpp', defaultValue: false, description: 'master branch, with --simCodeTarget=Cpp (ryzen-5950x-2).')
booleanParam(name: 'report_ryzen_5950x_1', defaultValue: false, description: 'Generate a report for ryzen-5950x-1 without running the tests')
booleanParam(name: 'report_ryzen_5950x_2', defaultValue: false, description: 'Generate a report for ryzen-5950x-2 without running the tests')
booleanParam(name: 'conversion_script', defaultValue: false, description: 'master branch with conversion script from MSL 3 to 4 (ryzen-5950x-1). This is an experimental job that does not run on a fixed schedule.')
booleanParam(name: 'solvers', defaultValue: false, description: 'master branch, simulated with -s cvode, -s gbode and -s ida (ryzen-9950x). This is an experimental job that does not run on a fixed schedule.')
booleanParam(name: 'generateSymbolicJacobian', defaultValue: false, description: 'master branch, with --generateSymbolicJacobian (ryzen-5950x-1). This is an experimental job that does not run on a fixed schedule.')
booleanParam(name: 'wasm_jit', defaultValue: false, description: 'master branch, with --simCodeTarget=wasm-jit (ryzen-9950x). Every model is translated and simulated the way simulate() does, filling wasm-jit. This is an experimental job that does not run on a fixed schedule.')
booleanParam(name: 'wasm_web', defaultValue: false, description: 'master branch, with --simCodeTarget=wasm-jit (ryzen-9950x), simulated the way the web omc does: the driver and the linear and nonlinear solvers in wasm, the in-wasm libm, and external "C" only from prebuilt wasm libraries. Fills wasm-web. This is an experimental job that does not run on a fixed schedule.')
booleanParam(name: 'wasm_fmu', defaultValue: false, description: 'master branch, with --simCodeTarget=wasm-jit (ryzen-9950x). Every model is exported once as an unzipped wasm FMU and simulated through both of its FMI 3.0 interfaces, filling wasm-jit-me (Model Exchange) and wasm-jit-cs (Co-Simulation). This is an experimental job that does not run on a fixed schedule.')
booleanParam(name: 'c_plus_rust', defaultValue: false, description: 'master branch, with --simCodeTarget=C+Rust, filling c-plus-rust (ryzen-9950x). The C code generator emits what it does for --simCodeTarget=C; the executable links libSimulationRuntimeRust instead of libSimulationRuntimeC, so the difference against master is the simulation runtime alone. This is an experimental job that does not run on a fixed schedule.')
booleanParam(name: 'heavy_tests', defaultValue: false, description: 'master branch, runs one test at a time. That is, no parallel launching of tests. omc will use multiple threads for each test (-n=1 is not set unlike the other regression tests.), (r7945). This is an experimental job that does not run on a fixed schedule.')
booleanParam(name: 'heavy_tests_wasm', defaultValue: false, description: 'heavy_tests with --simCodeTarget=wasm-jit, filling heavy_tests_wasm (r7945). Its results are in the heavy_tests report. This is an experimental job that does not run on a fixed schedule.')
string(name: 'pull_request', defaultValue: '', description: 'Test an OpenModelica pull request rather than a branch: its number, e.g. 16354. omc is built from refs/pull/<N>/merge - the pull request as it would land - the results fill a pr-<N> table, and the report compares them against the newest run of pull_request_baseline. Left empty, nothing of this runs.')
choice(name: 'pull_request_target', choices: ['wasm-jit', 'C'], description: 'The --simCodeTarget a pull request is simulated with. wasm-jit is the cheaper run and is compared against the wasm-jit job; C against master.')
choice(name: 'pull_request_solver', choices: ['default', 'gbode', 'cvode', 'ida'], description: 'The solver a pull request is simulated with: default is the one each model asks for, the others are the configs/solvers.json entries of that name. With the C target, the baseline is then that solver\'s table, which the solvers job fills; nothing fills one for wasm-jit, so set pull_request_baseline yourself or compare against the default solver.')
string(name: 'pull_request_simflags', defaultValue: '', description: 'Simulation flags appended to every model\'s, after those of pull_request_solver, e.g. -gbm=esdirk3 to run gbode with another method.')
string(name: 'pull_request_baseline', defaultValue: '', description: 'The branch a pull request is compared against. Left empty, the one pull_request_target is tested by: wasm-jit for wasm-jit, master for C, or for C with a pull_request_solver its own table. Its newest run has to come from pull_request_node, or the report compares the machines as much as the pull request.')
string(name: 'pull_request_config', defaultValue: 'configs/conf.json', description: 'What a pull request run tests. A full run takes days, so a smaller configuration file is often the better question to ask.')
choice(name: 'pull_request_node', choices: ['ryzen-9950x', 'ryzen-5950x-1', 'ryzen-5950x-2-1', 'r7945-1'], description: 'The machine a pull request runs on. The default is the one that produces the master and wasm-jit runs it is compared against.')
booleanParam(name: 'pull_request_comment', defaultValue: false, description: 'Post the summary of a pull request run as a comment on the pull request, replacing the one an earlier run posted. Needs an OpenModelica-Hudson credential; without it the report is still written and published, only not commented.')
booleanParam(name: 'drop_stale_pull_request_tables', defaultValue: false, description: 'Drop the pr-<N> tables of pull requests that have been merged or closed, and of those tested more than 60 days ago. The reports published for them are not touched.')
}
environment {
LC_ALL = 'C.UTF-8'
// Where the results go. The scripts take it from here instead of a --db
// option on every single invocation.
LIBTEST_DB = 'postgresql://om@openmodelica.org/omdb'
}
stages {
stage('test') { parallel {
stage('v1.26') {
agent {
node {
label 'ryzen-5950x-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.v1_26 }
}
steps {
runRegressiontest('maintenance/v1.26', 'v1.26', '', '', false, '', '', false, false)
}
}
stage('v1.27') {
agent {
node {
label 'ryzen-5950x-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.v1_27 }
}
steps {
runRegressiontest('maintenance/v1.27', 'v1.27', '', '', false, '', '', false, false)
}
}
stage('master') {
agent {
node {
label 'ryzen-9950x'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.master }
}
steps {
runRegressiontest('master', 'master', '', '', false, '', '', false, false, 0, 'configs/conf.json', '', '.CI/testing', null, null)
}
}
stage('solvers') {
agent {
node {
label 'ryzen-9950x'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.solvers }
}
steps {
runRegressiontest('master', 'master', '', '', false, '', '', false, false, 0, 'configs/conf.json', '', '.CI/testing', null,
['cvode', 'gbode', 'ida'])
}
}
stage('conversion script') {
agent {
node {
label 'ryzen-5950x-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.conversion_script }
}
steps {
runRegressiontest('master', 'conversion', '', '', false, '', '', false, true)
}
}
stage('pull request') {
agent {
node {
label "${params.pull_request_node ?: 'ryzen-9950x'}"
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { pullRequest() }
}
steps {
script {
if (!(pullRequest() ==~ /[0-9]+/)) {
error "pull_request is a pull request number; got '${params.pull_request}'"
}
if (pullRequestSimflags().contains("'")) {
error "pull_request_simflags cannot contain a single quote; got '${pullRequestSimflags()}'"
}
}
// Before the clone, the reset and the build: a number that is not a
// pull request costs minutes to find out about otherwise. Issues and
// pull requests share one numbering, so an issue number gets this far.
sh """
if ! git ls-remote --exit-code https://github.com/OpenModelica/OpenModelica.git 'refs/pull/${pullRequest()}/*' > /dev/null; then
echo "OpenModelica/OpenModelica has no pull request ${pullRequest()}. Issues and pull requests share one numbering there, so check that ${pullRequest()} is not the number of an issue."
exit 1
fi
"""
// One build of omc per pull request is kept, as for a branch, and they
// accumulate: a pull request is tested once and never again. The
// second line is the layout of the runs before they moved under pr/.
sh '''
find "$HOME/saved_omc/pr" -mindepth 1 -maxdepth 1 -type d -mtime +14 -exec rm -rf {} ";" 2> /dev/null || true
find "$HOME/saved_omc" -mindepth 1 -maxdepth 1 -name "pr-*" -type d -mtime +14 -exec rm -rf {} ";" || true
'''
script {
def config = params.pull_request_config ?: 'configs/conf.json'
def solverFlags = pullRequestSolver() == 'default' ? '' : "--solverflags=${pullRequestSolver()}"
if (pullRequestTarget() == 'C') {
runRegressiontest("pr/${pullRequest()}", "pr/${pullRequest()}", '', '', false, pullRequestSimflags(), solverFlags, false, false, 0, config)
} else {
runRegressiontest("pr/${pullRequest()}", "pr/${pullRequest()}", 'setCommandLineOptions("--simCodeTarget=wasm-jit")', '', false, pullRequestSimflags(), "--nobuildmodel ${solverFlags}", false, false, 0, config,
wasmCmakeFlags())
}
}
}
}
stage('newInst-newBackend') {
agent {
node {
label 'ryzen-5950x-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.newInst_newBackend }
}
steps {
runRegressiontest('master', 'newInst-newBackend', 'setCommandLineOptions("-d=newInst,-frontEndUnitCheck --newBackend")', '', false, '', '', false, false)
}
}
stage('v1.26 FMI') {
agent {
node {
label 'ryzen-5950x-2-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.fmi_v1_26 || params.fmpy_fmi_v1_26 }
}
steps {
runRegressiontest('maintenance/v1.26', 'v1.26-fmi', '', omsimulatorHash(), false, '', '', false, false, 0, 'configs/conf.json', '', '', fmiSimulators(params.fmi_v1_26, params.fmpy_fmi_v1_26))
}
}
stage('v1.27 FMI') {
agent {
node {
label 'ryzen-5950x-2-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.fmi_v1_27 || params.fmpy_fmi_v1_27 }
}
steps {
runRegressiontest('maintenance/v1.27', 'v1.27-fmi', '', omsimulatorHash(), false, '', '', false, false, 0, 'configs/conf.json', '', '', fmiSimulators(params.fmi_v1_27, params.fmpy_fmi_v1_27))
}
}
stage('master FMI') {
agent {
node {
label 'ryzen-5950x-2-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.fmi_master || params.fmpy_fmi_master }
}
steps {
runRegressiontest('master', 'master-fmi', '', 'origin/master', false, '', '', false, false, 0, 'configs/conf.json', '', '', fmiSimulators(params.fmi_master, params.fmpy_fmi_master))
}
}
stage('v1.26 CVODE CS-FMUs with OMSimulator') {
agent {
node {
label 'ryzen-5950x-2-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.cs_fmu_cvode_v1_26 }
}
steps {
runRegressiontest('maintenance/v1.26', 'v1.26-cs-fmu-cvode', 'setCommandLineOptions("--fmiFlags=s:cvode --fmuRuntimeDepends=modelica")', omsimulatorHash(), false, '', '', false, false)
}
}
stage('v1.27 CVODE CS-FMUs with OMSimulator') {
agent {
node {
label 'ryzen-5950x-2-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.cs_fmu_cvode_v1_27 }
}
steps {
runRegressiontest('maintenance/v1.27', 'v1.27-cs-fmu-cvode', 'setCommandLineOptions("--fmiFlags=s:cvode --fmuRuntimeDepends=modelica")', omsimulatorHash(), false, '', '', false, false)
}
}
stage('master CVODE CS-FMUs with OMSimulator') {
agent {
node {
label 'ryzen-5950x-2-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.cs_fmu_cvode_master }
}
steps {
runRegressiontest('master', 'master-cs-fmu-cvode', 'setCommandLineOptions("--fmiFlags=s:cvode --fmuRuntimeDepends=modelica")', 'origin/master', false, '', '', false, false)
}
}
stage('newInst-daeMode') {
agent {
node {
label 'ryzen-5950x-2-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.newInst_daeMode }
}
steps {
runRegressiontest('master', 'newInst-daeMode', 'setCommandLineOptions("-d=newInst,-frontEndUnitCheck --daeMode=true")', '', false, '', '', false, false)
}
}
stage('newBackend-daeMode') {
agent {
node {
label 'ryzen-5950x-2-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.newBackend_daeMode }
}
steps {
runRegressiontest('master', 'newBackend-daeMode', 'setCommandLineOptions("-d=newInst,-frontEndUnitCheck --newBackend --daeMode=true")', '', false, '', '', false, false)
}
}
stage('oldInst') {
agent {
node {
label 'ryzen-5950x-2-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.oldInst }
}
steps {
runRegressiontest('master', 'oldInst', 'setCommandLineOptions("-d=nonewInst")', '', false, '', '', false, false)
}
}
stage('wasm-jit') {
agent {
node {
label 'ryzen-9950x'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.wasm_jit }
}
steps {
// --nobuildmodel: the model is translated, compiled and simulated by one simulate(),
// so omc reports the JIT compile as the build time rather than leaving it in the
// simulation the way a resimulate does.
runRegressiontest('master', 'wasm-jit', 'setCommandLineOptions("--simCodeTarget=wasm-jit")', '', false, '', '--nobuildmodel', false, false, 0, 'configs/conf.json',
wasmCmakeFlags())
}
}
stage('wasm-fmu') {
agent {
node {
label 'ryzen-9950x'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.wasm_fmu }
}
steps {
// --branch=wasm-jit and no runner of its own fills that table: the two runners write
// wasm-jit-me and wasm-jit-cs, beside the wasm-jit the stage above fills.
runRegressiontest('master', 'wasm-jit', 'setCommandLineOptions("--simCodeTarget=wasm-jit")', '', false, '', '--wasmfmu=me,cs', false, false, 0, 'configs/conf.json',
wasmCmakeFlags())
}
}
stage('wasm-web') {
agent {
node {
label 'ryzen-9950x'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.wasm_web }
}
steps {
// Its own build (~/saved_omc/wasm-web): the runtime links the web's in-wasm
// driver and solvers, which the environment below selects.
withEnv(wasmWebEnv()) {
runRegressiontest('master', 'wasm-web', 'setCommandLineOptions("--simCodeTarget=wasm-jit")', '', false, '', '--nobuildmodel', false, false, 0, 'configs/conf.json',
wasmCmakeFlags() + ' -DRUST_OMC_WASM_INWASM_DRIVER=ON')
}
}
}
stage('C+Rust') {
agent {
node {
label 'ryzen-9950x'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.c_plus_rust }
}
steps {
runRegressiontest('master', 'c-plus-rust', 'setCommandLineOptions("--simCodeTarget=C+Rust")', '', false, '', '', false, false)
}
}
stage('generateSymbolicJacobian') {
agent {
node {
label 'ryzen-5950x-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.generateSymbolicJacobian }
}
steps {
runRegressiontest('master', 'generateSymbolicJacobian', 'setCommandLineOptions("--generateSymbolicJacobian")', '', false, '', '', false, false)
}
}
stage('heavy_tests') {
agent {
node {
label 'r7945-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.heavy_tests }
}
steps {
runRegressiontest('master', 'heavy_tests', '', '', false, '', '', false, false, 1, 'configs/heavy_tests.json')
}
}
stage('heavy_tests_wasm') {
agent {
node {
label 'r7945-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.heavy_tests_wasm }
}
steps {
runRegressiontest('master', 'heavy_tests_wasm', 'setCommandLineOptions("--simCodeTarget=wasm-jit")', '', false, '', '--nobuildmodel', false, false, 1, 'configs/heavy_tests.json',
wasmCmakeFlags())
}
}
stage('C++ v1.26') {
agent {
node {
label 'ryzen-5950x-2-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.cpp_v1_26 }
}
steps {
runRegressiontest('maintenance/v1.26', 'v1.26-cpp', 'setCommandLineOptions("--simCodeTarget=Cpp")', '', false, '', '', false, false)
}
}
stage('C++ v1.27') {
agent {
node {
label 'ryzen-5950x-2-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.cpp_v1_27 }
}
steps {
runRegressiontest('maintenance/v1.27', 'v1.27-cpp', 'setCommandLineOptions("--simCodeTarget=Cpp")', '', false, '', '', false, false)
}
}
stage('C++') {
agent {
node {
label 'ryzen-5950x-2-1'
customWorkspace 'ws/OpenModelicaLibraryTestingWork'
}
}
options { skipDefaultCheckout() }
when {
beforeAgent true
expression { params.cpp }
}
steps {
runRegressiontest('master', 'cpp', 'setCommandLineOptions("--simCodeTarget=Cpp")', '', false, '', '', false, false)
}
}
} }
stage('report') {
agent {
dockerfile {
label 'linux'
dir '.CI/build-dep'
customWorkspace 'ws/OpenModelicaLibraryTestingReport'
args '-e IDA_EMAIL_USR=$IDA_EMAIL_USR -e IDA_EMAIL_PSW=$IDA_EMAIL_PSW'
}
}
when {
beforeAgent true
expression { params.v1_25 || params.v1_26 || params.v1_27 || params.master || params.conversion_script || params.report_ryzen_5950x_1 || params.report_ryzen_5950x_2 || params.newInst_newBackend || params.generateSymbolicJacobian || params.heavy_tests || params.heavy_tests_wasm || params.fmi_v1_25 || params.fmi_v1_26 || params.fmi_v1_27 || params.fmi_master || params.fmpy_fmi_v1_25 || params.fmpy_fmi_v1_26 || params.fmpy_fmi_v1_27 || params.fmpy_fmi_master || params.newInst_daeMode || params.newBackend_daeMode || params.oldInst || params.cpp || params.solvers || params.wasm_jit || params.wasm_fmu || params.wasm_web || params.c_plus_rust}
}
environment {
GITBRANCHES = 'maintenance/v1.20 maintenance/v1.21 maintenance/v1.22 maintenance/v1.23 maintenance/v1.24 maintenance/v1.25 maintenance/v1.26 maintenance/v1.27 master newInst-newBackend'
GITBRANCHES_FMI = 'maintenance/v1.20-fmi maintenance/v1.21-fmi maintenance/v1.22-fmi maintenance/v1.23-fmi maintenance/v1.24-fmi maintenance/v1.25-fmi maintenance/v1.26-fmi maintenance/v1.27-fmi maintenance/v1.22-fmi-fmpy maintenance/v1.23-fmi-fmpy maintenance/v1.24-fmi-fmpy maintenance/v1.25-fmi-fmpy maintenance/v1.26-fmi-fmpy maintenance/v1.27-fmi-fmpy master-fmi master-fmi-fmpy'
GITBRANCHES_NEWINST = 'oldInst'
GITBRANCHES_DAE = 'newInst-daeMode'
GITBRANCHES_NEWBACKEND_DAE = 'newBackend-daeMode'
GITBRANCHES_CPP = 'v1.19-cpp v1.20-cpp v1.21-cpp v1.22-cpp v1.23-cpp v1.24-cpp v1.25-cpp cpp v1.26-cpp v1.27-cpp'
GITBRANCHES_WASM_JIT = 'wasm-jit-me wasm-jit-cs wasm-jit wasm-web'
GITBRANCHES_C_PLUS_RUST = 'c-plus-rust'
GITBRANCHES_SPECIAL = 'master newInst-newBackend'
PYTHONIOENCODING = 'utf-8'
IDA_EMAIL = credentials('IDA email')
// A secret file holding one pgpass line; libpq reads the password from
// it, so it never reaches a command line or the build log. It is bound
// per stage because writing the file needs the workspace of a node,
// which the pipeline as a whole does not have (agent none).
PGPASSFILE = credentials('omdb-pgpass')
}
steps {
sh 'rm -rf *.html history pending-reports.json'
sh '''
if ! test -d OpenModelica; then
git clone https://openmodelica.org/git-readonly/OpenModelica.git
fi
cd OpenModelica
git fetch
'''
sh './clean-empty-omcversion-dates.py'
sh "./all-reports.py --email --omcgitdir=OpenModelica ${env.GITBRANCHES} ${env.GITBRANCHES_FMI} ${env.GITBRANCHES_NEWINST} ${env.GITBRANCHES_DAE} ${env.GITBRANCHES_NEWBACKEND_DAE} ${env.GITBRANCHES_CPP} ${env.GITBRANCHES_WASM_JIT} ${env.GITBRANCHES_C_PLUS_RUST} conversion heavy_tests heavy_tests_wasm generateSymbolicJacobian gbode cvode ida"
sh "./all-plots.py ${env.GITBRANCHES} ${env.GITBRANCHES_FMI} ${env.GITBRANCHES_NEWINST} ${env.GITBRANCHES_DAE} ${env.GITBRANCHES_NEWBACKEND_DAE} ${env.GITBRANCHES_CPP} ${env.GITBRANCHES_WASM_JIT} ${env.GITBRANCHES_C_PLUS_RUST} conversion heavy_tests heavy_tests_wasm generateSymbolicJacobian gbode cvode ida"
// Recorded and announced only once they are up: a report the database
// knows about is never generated again, so one that a later failure in
// this stage stops from being uploaded would be lost for good.
sshPublisher(publishers: [sshPublisherDesc(configName: 'LibraryTestingReports', transfers: [sshTransfer(sourceFiles: 'history/**')])])
sh './publish-reports.py'
sh "./report.py --branches='${env.GITBRANCHES} ${env.GITBRANCHES_WASM_JIT}' configs/conf.json configs/conf-old.json configs/conf-nonstandard.json"
sh 'mv overview.html overview-combined.html'
sh "./report.py --branches='${env.GITBRANCHES} ${env.GITBRANCHES_WASM_JIT}' configs/conf-old.json"
sh "mv overview.html overview-old-libs.html"
sh "./report.py --branches='${env.GITBRANCHES} ${env.GITBRANCHES_WASM_JIT}' configs/conf-nonstandard.json"
sh "mv overview.html overview-nonstandard-libs.html"
sh "./report.py --branches='${env.GITBRANCHES_SPECIAL} conversion' configs/conf.json"
sh "mv overview.html overview-special-jobs.html"
sh "./report.py --branches='generateSymbolicJacobian' configs/conf.json"
sh "mv overview.html overview-generateSymbolicJacobian.html"
sh "./report.py --branches='heavy_tests heavy_tests_wasm' configs/heavy_tests.json"
sh "mv overview.html overview-heavy_tests.html"
sh "./report.py --branches='${env.GITBRANCHES_NEWINST}' configs/conf.json"
sh "mv overview.html overview-oldinst.html"
sh "./report.py --branches='${env.GITBRANCHES_NEWINST}' configs/conf.json configs/conf-old.json configs/conf-nonstandard.json"
sh "mv overview.html overview-combined-oldinst.html"
sh "./report.py --branches='${env.GITBRANCHES_NEWINST}' configs/conf-old.json"
sh "mv overview.html overview-old-libs-oldinst.html"
sh "./report.py --branches='${env.GITBRANCHES_NEWINST}' configs/conf-nonstandard.json"
sh "mv overview.html overview-nonstandard-libs-oldinst.html"
sh "./report.py --branches='${env.GITBRANCHES_FMI}' configs/conf.json"
sh "mv overview.html overview-fmi.html"
sh "./report.py --branches='${env.GITBRANCHES_FMI}' configs/conf.json configs/conf-old.json configs/conf-nonstandard.json"
sh "mv overview.html overview-combined-fmi.html"
sh "./report.py --branches='${env.GITBRANCHES_FMI}' configs/conf-old.json"
sh "mv overview.html overview-old-libs-fmi.html"
sh "./report.py --branches='${env.GITBRANCHES_FMI}' configs/conf-nonstandard.json"
sh "mv overview.html overview-nonstandard-libs-fmi.html"
sh "./report.py --branches='${env.GITBRANCHES_DAE}' configs/conf.json"
sh "mv overview.html overview-dae.html"
sh "./report.py --branches='${env.GITBRANCHES_DAE}' configs/conf.json configs/conf-old.json configs/conf-nonstandard.json"
sh "mv overview.html overview-combined-dae.html"
sh "./report.py --branches='${env.GITBRANCHES_DAE}' configs/conf-old.json"
sh "mv overview.html overview-old-libs-dae.html"
sh "./report.py --branches='${env.GITBRANCHES_DAE}' configs/conf-nonstandard.json"
sh "mv overview.html overview-nonstandard-libs-dae.html"
sh "./report.py --branches='${env.GITBRANCHES_NEWBACKEND_DAE}' configs/conf.json"
sh "mv overview.html overview-newbackend-dae.html"
sh "./report.py --branches='${env.GITBRANCHES_NEWBACKEND_DAE}' configs/conf.json configs/conf-old.json configs/conf-nonstandard.json"
sh "mv overview.html overview-combined-newbackend-dae.html"
sh "./report.py --branches='${env.GITBRANCHES_NEWBACKEND_DAE}' configs/conf-old.json"
sh "mv overview.html overview-old-libs-newbackend-dae.html"
sh "./report.py --branches='${env.GITBRANCHES_NEWBACKEND_DAE}' configs/conf-nonstandard.json"
sh "mv overview.html overview-nonstandard-libs-newbackend-dae.html"
sh "./report.py --branches='cvode gbode ida master' configs/conf.json"
sh "mv overview.html overview-solvers.html"
// The ways a wasm-jit model is simulated, against master: the plain
// simulation path, the two FMI 3.0 interfaces of one exported wasm FMU,
// which share an export and differ only in simulation and verification,
// and the web omc's in-wasm path.
sh "./report.py --branches='master ${env.GITBRANCHES_WASM_JIT}' configs/conf.json"
sh "mv overview.html overview-wasm-jit.html"
sh "./report.py --branches='${env.GITBRANCHES_CPP}' configs/conf.json"
sh "mv overview.html overview-c++.html"
sh "./report.py --branches='${env.GITBRANCHES} ${env.GITBRANCHES_C_PLUS_RUST}' configs/conf.json"
sh 'date'
sh 'find overview*.html history -type f | wc -l'
sh 'find overview*.html history'
sshPublisher(publishers: [sshPublisherDesc(configName: 'LibraryTestingReports', transfers: [sshTransfer(sourceFiles: 'overview*.html')])])
}
}
stage('drop stale pull request tables') {
agent {
dockerfile {
label 'linux'
dir '.CI/build-dep'
customWorkspace 'ws/OpenModelicaLibraryTestingReport'
}
}
when {
beforeAgent true
expression { params.drop_stale_pull_request_tables ?: false }
}
environment {
PGPASSFILE = credentials('omdb-pgpass')
}
steps {
sh './drop-pr-tables.py --yes'
}
}
/* A pull request is not reported on by the stage above: that one compares a
* branch against its own previous run, which a pull request has none of, and
* regenerating every overview for a job that tested one thing would take
* longer than the report itself. */
stage('pull request report') {
agent {
dockerfile {
label 'linux'
dir '.CI/build-dep'
customWorkspace 'ws/OpenModelicaLibraryTestingReport'
}
}
when {
beforeAgent true
expression { pullRequest() }
}
environment {
PYTHONIOENCODING = 'utf-8'
PGPASSFILE = credentials('omdb-pgpass')
}
steps {
script {
if (!(pullRequest() ==~ /[0-9]+/)) {
error "pull_request is a pull request number; got '${params.pull_request}'"
}
}
sh 'rm -rf history'
sh "./pr-report.py '${pullRequest()}' --baseline='${pullRequestBaseline()}'"
// Uploaded before it is announced, as the branch reports are: the
// comment links to the report, and nothing after this may be what stops
// it from being published.
sshPublisher(publishers: [sshPublisherDesc(configName: 'LibraryTestingReports', transfers: [sshTransfer(sourceFiles: 'history/**')])])
// The summary is in the build log as well, so a run without a token
// still leaves it somewhere to copy from.
sh "cat 'history/pr/${pullRequest()}/00_comment.md'"
script {
if (params.pull_request_comment) {
// Whoever the token belongs to is who the comment comes from.
withCredentials([string(credentialsId: 'OpenModelica-Hudson', variable: 'GITHUB_TOKEN')]) {
sh "./pr-report.py '${pullRequest()}' --comment-only"
}
}
}
}
}
}
}
/**
* The pull request this job is testing, or "" when it is testing branches.
*
* A job only learns of a parameter that has been added to it once a build has
* run with the definition, so the first build after this file changes sees the
* new ones as null - which is every build of the pipeline, not only one asking
* for a pull request, because the stages that ignore them still have to decide
* whether to run. Everything that reads them therefore falls back to what the
* definition says the default is.
*/
def pullRequest() {
return (params.pull_request ?: '').trim()
}
def pullRequestTarget() {
return params.pull_request_target ?: 'wasm-jit'
}
def pullRequestSolver() {
return params.pull_request_solver ?: 'default'
}
def pullRequestSimflags() {
return (params.pull_request_simflags ?: '').trim()
}
def pullRequestBaseline() {
def baseline = (params.pull_request_baseline ?: '').trim()
if (baseline) {
return baseline
}
if (pullRequestTarget() == 'C') {
return pullRequestSolver() == 'default' ? 'master' : pullRequestSolver()
}
return 'wasm-jit'
}
/**
* The cores of the node a build is running on, physical and logical, asked for
* the way the OpenModelica job asks (numPhysicalCPU in .CI/common.groovy
* there), so that a machine is built on as itself rather than as the machine
* the numbers were written for.
*
* Unlike that one the answer is not stashed in the environment: this pipeline
* is agent none and its stages run on several machines within one build, so a
* cached answer would be the first node's. An override set on the node itself
* is still honoured.
*/
def numPhysicalCPU() {
if (env.JENKINS_NUM_PHYSICAL_CPU) {
return env.JENKINS_NUM_PHYSICAL_CPU
}
return sh(script: 'lscpu -p | egrep -v "^#" | sort -u -t, -k 2,4 | wc -l', returnStdout: true).trim()
}
def numLogicalCPU() {
if (env.JENKINS_NUM_LOGICAL_CPU) {
return env.JENKINS_NUM_LOGICAL_CPU
}
return sh(script: 'nproc', returnStdout: true).trim()
}
def omsimulatorHash() {
return 'master'
}
/**
* Installs the libraries a run tests, with the image's /usr/bin/omc, and returns the home
* directory holding them. The conversion script uses the omc that was just built instead.
*/
def installLibraries(boolean removePackageOrder, boolean conversionScript, name, String omhomeTestedOMC, Closure runSh) {
sh "rm -rf '${env.HOME}/saved_omc/libraries/.openmodelica/libraries'"
sh "mkdir -p '${env.HOME}/saved_omc/libraries/'"
runSh("HOME='${env.HOME}/saved_omc/libraries/' /usr/bin/omc OpenModelicaLibraryTesting/.CI/installLibraries.mos")
if (removePackageOrder) {
sh "find '${env.HOME}/saved_omc/libraries/' -name package.order -exec rm '{}' ';'"
}
echo "installLibraries removePackageOrder: ${removePackageOrder} conversionScript: ${conversionScript} name: ${name}"
if (conversionScript) {
runSh("""
cd '${WORKSPACE}/OpenModelicaLibraryTesting'
OPENMODELICAHOME="${omhomeTestedOMC}" ./conversionscript.py --diff --allowErrorsInDiff '${env.HOME}/saved_omc/libraries/.openmodelica/libraries'
scp converted-libraries/.openmodelica/libraries/*.diff 'libraries.openmodelica.org:/var/www/libraries.openmodelica.org/branches/${name}'
""")
return "${WORKSPACE}/OpenModelicaLibraryTesting/converted-libraries"
} else {
return "${env.HOME}/saved_omc/libraries"
}
}
/**
* `docker run` flags capping the container's cgroup at 90% of the node's RAM. test.py limits one
* test at a time, not the sum of the parallel ones. --memory-swap has to repeat the limit; docker
* reads 0 as "unset" and then allows swapping.
*/
def memoryLimitArgs() {
def mb = sh(script: '''awk '/^MemTotal:/ { print int($2 / 1024 * 0.85) }' /proc/meminfo''',
returnStdout: true).trim()
echo "Test container memory limit: ${mb} MB, no swap"
return "--memory=${mb}m --memory-swap=${mb}m"
}
/**
* Runs `body` with the environment of the shared Rust compile cache of the OpenModelica job
* (OpenModelica/.CI/sccache/), which builds the same commits first. Hitting its keys needs the
* same rust settings (opt-level 2, -DRUST_OMC_THREADS=4 at the call site), the same toolchain
* (hence the same docker image) and SCCACHE_BASEDIRS set to the checkout directory, which is a
* subdirectory of the workspace here but the workspace itself there.
*
* The server is started by sccachePreamble() instead, since a build in a container cannot use
* one started outside it.
*/
def withSccache(Closure body) {
withCredentials([string(credentialsId: 'sccache-ci-secret-key',
variable: 'AWS_SECRET_ACCESS_KEY')]) {
withEnv(['RUSTC_WRAPPER=sccache',
'SCCACHE_BUCKET=omc-sccache',
'SCCACHE_ENDPOINT=https://sccache.openmodelica.org',
'SCCACHE_REGION=auto',
'SCCACHE_S3_USE_SSL=true',
'AWS_ACCESS_KEY_ID=sccache-ci',
'CARGO_INCREMENTAL=0',
'CARGO_PROFILE_RELEASE_OPT_LEVEL=2',
"SCCACHE_BASEDIRS=${env.WORKSPACE}/OpenModelica"]) {
body()
}
}
}
/**
* Prepended to a build running under withSccache: starts a server with the current environment
* (a running one keeps the one it was started with), or drops RUSTC_WRAPPER if there is no sccache.
*/
def sccachePreamble() {
return '''
if command -v sccache > /dev/null; then
log="`mktemp`"
sccache --stop-server > /dev/null 2>&1 || true
SCCACHE_ERROR_LOG="$log" SCCACHE_LOG=warn sccache --start-server
sccache --show-stats
if grep -qiE "storage (write )?check failed|read-only storage|cache storage failed" "$log"; then
echo "WARNING: the sccache S3 backend is unusable; building without a shared cache:" >&2
cat "$log" >&2
fi
rm -f "$log"
else
echo "sccache was not found; building without a compile cache"
unset RUSTC_WRAPPER
fi
'''
}
/**
* Whether omc is built with cmake for `branch`, rather than with Autoconf and Makefiles.
*
* The Autoconf+Make build is being removed from OpenModelica (OpenModelica/OpenModelica#14387),
* so master - and the pull requests that would be merged into it - is built with cmake. The
* maintenance branches are still released from the Autoconf build and keep it until they are
* dropped.
*/
def buildsWithCMake(branch) {
return !branch.startsWith('maintenance/')
}
/**
* Launches the test.py script with the given options.
*
* @param branch: OpenModelica branch to test. Will checkout the branch and build omc from it.
* @param name: Unique name of the library test. Passed to test.py via flag `--branch`.
* Also used for stashing omc and uploading results to https://test.openmodelica.org.
* @param extraFlags: Additional compiler flags passed to test.py via flag `--extraflags`.
* @param omsHash: OMSimulator SHA.
* @param omcompiler: Checkout old OMCompiler submodule. Should be `false` nowadays.
* @param extrasimflags: Additional simulation flags passed to test.py via flag `--extrasimflags`.
* @param testFlags: Additional flags passed to test.py verbatim, e.g. `--nobuildmodel`.
* @param removePackageOrder: Passed to `installLibraries`.
* @param conversionScript: Passed to `installLibraries`.
* @param jobs: The number of tests/jobs to launch in parallel.
* By default this is set to `0` which means launch as many tests as there are available
* physical cpus on the machine'.
* @param libs_config_file: The config file to be used for testing.
* This file specifies which libraries to test and what options to use for them.
* @param cmakeFlags: Target-specific cmake flags, e.g. `-DOM_OMC_ENABLE_RUST=ON`, added to the
* shared release flags. Only for the branches built with cmake, see
* buildsWithCMake().
* @param dockerfile: Directory with a Dockerfile, relative to the testing repository. Defaults to
* `.CI/testing`, the image every job runs in: the omc build, the OMSimulator
* build and test.py happen inside it, and only the steps using the node's own
* omc stay outside. Passing `''` runs the job on the node itself.
*/
/* The FMI simulators a job runs, from the parameters that used to start one job each.
* Both ticked is one job that builds every FMU once and simulates it with both,
* filling <branch>-fmi and <branch>-fmi-fmpy exactly as the two jobs did. */
def fmiSimulators(boolean omsimulator, boolean fmpy) {
def simulators = []
if (omsimulator) simulators << 'OMSimulator'
if (fmpy) simulators << 'fmpy'
return simulators
}
/* The cmake flags of an omc that can translate to wasm-jit. The wasm-jit and
* wasm-fmu jobs share the build cached in ~/saved_omc/wasm-jit, so they have to
* ask for the same one. */
def wasmCmakeFlags() {
return '-DOM_OMC_ENABLE_RUST=ON -DRUST_OMC_CI=ON -DRUST_OMC_THREADS=4 -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache'
}
/* What makes the wasm-web job's omc simulate the way the web omc does. */
def wasmWebEnv() {
return ['OMC_WASM_INWASM_DRIVER=1',
'OMC_WASM_HOST_LIN_SOLVE=0',
'OMC_WASM_HOST_LIBM=0',
'OMC_WASM_NATIVE_EXTERNALS=0']
}
def runRegressiontest(branch, name, extraFlags, omsHash, omcompiler, extrasimflags, testFlags, boolean removePackageOrder, boolean conversionScript, int jobs=0, libs_config_file = 'configs/conf.json', cmakeFlags = '', dockerfile = '.CI/testing', fmiSimulators = null, solvers = null) {
sh '''
find /tmp -name "*openmodelica.hudson*" -exec rm {} ";" || true
if test -z "$WORKSPACE"; then
echo "Odd workspace"
exit 1
fi
'''
// Checked out before the omc build rather than just before test.py: it provides the Dockerfile.
sh """
if test ! -d OpenModelicaLibraryTesting; then
git clone --recursive https://openmodelica.org/git-readonly/OpenModelicaLibraryTesting.git OpenModelicaLibraryTesting
fi
cd OpenModelicaLibraryTesting
git fetch
git reset --hard origin/master
"""
def image = null
if (dockerfile) {
// Build context is the Dockerfile directory; the workspace has a directory per tested model.
sh "cp OpenModelicaLibraryTesting/requirements.txt OpenModelicaLibraryTesting/${dockerfile}/"
// Tagged after the Dockerfile rather than after the job, so that jobs sharing an image share
// its tag as well and a node keeps one of them instead of one per job it has ever run.
image = docker.build("openmodelica-library-testing:${dockerfile.tokenize('/').last()}",
"--pull OpenModelicaLibraryTesting/${dockerfile}")
}
// --init reaps the omc processes test.py orphans. ssh refuses to run for a uid it cannot look
// up, so the node's passwd entry is needed to publish the results. The home holds the cached omc
// build, the libraries (HOME during the test) and the ssh key; test.py writes a hash next to
// every reference file. rust-cargo-registry is the volume the OpenModelica job uses. The MQTT
// example of Modelica_DeviceDrivers gets the broker test.py starts instead of the public one.
def dockerArgs = "--init" +
(image ? " ${memoryLimitArgs()}" : '') +
" --add-host test.mosquitto.org:127.0.0.1" +
" -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro" +
" -v ${env.HOME}:${env.HOME}" +
" -v /mnt/ReferenceFiles:/mnt/ReferenceFiles" +
" --mount type=volume,source=rust-cargo-registry,target=/opt/rust/cargo/registry"
// Only in a container is the cgroup this job's own; a breach kills the greediest omc, which need
// not be the model that caused it. memory.stat says what kind of memory it was (anon, file,
// shmem, slab), and the process list what test.py left behind.
def cgroupReport = image ? """
cat /sys/fs/cgroup/memory.max || true
trap 'cat /sys/fs/cgroup/memory.peak /sys/fs/cgroup/memory.events /sys/fs/cgroup/memory.stat; ps -eo pid,ppid,sid,etimes,rss,args --sort=-rss | head -40' EXIT
""" : ''
// Jenkins exports the node's environment into the container, hiding the image's.
def dockerEnv = ['PATH+VENV=/opt/libtest-venv/bin',
'PATH+CARGO=/opt/rust/cargo/bin',
'CARGO_HOME=/opt/rust/cargo',
'RUSTUP_HOME=/opt/rust/rustup']
// Runs a script in the image if the target uses one, on the node otherwise.
def runSh = { args ->
if (image) {
image.inside(dockerArgs) { withEnv(dockerEnv) { sh(args) } }