CellMLToSharedLibraryConverter.cpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2011
00004 
00005 University of Oxford means the Chancellor, Masters and Scholars of the
00006 University of Oxford, having an administrative office at Wellington
00007 Square, Oxford OX1 2JD, UK.
00008 
00009 This file is part of Chaste.
00010 
00011 Chaste is free software: you can redistribute it and/or modify it
00012 under the terms of the GNU Lesser General Public License as published
00013 by the Free Software Foundation, either version 2.1 of the License, or
00014 (at your option) any later version.
00015 
00016 Chaste is distributed in the hope that it will be useful, but WITHOUT
00017 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
00019 License for more details. The offer of Chaste under the terms of the
00020 License is subject to the License being interpreted in accordance with
00021 English Law and subject to any action against the University of Oxford
00022 being under the jurisdiction of the English Courts.
00023 
00024 You should have received a copy of the GNU Lesser General Public License
00025 along with Chaste. If not, see <http://www.gnu.org/licenses/>.
00026 
00027 */
00028 
00029 #include "CellMLToSharedLibraryConverter.hpp"
00030 
00031 #include <sstream>
00032 #include <unistd.h> // For getpid()
00033 #include <sys/stat.h> // For mkdir()
00034 #include <ctime>
00035 #include <cstring> // For strerror()
00036 #include <cerrno> // For errno
00037 
00038 #include "Exception.hpp"
00039 #include "ChasteBuildRoot.hpp"
00040 #include "PetscTools.hpp"
00041 #include "DynamicModelLoaderRegistry.hpp"
00042 #include "GetCurrentWorkingDirectory.hpp"
00043 
00044 CellMLToSharedLibraryConverter::CellMLToSharedLibraryConverter(bool preserveGeneratedSources,
00045                                                                std::string component)
00046     : mPreserveGeneratedSources(preserveGeneratedSources),
00047       mComponentName(component)
00048 {
00049 }
00050 
00051 DynamicCellModelLoader* CellMLToSharedLibraryConverter::Convert(const FileFinder& rFilePath,
00052                                                                 bool isCollective)
00053 {
00054     DynamicCellModelLoader* p_loader;
00055     std::string absolute_path = rFilePath.GetAbsolutePath();
00056     // Check the file exists
00057     if (!rFilePath.Exists())
00058     {
00059         EXCEPTION("Dynamically loadable cell model '" + absolute_path + "' does not exist.");
00060     }
00061     // Find out whether rFilePath is a .cellml or .so
00062     size_t dot_position = absolute_path.find_last_of(".");
00063     if (dot_position == std::string::npos)
00064     {
00065         EXCEPTION("File does not have an extension: " + absolute_path);
00066     }
00067     std::string extension = absolute_path.substr(dot_position+1);
00068     if (extension == "cellml")
00069     {
00070         // Split the path into folder and leaf
00071         size_t slash_position = absolute_path.find_last_of("/\\");
00072         assert(slash_position != std::string::npos);
00073         std::string folder = absolute_path.substr(0, slash_position+1); // Include trailing slash
00074         std::string leaf = absolute_path.substr(slash_position+1, dot_position-slash_position); // Include dot
00075         std::string so_path = folder + "lib" + leaf + "so";
00076         // Does the .so file already exist (and was it modified after the .cellml?)
00077         FileFinder so_file(so_path, RelativeTo::Absolute);
00078         if (!so_file.Exists() || rFilePath.IsNewerThan(so_file))
00079         {
00080             if (!isCollective)
00081             {
00082                 EXCEPTION("Unable to convert .cellml to .so unless called collectively, due to possible race conditions.");
00083             }
00084             ConvertCellmlToSo(absolute_path, folder, leaf);
00085         }
00086         // Load the .so
00087         p_loader = DynamicModelLoaderRegistry::Instance()->GetLoader(so_file);
00088     }
00089     else if (extension == "so")
00090     {
00091         // Just load the .so
00092         p_loader = DynamicModelLoaderRegistry::Instance()->GetLoader(rFilePath);
00093     }
00094     else
00095     {
00096         EXCEPTION("Unsupported extension '." + extension + "' of file '" + absolute_path + "'; must be .so or .cellml");
00097     }
00098 
00099     return p_loader;
00100 }
00101 
00102 void CellMLToSharedLibraryConverter::ConvertCellmlToSo(const std::string& rCellmlFullPath,
00103                                                        const std::string& rCellmlFolder,
00104                                                        const std::string& rModelLeafName)
00105 {
00106     std::string tmp_folder, build_folder;
00107     std::string old_cwd = GetCurrentWorkingDirectory();
00108     // Check that the Chaste source tree exists
00109     FileFinder chaste_root("", RelativeTo::ChasteSourceRoot);
00110     if (!chaste_root.IsDir())
00111     {
00112         EXCEPTION("No Chaste source tree found at '" << chaste_root.GetAbsolutePath()
00113                   << "' - you need the source to use CellML models directly in Chaste.");
00114     }
00115     FileFinder component_dir(mComponentName, RelativeTo::ChasteSourceRoot);
00116     if (!component_dir.IsDir())
00117     {
00118         EXCEPTION("Unable to convert CellML model: required Chaste component '" << mComponentName
00119                   << "' does not exist in '" << ChasteBuildRootDir() << "'.");
00120     }
00121     // Try the conversion
00122     try
00123     {
00124         // Need to create a .so file from the CellML...
00125         if (PetscTools::AmMaster())
00126         {
00127             // Create a temporary folder within heart/dynamic
00128             std::stringstream folder_name;
00129             folder_name << "dynamic/tmp_" << getpid() << "_" << time(NULL);
00130             tmp_folder = component_dir.GetAbsolutePath() + "/" + folder_name.str();
00131             build_folder = component_dir.GetAbsolutePath() + "/build/" + ChasteBuildDirName() + "/" + folder_name.str();
00132             int ret = mkdir(tmp_folder.c_str(), 0700);
00133             if (ret != 0)
00134             {
00135                 EXCEPTION("Failed to create temporary folder '" << tmp_folder << "' for CellML conversion: "
00136                           << strerror(errno));
00137             }
00138             // Copy the .cellml file (and any relevant others) into the temporary folder
00139             size_t dot_pos = rCellmlFullPath.rfind('.');
00140             std::string cellml_base = rCellmlFullPath.substr(0, dot_pos);
00141             EXPECT0(system, "cp " + cellml_base + "* " + tmp_folder);
00142             // If there's a config file, copy that too
00143             std::string config_path = rCellmlFullPath.substr(0, rCellmlFullPath.length() - 7) + "-conf.xml";
00144             if (FileFinder(config_path, RelativeTo::Absolute).Exists())
00145             {
00146                 EXPECT0(system, "cp " + config_path + " " + tmp_folder);
00147             }
00148             // Change to Chaste source folder
00149             EXPECT0(chdir, ChasteBuildRootDir());
00150             // Run scons to generate C++ code and compile it to a .so
00151             EXPECT0(system, "scons --warn=no-all dyn_libs_only=1 build=" + ChasteBuildType() + " " + tmp_folder);
00152             EXCEPT_IF_NOT(FileFinder(tmp_folder + "/lib" + rModelLeafName + "so", RelativeTo::Absolute).Exists());
00153             // CD back
00154             EXPECT0(chdir, old_cwd);
00155             // Copy the .so to the same folder as the original .cellml file
00156             EXPECT0(system, "cp " + tmp_folder + "/lib" + rModelLeafName + "so " + rCellmlFolder);
00157             if (mPreserveGeneratedSources)
00158             {
00159                 // Copy generated source code as well
00160                 EXPECT0(system, "cp " + build_folder + "/*.?pp " + rCellmlFolder);
00161             }
00162             // Delete the temporary folders
00163             EXPECT0(system, "rm -r " + build_folder);
00164             EXPECT0(system, "rm -r " + tmp_folder);
00165         }
00166     }
00167     catch (Exception& e)
00168     {
00169         PetscTools::ReplicateException(true);
00170         if (FileFinder(tmp_folder, RelativeTo::Absolute).Exists())
00171         {
00172             if (mPreserveGeneratedSources)
00173             {
00174                 // Copy any temporary files
00175                 IGNORE_RET(system, "cp -r " + build_folder + " " + rCellmlFolder + "/build/");
00176                 IGNORE_RET(system, "cp -r " + tmp_folder + " " + rCellmlFolder + "/tmp/");
00177             }
00178             // Delete the temporary folders
00179             IGNORE_RET(system, "rm -rf " + build_folder); // -f because folder might not exist
00180             IGNORE_RET(system, "rm -r " + tmp_folder);
00181         }
00182         IGNORE_RET(chdir, old_cwd);
00183         EXCEPTION("Conversion of CellML to Chaste shared object failed. Error was: " + e.GetMessage());
00184     }
00185     // This also has the effect of a barrier, ensuring all processes wait for the
00186     // shared library to be created.
00187     PetscTools::ReplicateException(false);
00188 }
00189 
00190 void CellMLToSharedLibraryConverter::CreateOptionsFile(const OutputFileHandler& rHandler,
00191                                                        const std::string& rModelName,
00192                                                        const std::vector<std::string>& rArgs,
00193                                                        const std::string& rExtraXml)
00194 {
00195     if (PetscTools::AmMaster())
00196     {
00197         out_stream p_optfile = rHandler.OpenOutputFile(rModelName + "-conf.xml");
00198         (*p_optfile) << "<?xml version='1.0'?>" << std::endl
00199                      << "<pycml_config>" << std::endl;
00200         if (!rArgs.empty())
00201         {
00202             (*p_optfile) << "<command_line_args>" << std::endl;
00203             for (unsigned i=0; i<rArgs.size(); i++)
00204             {
00205                 (*p_optfile) << "<arg>" << rArgs[i] << "</arg>" << std::endl;
00206             }
00207             (*p_optfile) << "</command_line_args>" << std::endl;
00208         }
00209         (*p_optfile) << rExtraXml << "</pycml_config>" << std::endl;
00210         p_optfile->close();
00211     }
00212     PetscTools::Barrier("CellMLToSharedLibraryConverter::CreateOptionsFile");
00213 }
Generated on Thu Dec 22 13:00:06 2011 for Chaste by  doxygen 1.6.3