diff --git a/.CI/Jenkinsfile b/.CI/Jenkinsfile index 207e265..56be04f 100644 --- a/.CI/Jenkinsfile +++ b/.CI/Jenkinsfile @@ -36,6 +36,7 @@ pipeline { booleanParam(name: 'ida', defaultValue: false, description: 'master branch, simulated with -s ida, filling ida (ryzen-5950x-1). Ticking it together with master, cvode or gbode runs one job that builds every model once and simulates it with each of them. 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 exported once as a wasm artifact and simulated three ways, filling wasm-jit (the runtime inside the artifact), wasm-jit-me (FMI 3.0 Model Exchange) and wasm-jit-cs (FMI 3.0 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-5950x-1). The C code generator emits what it always did; 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.), (ryzen-5950x-1). 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//merge - the pull request as it would land - the results fill a pr- table, and the report compares them against the newest run of pull_request_baseline. Left empty, nothing of this runs.') @@ -341,6 +342,26 @@ pipeline { '-DOM_OMC_ENABLE_RUST=ON -DRUST_OMC_CI=ON -DRUST_OMC_THREADS=4 -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache') } } + stage('C+Rust') { + agent { + node { + label 'ryzen-5950x-1' + customWorkspace 'ws/OpenModelicaLibraryTestingWork' + } + } + options { skipDefaultCheckout() } + when { + beforeAgent true + expression { params.c_plus_rust } + } + steps { + // A cmake build rather than the autotools one every other C target uses: + // libSimulationRuntimeRust is a cmake target (SimulationRuntime/rust), and + // without it the generated makefile has nothing to link. + runRegressiontest('master', 'c-plus-rust', 'setCommandLineOptions("--simCodeTarget=C+Rust")', '', false, '', '', false, false, 0, 'configs/conf.json', + '-DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache') + } + } stage('generateSymbolicJacobian') { agent { node { @@ -435,7 +456,7 @@ pipeline { } 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.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.cvode || params.gbode || params.ida || params.wasm_jit} + 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.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.cvode || params.gbode || params.ida || params.wasm_jit || 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' @@ -445,7 +466,8 @@ pipeline { 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 wasm-jit-me wasm-jit-cs' - GITBRANCHES_SPECIAL = 'master wasm-jit newInst-newBackend' + GITBRANCHES_C_PLUS_RUST = 'c-plus-rust' + GITBRANCHES_SPECIAL = 'master wasm-jit c-plus-rust newInst-newBackend' PYTHONIOENCODING = 'utf-8' IDA_EMAIL = credentials('IDA email') // A secret file holding one pgpass line; libpq reads the password from @@ -465,8 +487,8 @@ pipeline { ''' 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} conversion heavy_tests 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} conversion heavy_tests generateSymbolicJacobian gbode cvode ida" + 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 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 generateSymbolicJacobian gbode cvode ida" 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' @@ -533,6 +555,11 @@ pipeline { sh "./report.py --branches='${env.GITBRANCHES_WASM_JIT} master' configs/conf.json" sh "mv overview.html overview-wasm-jit.html" + // Against master, which the job shares a machine and a compiler with: the + // simulation runtime is the only thing that differs. + sh "./report.py --branches='${env.GITBRANCHES_C_PLUS_RUST} master' configs/conf.json" + sh "mv overview.html overview-c-plus-rust.html" + sh "./report.py --branches='${env.GITBRANCHES_CPP}' configs/conf.json" sh "mv overview.html overview-c++.html" diff --git a/README.md b/README.md index a027a0f..d7b0ff2 100644 --- a/README.md +++ b/README.md @@ -355,6 +355,23 @@ entry there (`simflags` is what is appended to the model's simulation flags, `--wasmjitrunner`, `--fmisimulator` and `--solver` each fan one build out into several result branches, so a job uses one of them, not several. +### The Rust simulation runtime under the C code generator + +`--simCodeTarget=C+Rust` emits the sources `--simCodeTarget=C` emits and links +`libSimulationRuntimeRust` instead of `libSimulationRuntimeC`, so a run against +master differs in the simulation runtime and nothing else. + +```bash +./test.py --branch=c-plus-rust --extraflags='--simCodeTarget=C+Rust' configs/myConf.json +./report.py --branches="c-plus-rust master" +# the overview.html it writes is published as overview-c-plus-rust.html +``` + +The runtime is a cmake target of the OpenModelica build +(`SimulationRuntime/rust`, `OM_ENABLE_RUST_SIM_RUNTIME`, on by default where +cargo is installed) and has no autotools equivalent, so omc has to be built with +cmake or the generated makefile finds nothing to link. + ### One build, several solvers Which solver integrates a model is a simulation flag, so testing another one diff --git a/shared.py b/shared.py index 967c785..9d74696 100644 --- a/shared.py +++ b/shared.py @@ -75,8 +75,8 @@ def fixData(data,abortSimulationFlag,alarmFlag,overrideDefaults,defaultCustomCom data["libraryVersion"] = data.get("libraryVersion") or "default" data["libraryVersionLatestInPackageManager"] = data.get("libraryVersionLatestInPackageManager") or False data["libraryVersionExactMatch"] = data.get("libraryVersionExactMatch") or False - data["alarmFlag"] = data.get("alarmFlag") or (alarmFlag if data["simCodeTarget"] in ("C","wasm-jit") else "") - data["abortSlowSimulation"] = data.get("abortSlowSimulation") or (abortSimulationFlag if data["simCodeTarget"]=="C" else "") + data["alarmFlag"] = data.get("alarmFlag") or (alarmFlag if data["simCodeTarget"] in ("C","C+Rust","wasm-jit") else "") + data["abortSlowSimulation"] = data.get("abortSlowSimulation") or (abortSimulationFlag if data["simCodeTarget"] in ("C","C+Rust") else "") data["simFlags"] = simulationFlags(data, data["ulimitExe"]) if "changeHash" in data: # Force rebuilding the library due to change in the testing script data["changeHash"] = data["changeHash"] diff --git a/testmodel.py b/testmodel.py index 27cec7b..83cad9a 100755 --- a/testmodel.py +++ b/testmodel.py @@ -263,7 +263,7 @@ def target(res): with open(errFile, 'a+') as fp: fp.write("Running: %s\n" % " ".join(sys.argv)) -if conf["simCodeTarget"] not in ["Cpp","C","wasm-jit"]: +if conf["simCodeTarget"] not in ["Cpp","C","C+Rust","wasm-jit"]: with open(errFile, 'a+') as fp: fp.write("Unknown simCodeTarget in %s" % conf["simCodeTarget"]) writeResultAndExit(1) @@ -448,7 +448,7 @@ def wasmJitAcceptsFlag(flagVal): except: # omc answers nothing when the call fails, and nothing else reads the buffer. raise Exception("%s failed:\n%s" % (cmd, omc.sendExpression("getErrorString()", parsed = False))) -if conf["simCodeTarget"] in ("C","wasm-jit") and sendExpressionOldOrNew('classAnnotationExists(%s, __OpenModelica_simulationFlags)' % conf["modelName"]): +if conf["simCodeTarget"] in ("C","C+Rust","wasm-jit") and sendExpressionOldOrNew('classAnnotationExists(%s, __OpenModelica_simulationFlags)' % conf["modelName"]): for flag in sendExpressionOldOrNew('getAnnotationNamedModifiers(%s,"__OpenModelica_simulationFlags")' % conf["modelName"]): if flag=="The searched annotation name not found": # Old, stupid API @@ -718,7 +718,7 @@ def simulateExecutable(name, solverFlags, resFile, simFile): # A run sharing the directory with other solvers needs a result file of its own. resultArgument = "-r=%s" % resFile if runnerSuffix(name) and outputFormat != "empty" else "" cmd = " ".join(x for x in (exe, annotationSimFlags, conf["simFlags"], emit_protected, - "-lv LOG_STATS" if conf["simCodeTarget"]=="C" else "", + "-lv LOG_STATS" if conf["simCodeTarget"] in ("C","C+Rust") else "", resultArgument, solverFlags) if x.strip()) with open(simFile,"w") as fp: fp.write("Environment - simulationEnvironment:\n") diff --git a/testresults.md b/testresults.md index 51467f3..64fab03 100644 --- a/testresults.md +++ b/testresults.md @@ -26,6 +26,11 @@ runtime inside it, `wasm-jit-me` its FMI 3.0 Model Exchange interface and `wasm-jit-cs` its FMI 3.0 Co-Simulation one — so the three differ only in how the same compiled model is driven + - [C+Rust](https://libraries.openmodelica.org/branches/overview-c-plus-rust.html): + simulation with the Rust runtime under the C code generator (compiler flag + `--simCodeTarget=C+Rust`). The generated sources are the ones the C target + emits; the executable links `libSimulationRuntimeRust` instead of + `libSimulationRuntimeC`, so the difference against master is the runtime alone - **Regression reports and history plots**: Regression reports are periodically generated, using the latest development