Skip to content

Commit

Permalink
CMake FMU: Copy runtime library dependencies into FMU (#9441)
Browse files Browse the repository at this point in the history
* CMake FMUs with library runtime dependencies
  - Needs CMake >= v3.21
  - Add runtime dependencies to install
* New flag --fmuRuntimeDepends
  - Include none, modelica or all runtime dependencies.
  - Defaults to modelica.
* Add ORIGIN to RPATH
* Test if removal of .fmutmp directory was succesfull
* Removed dead code SimCodeMain
* Downgrading minimum CMake version to 3.5
* Added CMake FMU export test case
* Disable CMake FMUs for cross-compilation export
  • Loading branch information
AnHeuermann committed Oct 5, 2022
1 parent f2a0f69 commit 42e1d1b
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 46 deletions.
4 changes: 3 additions & 1 deletion OMCompiler/Compiler/Script/CevalScriptBackend.mo
Original file line number Diff line number Diff line change
Expand Up @@ -4134,7 +4134,9 @@ algorithm
end if;

if not Flags.isSet(Flags.GEN_DEBUG_SYMBOLS) then
System.removeDirectory(fmutmp);
if not System.removeDirectory(fmutmp) then
Error.addInternalError("Failed to remove directory: " + fmutmp, sourceInfo());
end if;
end if;
end callBuildModelFMU;

Expand Down
48 changes: 14 additions & 34 deletions OMCompiler/Compiler/SimCode/SimCodeMain.mo
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,20 @@ algorithm
destination = fmu_tmp_sources_dir + "CMakeLists.txt");
cmakelistsStr := System.readFile(fmu_tmp_sources_dir + "CMakeLists.txt");
cmakelistsStr := System.stringReplace(cmakelistsStr, "@FMU_NAME_IN@", simCode.fileNamePrefix);
_ := match (Flags.getConfigString(Flags.FMU_RUNTIME_DEPENDS))
case("none") algorithm
cmakelistsStr := System.stringReplace(cmakelistsStr, "@RUNTIME_DEPENDENCIES_LEVEL@", "none");
then();
case("modelica") algorithm
cmakelistsStr := System.stringReplace(cmakelistsStr, "@RUNTIME_DEPENDENCIES_LEVEL@", "modelica");
then();
case("all") algorithm
cmakelistsStr := System.stringReplace(cmakelistsStr, "@RUNTIME_DEPENDENCIES_LEVEL@", "all");
then();
else algorithm
Error.addCompilerError("Unsupported value " + Flags.getConfigString(Flags.FMU_RUNTIME_DEPENDS) + "for compiler flag 'fmuRuntimeDepends'.");
then();
end match;
if isSome(simCode.fmiSimulationFlags) then
cmakelistsStr := System.stringReplace(cmakelistsStr, "@WITH_SUNDIALS@", ";WITH_SUNDIALS");
else
Expand Down Expand Up @@ -952,47 +966,13 @@ algorithm
SerializeInitXML.simulationInitFileReturnBool(simCode=simCode, guid=guid);
SerializeModelInfo.serialize(simCode, Flags.isSet(Flags.INFO_XML_OPERATIONS));

//runTplWriteFile(func = function CodegenFMU.fmuModelDescriptionFile(in_a_simCode=simCode, in_a_guid=guid, in_a_FMUVersion=FMUVersion, in_a_FMUType=FMUType, in_a_sourceFiles={}), file=simCode.fullPathPrefix+"/"+"modelDescription.xml");
runTpl(func = function CodegenOMSI_common.generateFMUModelDescriptionFile(a_simCode=simCode, a_guid=guid, a_FMUVersion=FMUVersion, a_FMUType=FMUType, a_sourceFiles={}, a_fileName=simCode.fullPathPrefix+"/"+"modelDescription.xml"));
runTplWriteFile(func = function CodegenOMSIC.createMakefile(a_simCode=simCode, a_target=Config.simulationCodeTarget(), a_makeflieName=fileprefix+"_FMU.makefile"), file=simCode.fullPathPrefix+"/"+fileprefix+"_FMU.makefile");

runTplWriteFile(func = function CodegenOMSIC.generateOMSIC(a_simCode=simCode), file=simCode.fullPathPrefix+"/"+fileprefix+"_omsic.c");

