CellMLToSharedLibraryConverter.cpp
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "CellMLToSharedLibraryConverter.hpp"
00037
00038 #include <sstream>
00039 #include <sys/stat.h>
00040 #include <ctime>
00041 #include <cstring>
00042 #include <cerrno>
00043
00044 #include <boost/foreach.hpp>
00045
00046 #include "ChasteSyscalls.hpp"
00047 #include "Exception.hpp"
00048 #include "Warnings.hpp"
00049 #include "ChasteBuildRoot.hpp"
00050 #include "PetscTools.hpp"
00051 #include "DynamicModelLoaderRegistry.hpp"
00052 #include "GetCurrentWorkingDirectory.hpp"
00053
00054 #define IGNORE_EXCEPTIONS(code) \
00055 try { \
00056 code; \
00057 } catch (...) {}
00058
00059
00060
00062 #ifdef __APPLE__
00063
00064 const std::string CellMLToSharedLibraryConverter::msSoSuffix = "dylib";
00065 #else
00066
00067 const std::string CellMLToSharedLibraryConverter::msSoSuffix = "so";
00068 #endif
00069
00070 CellMLToSharedLibraryConverter::CellMLToSharedLibraryConverter(bool preserveGeneratedSources,
00071 std::string component)
00072 : mPreserveGeneratedSources(preserveGeneratedSources),
00073 mComponentName(component)
00074 {
00075 }
00076
00077 DynamicCellModelLoaderPtr CellMLToSharedLibraryConverter::Convert(const FileFinder& rFilePath,
00078 bool isCollective)
00079 {
00080 DynamicCellModelLoaderPtr p_loader;
00081 std::string absolute_path = rFilePath.GetAbsolutePath();
00082
00083 size_t dot_position = absolute_path.find_last_of(".");
00084 if (dot_position == std::string::npos)
00085 {
00086 EXCEPTION("File does not have an extension: " + absolute_path);
00087 }
00088 std::string extension = absolute_path.substr(dot_position+1);
00089
00090
00091 FileFinder file_path_copy(rFilePath);
00092 #ifdef __APPLE__
00093 if (extension == "so")
00094 {
00095 WARN_ONCE_ONLY("CellMLToSharedLibraryConverter asked to load a \".so\" file. On this architecture it should be \".dylib\"");
00096 extension = "dylib";
00097 absolute_path.replace(dot_position+1, 5, extension);
00098 file_path_copy.SetPath(absolute_path, RelativeTo::Absolute);
00099 }
00100 #endif
00101
00102 if (!file_path_copy.Exists())
00103 {
00104 EXCEPTION("Dynamically loadable cell model '" + absolute_path + "' does not exist.");
00105 }
00106 if (extension == "cellml")
00107 {
00108
00109 size_t slash_position = absolute_path.find_last_of("/\\");
00110 assert(slash_position != std::string::npos);
00111 std::string folder = absolute_path.substr(0, slash_position+1);
00112 std::string leaf = absolute_path.substr(slash_position+1, dot_position-slash_position);
00113 std::string so_path = folder + "lib" + leaf + msSoSuffix;
00114
00115 FileFinder so_file(so_path, RelativeTo::Absolute);
00116 if (!so_file.Exists() || rFilePath.IsNewerThan(so_file))
00117 {
00118 if (!isCollective)
00119 {
00120 EXCEPTION("Unable to convert .cellml to .so unless called collectively, due to possible race conditions.");
00121 }
00122 ConvertCellmlToSo(absolute_path, folder);
00123 }
00124
00125 p_loader = DynamicModelLoaderRegistry::Instance()->GetLoader(so_file);
00126 }
00127 else if (extension == msSoSuffix)
00128 {
00129
00130
00131 p_loader = DynamicModelLoaderRegistry::Instance()->GetLoader(file_path_copy);
00132 }
00133 else
00134 {
00135 EXCEPTION("Unsupported extension '." + extension + "' of file '" + absolute_path + "'; must be .so, .dylib or .cellml");
00136 }
00137
00138 return p_loader;
00139 }
00140
00141 void CellMLToSharedLibraryConverter::ConvertCellmlToSo(const std::string& rCellmlFullPath,
00142 const std::string& rCellmlFolder)
00143 {
00144 FileFinder tmp_folder;
00145 FileFinder build_folder;
00146
00147 std::string old_cwd = GetCurrentWorkingDirectory();
00148
00149 FileFinder chaste_root("", RelativeTo::ChasteSourceRoot);
00150
00151 if (!chaste_root.IsDir())
00152 {
00153 EXCEPTION("No Chaste source tree found at '" << chaste_root.GetAbsolutePath()
00154 << "' - you need the source to use CellML models directly in Chaste.");
00155 }
00156 FileFinder component_dir(mComponentName, RelativeTo::ChasteSourceRoot);
00157 if (!component_dir.IsDir())
00158 {
00159 EXCEPTION("Unable to convert CellML model: required Chaste component '" << mComponentName
00160 << "' does not exist in '" << ChasteBuildRootDir() << "'.");
00161 }
00162
00163 try
00164 {
00165
00166 if (PetscTools::AmMaster())
00167 {
00168
00169 std::stringstream folder_name;
00170 folder_name << "dynamic/tmp_" << getpid() << "_" << time(NULL);
00171
00172 tmp_folder.SetPath(component_dir.GetAbsolutePath() + "/" + folder_name.str(), RelativeTo::Absolute);
00173 build_folder.SetPath(component_dir.GetAbsolutePath() + "/build/" + ChasteBuildDirName() + "/" + folder_name.str(), RelativeTo::Absolute);
00174 int ret = mkdir((tmp_folder.GetAbsolutePath()).c_str(), 0700);
00175 if (ret != 0)
00176 {
00177 EXCEPTION("Failed to create temporary folder '" << tmp_folder.GetAbsolutePath() << "' for CellML conversion: "
00178 << strerror(errno));
00179 }
00180
00181
00182 FileFinder cellml_file(rCellmlFullPath, RelativeTo::Absolute);
00183 FileFinder cellml_folder = cellml_file.GetParent();
00184 std::string cellml_leaf_name = cellml_file.GetLeafNameNoExtension();
00185 std::vector<FileFinder> cellml_files = cellml_folder.FindMatches(cellml_leaf_name + "*");
00186
00187 BOOST_FOREACH(const FileFinder& r_cellml_file, cellml_files)
00188 {
00189 r_cellml_file.CopyTo(tmp_folder);
00190 }
00191
00192
00193 EXPECT0(chdir, ChasteBuildRootDir());
00194
00195 EXPECT0(system, "scons --warn=no-all dyn_libs_only=1 build=" + ChasteBuildType() + " " + tmp_folder.GetAbsolutePath());
00196
00197 FileFinder so_file(tmp_folder.GetAbsolutePath() + "/lib" + cellml_leaf_name + "." + msSoSuffix, RelativeTo::Absolute);
00198 EXCEPT_IF_NOT(so_file.Exists());
00199
00200 EXPECT0(chdir, old_cwd);
00201
00202
00203 FileFinder destination_folder(rCellmlFolder, RelativeTo::Absolute);
00204 so_file.CopyTo(destination_folder);
00205
00206 if (mPreserveGeneratedSources)
00207 {
00208
00209 std::vector<FileFinder> generated_files = build_folder.FindMatches("*.?pp");
00210 BOOST_FOREACH(const FileFinder& r_generated_file, generated_files)
00211 {
00212 r_generated_file.CopyTo(destination_folder);
00213 }
00214 }
00215
00216 build_folder.DangerousRemove();
00217 tmp_folder.DangerousRemove();
00218 }
00219 }
00220 catch (Exception& e)
00221 {
00222 PetscTools::ReplicateException(true);
00223 if (tmp_folder.IsPathSet() && tmp_folder.Exists())
00224 {
00225 if (mPreserveGeneratedSources)
00226 {
00227
00228 IGNORE_EXCEPTIONS(build_folder.CopyTo(FileFinder(rCellmlFolder + "/build/", RelativeTo::Absolute)));
00229 IGNORE_EXCEPTIONS(tmp_folder.CopyTo(FileFinder(rCellmlFolder + "/tmp/", RelativeTo::Absolute)));
00230 }
00231
00232 IGNORE_EXCEPTIONS(build_folder.DangerousRemove());
00233 IGNORE_EXCEPTIONS(tmp_folder.DangerousRemove());
00234 }
00235 IGNORE_RET(chdir, old_cwd);
00236 EXCEPTION("Conversion of CellML to Chaste shared object failed. Error was: " + e.GetMessage());
00237 }
00238
00239
00240 PetscTools::ReplicateException(false);
00241 }
00242
00243 void CellMLToSharedLibraryConverter::CreateOptionsFile(const OutputFileHandler& rHandler,
00244 const std::string& rModelName,
00245 const std::vector<std::string>& rArgs,
00246 const std::string& rExtraXml)
00247 {
00248 if (PetscTools::AmMaster())
00249 {
00250 out_stream p_optfile = rHandler.OpenOutputFile(rModelName + "-conf.xml");
00251 (*p_optfile) << "<?xml version='1.0'?>" << std::endl
00252 << "<pycml_config>" << std::endl;
00253 if (!rArgs.empty())
00254 {
00255 (*p_optfile) << "<command_line_args>" << std::endl;
00256 for (unsigned i=0; i<rArgs.size(); i++)
00257 {
00258 (*p_optfile) << "<arg>" << rArgs[i] << "</arg>" << std::endl;
00259 }
00260 (*p_optfile) << "</command_line_args>" << std::endl;
00261 }
00262 (*p_optfile) << rExtraXml << "</pycml_config>" << std::endl;
00263 p_optfile->close();
00264 }
00265 PetscTools::Barrier("CellMLToSharedLibraryConverter::CreateOptionsFile");
00266 }