runTpl(func = function CodegenOMSI_common.generateEquationsCode(a_simCode=simCode, a_FileNamePrefix=fileprefix));
then ();
/*Temporarily disabled
case (_,"omsicpp")
algorithm
guid := System.getUUIDStr();
fileprefix := simCode.fileNamePrefix;
// create tmp directory for generated files, but first remove the old one!
if System.directoryExists(simCode.fullPathPrefix) then
if not System.removeDirectory(simCode.fullPathPrefix) then
Error.addInternalError("Failed to remove directory: " + simCode.fullPathPrefix, sourceInfo());
fail();
end if;
end if;
if not System.createDirectory(simCode.fullPathPrefix) then
Error.addInternalError("Failed to create tmp folder "+simCode.fullPathPrefix, sourceInfo());
System.fflush();
fail();
end if;
SerializeInitXML.simulationInitFileReturnBool(simCode=simCode, guid=guid);
SerializeModelInfo.serialize(simCode, Flags.isSet(Flags.INFO_XML_OPERATIONS));
//runTplWriteFile(func = function CodegenFMU.fmuModelDescriptionFile(in_a_simCode=simCode, in_a_guid=guid, in_a_FMUVersion=FMUVersion, in_a_FMUType=FMUType, in_a_sourceFiles={}), file=simCode.fullPathPrefix+"/"+"modelDescription.xml");
runTpl(func = function CodegenOMSI_common.generateFMUModelDescriptionFile(a_simCode=simCode, a_guid=guid, a_FMUVersion=FMUVersion, a_FMUType=FMUType, a_sourceFiles={}, a_fileName=simCode.fullPathPrefix+"/"+"modelDescription.xml"));
runTplWriteFile(func = function CodegenOMSIC.createMakefile(a_simCode=simCode, a_target=Config.simulationCodeTarget(), a_makeflieName=fileprefix+"_FMU.makefile"), file=simCode.fullPathPrefix+"/"+fileprefix+"_FMU.makefile");
runTplWriteFile(func = function CodegenOMSIC.generateOMSIC(a_simCode=simCode), file=simCode.fullPathPrefix+"/"+fileprefix+"_omsic.c");
runTpl(func = function CodegenOMSI_common.generateEquationsCode(a_simCode=simCode, a_FileNamePrefix=fileprefix));
runTpl(func = function CodegenOMSICpp.translateModel(a_simCode=simCode, a_FMUVersion=FMUVersion, a_FMUType=FMUType));
then ();
*/
case (_,"Cpp")
equation
if(Flags.isSet(Flags.HPCOM)) then
Expand Down
1 change: 1 addition & 0 deletions OMCompiler/Compiler/SimCode/SimCodeUtil.mo
Original file line number Diff line number Diff line change
Expand Up @@ -15565,6 +15565,7 @@ protected
algorithm
(locations, libraries) := getDirectoriesForDLLsFromLinkLibs(libs);
locations := List.map(locations, addQuotationMarks);
// Use target_link_directories when CMake 3.13 is available and skip the find_library part
for lib in libraries loop
cmakecode := cmakecode + "find_library(" + lib + "\n" +
" NAMES " + lib + "\n" +
Expand Down
18 changes: 16 additions & 2 deletions OMCompiler/Compiler/Util/Flags.mo
Original file line number Diff line number Diff line change
Expand Up @@ -1362,8 +1362,8 @@ constant ConfigFlag FMU_CMAKE_BUILD = CONFIG_FLAG(142, "fmuCMakeBuild",
NONE(), EXTERNAL(), STRING_FLAG("default"),
SOME(STRING_DESC_OPTION({
("default", Gettext.notrans("Let omc decide if CMake should be used.")),
("true", Gettext.notrans("Use CMake to compile FMU binaries.")),
("false", Gettext.notrans("Use default GNU Autoconf toolchain to compile FMU binaries."))
("true", Gettext.notrans("Use CMake to compile FMU binaries.")),
("false", Gettext.notrans("Use default GNU Autoconf toolchain to compile FMU binaries."))
})),
Gettext.gettext("Defines if FMUs will be configured and build with CMake."));

Expand Down Expand Up @@ -1430,6 +1430,20 @@ constant ConfigFlag OBFUSCATE = CONFIG_FLAG(152, "obfuscate",
})),
Gettext.gettext("Obfuscates identifiers in the simulation model"));

constant ConfigFlag FMU_RUNTIME_DEPENDS = CONFIG_FLAG(153, "fmuRuntimeDepends",
NONE(), EXTERNAL(), STRING_FLAG("modelica"),
SOME(STRING_DESC_OPTION({
("none", Gettext.notrans("No runtime library dependencies are copied into the FMU.")),
("modelica", Gettext.notrans("All modelica runtime library dependencies are copied into the FMU." +
"System librarys located in '/lib*', '/usr/lib*' and '/usr/local/lib*' are excluded." +
"Needs --fmuCMakeBuild=true and CMake version >= 3.21.")),
("all", Gettext.notrans("All runtime library dependencies are copied into the FMU." +
"System librarys are copied as well." +
"Needs --fmuCMakeBuild=true and CMake version >= 3.21."))
})),
Gettext.gettext("Defines if runtime library dependencies are included in the FMU. Only used when compiler flag fmuCMakeBuild=true."));


function getFlags
"Loads the flags with getGlobalRoot. Assumes flags have been loaded."
input Boolean initialize = true;
Expand Down
3 changes: 2 additions & 1 deletion OMCompiler/Compiler/Util/FlagsUtil.mo
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,8 @@ constant list<Flags.ConfigFlag> allConfigFlags = {
Flags.TEARING_ALWAYS_DERIVATIVES,
Flags.DUMP_FLAT_MODEL,
Flags.SIMULATION,
Flags.OBFUSCATE
Flags.OBFUSCATE,
Flags.FMU_RUNTIME_DEPENDS
};

public function new
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@

cmake_minimum_required(VERSION 3.7)
cmake_minimum_required(VERSION 3.5)

set(FMU_NAME @FMU_NAME_IN@)

project(${FMU_NAME})

# Test if RUNTIME_DEPENDENCIES is needed and available
set(RUNTIME_DEPENDENCIES_LEVEL @RUNTIME_DEPENDENCIES_LEVEL@)
if(${CMAKE_VERSION} VERSION_LESS "3.21" AND NOT ${RUNTIME_DEPENDENCIES_LEVEL} STREQUAL "none")
message(FATAL_ERROR
"--fmuRuntimeDepends=${RUNTIME_DEPENDENCIES_LEVEL} requiers CMake version 3.21 or higher.\n"
"You are running version ${CMAKE_VERSION}.\n"
"Use OpenModelica compiler flag '--fmuRuntimeDepends=none' to disable including runtime dependencies into FMU.")
endif()

if(NOT FMI_INTERFACE_HEADER_FILES_DIRECTORY)
message(FATAL_ERROR "No FMI export headers provided. Set -DFMI_INTERFACE_HEADER_FILES_DIRECTORY=/path/to/fmi/headers")
endif()
Expand Down Expand Up @@ -44,12 +53,22 @@ set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/../binaries/${FMU_TARGET_PL
set(CMAKE_INSTALL_LIBDIR ${CMAKE_INSTALL_PREFIX})
set(CMAKE_INSTALL_BINDIR ${CMAKE_INSTALL_PREFIX})
set(CMAKE_SHARED_LIBRARY_PREFIX "")

# Set RPATH
if(APPLE)
set(CMAKE_INSTALL_RPATH "@loader_path")
else()
set(CMAKE_INSTALL_RPATH "$ORIGIN")
endif()

add_library(${FMU_NAME} SHARED
${FMU_RUNTIME_SOURCES}
${FMU_GENERATED_MODEL_SOURCES})


target_link_libraries(${FMU_NAME} PRIVATE Threads::Threads)
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.13")
target_link_options(${FMU_NAME} PRIVATE "LINKER:SHELL:--no-undefined")
endif()
target_link_libraries(${FMU_NAME} PRIVATE m Threads::Threads)
@FMU_ADDITIONAL_LIBS@

target_include_directories(${FMU_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
Expand All @@ -58,10 +77,26 @@ target_include_directories(${FMU_NAME} PRIVATE ${FMI_INTERFACE_HEADER_FILES_DIRE

target_compile_definitions(${FMU_NAME} PRIVATE OMC_MINIMAL_RUNTIME=1;OMC_FMI_RUNTIME=1;CMINPACK_NO_DLL)

install(TARGETS ${FMU_NAME}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR})
if(RUNTIME_DEPENDENCIES_LEVEL STREQUAL "all")
install(TARGETS ${FMU_NAME}
RUNTIME_DEPENDENCIES
PERMISSIONS WORLD_WRITE
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR})
elseif(RUNTIME_DEPENDENCIES_LEVEL STREQUAL "modelica")
install(TARGETS ${FMU_NAME}
RUNTIME_DEPENDENCIES POST_EXCLUDE_REGEXES "^\\/lib.*" "^\\/usr\\/lib.*" "^\\/usr\\/local\\/lib.*"
PERMISSIONS WORLD_WRITE
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR})
else()
install(TARGETS ${FMU_NAME}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()

add_custom_target(create_zip COMMAND
${CMAKE_COMMAND} -E tar "cfv" "../${CMAKE_PROJECT_NAME}.fmu" --format=zip
Expand Down
1 change: 1 addition & 0 deletions testsuite/omsimulator/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
TEST = ../rtest -v

TESTFILES = \
cmakeFMU.mos \
DualMassOscillator_cs.mos \
DualMassOscillator_me.mos \
DualMassOscillator.mos \
Expand Down
30 changes: 30 additions & 0 deletions testsuite/omsimulator/cmakeFMU.mos
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// name: cmakeFMU.mos
// status: correct
// keywords: fmu export cmake dynamic
// teardown_command: rm -rf DualMassOscillator.System1.fmu DualMassOscillator.System1_FMU* DualMassOscillator_System1_systemCall.log

loadFile("DualMassOscillator.mo"); getErrorString();
setCommandLineOptions("--fmuCMakeBuild=true --fmuRuntimeDepends=none"); getErrorString();
buildModelFMU(DualMassOscillator.System1, platforms={"dynamic"}); getErrorString();

system(getInstallationDirectoryPath() + "/bin/OMSimulator DualMassOscillator.System1.fmu", "DualMassOscillator_System1_systemCall.log");
readFile("DualMassOscillator_System1_systemCall.log");

// Result:
// true
// "Notification: Automatically loaded package Modelica 3.2.2 due to uses annotation.
// Notification: Automatically loaded package Complex 3.2.2 due to uses annotation.
// Notification: Automatically loaded package ModelicaServices 3.2.2 due to uses annotation.
// "
// true
// ""
// "DualMassOscillator.System1.fmu"
// ""
// 0
// "info: maximum step size for 'model.root': 0.001000
// info: Result file: model_res.mat (bufferSize=10)
// info: Final Statistics for 'model.root':
// NumSteps = 1001 NumRhsEvals = 1002 NumLinSolvSetups = 51
// NumNonlinSolvIters = 1001 NumNonlinSolvConvFails = 0 NumErrTestFails = 0
// "
// endResult
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ system("rm -rf CMakeCrossCompile && mkdir CMakeCrossCompile", "CMakeCrossCompile
cd("CMakeCrossCompile");

// Set CMake FMU without any filters
if not setCommandLineOptions("--fmuCMakeBuild=true --fmiFilter=none") then
if not setCommandLineOptions("--fmuCMakeBuild=true --fmuRuntimeDepends=none --fmiFilter=none") then
print(getErrorString());
exit(1);
end if;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ for c in {"FmuExportCrossCompile","RoomHeating_OM.RH","WaterTank.Control","Water
res := simulate("+c+", method=\"euler\", numberOfIntervals=2000, outputFormat=\"csv\", fileNamePrefix=c2); getErrorString();
print(getErrorString()+\"
\");
setCommandLineOptions(\"--fmuCMakeBuild=false\"); getErrorString();
b := buildModelFMU("+c+", fileNamePrefix=c2, version=\"2.0\", fmuType=\"me_cs\", platforms=platforms);
print(getErrorString()+\"
\");
Expand Down

0 comments on commit 42e1d1b

Please sign in to comment.