/* Copyright (C) 2000-2004 Code contributed by Greg Collecutt, Joseph Hope and Paul Cochrane This file is part of xmds. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* $Id: main.cc,v 1.43 2005/10/26 03:46:28 joehope Exp $ */ /*! @mainpage Documentation for xmds-1.5-2 @author Paul Cochrane @section intro Introduction xmds is the eXtensible Multi-Dimensional Simulator. XMDS is a code generator that integrates equations. You write them down in human readable form in a XML file, and it goes away and writes and compiles a C++ program that integrates those equations as fast as it can possibly be done in your architecture. Originally written by Greg Collecutt (and the majority of the code base is still due to him), however is now maintained by Paul Cochrane. @section install Installation Download the source tarball from http://www.xmds.org, unpack, and run the configure script in the xmds directory.
(as root, to be installed into /usr/local/bin)
./configure
(as a user, to be installed the bin/ directory in your home directory)
./configure --with-user
For more details you can also read the INSTALL file, and even the hand-written documentation. */ /*! @file main.cc @brief The main routine and supporting routines More detailed explanation... */ // This is the main entry routine for xmds #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; // ********************************************************************* //! Displays xmds usage void display_usage() { printf("\n"); printf("This is xmds version "); printf(VERSION); printf(" release "); printf(RELEASE); printf("\n"); printf("\n"); printf(" using C compiler "); printf(XMDS_CC); printf("\n"); if(strcmp(MPICC,"")) { printf(" (and C compiler %s\n",MPICC); printf(" for parallel work)\n"); } printf( "\n" "Usage: xmds [options] infile\n" "Options:\n" " infile: required, The input file\n" " -h/--help: optional, Display this information\n" " -v/--verbose: optional, Verbose mode\n" " -n/--nocompile: optional, Turns off automatic compilation of simulation\n" " -t/--template: optional, Outputs an xmds template either to the terminal,\n" " or to an optionally specified file\n" "\n" "For further help, please see http://www.xmds.org\n" ); } // ******************************************************************** /*! @brief Routine to parse the preferences file @param fPrefs The input preferences file stream @param cc The string representing the C/C++ compiler @param cflags The C/C++ compiler flags @param clibs The libraries and library directories for the C/C++ compiler @param cincludes The include files and directories for the C/C++ compiler @param cthreadlibs The threading libraries for the C/C++ compiler @param mpicc The C/C++ compiler for MPI (i.e. parallel) simulations @param mpicflags The C/C++ compiler flags for the MPI C/C++ compiler @param fftwlibs The libraries for using fftw @param fftw_mpi_libs The libraries necessary for using fftw with MPI @param verbose Whether or not to print verbose information @param debug Whether or not to print debugging information */ int parsePrefs(ifstream &fPrefs, string &cc, string &cflags, string &clibs, string &cincludes, string &cthreadlibs, string &mpicc, string &mpicflags, string &fftwlibs, string &fftw_mpi_libs, bool verbose, bool debug) { /* I've thought of a better way to do this, but first I'll just get this version going so that the feature is in, and then I'll go back and make it a bit more elegant. The idea is to read in each line of the prefs file individually (into a string), and then process the line. I'll need to read characters until I find an equals sign (ignoring spaces as I go), biff that into macroVar and then grab everything else and put that into macroVarValue. I could treat the string read in (ie the line I'm parsing) as a stack and pop characters off it until the equals sign is found, putting chars into macroVar and then put the rest (without equals sign into macroVarValue. Anyway, to be done... Also, I should make this into a function so that the code is only written the once! */ // grab the text char c; string macroVar, macroVarValue, prefsString; bool commentCharFlag; prefsString = ""; while( !fPrefs.eof() ) { // now we try the next line macroVar = ""; macroVarValue = ""; commentCharFlag = 0; // wait until we find the equals sign while( (c = fPrefs.get()) != '=' && !fPrefs.eof() && !commentCharFlag) { if (c == ' ') { if (debug) { cout << "space character before '=' found\n"; } } else if (c == '#') { if (debug) { cout << "comment character found (before '=' found)\n"; } commentCharFlag = 1; while ( (c = fPrefs.get()) != '\n' && !fPrefs.eof() ) { if (debug) { cout << "looping until end of line\n"; } // cout << "c = " << c << "\n"; } break; } else { macroVar = macroVar + c; } } // now loop until we find the return character while( !commentCharFlag && (c = fPrefs.get()) != '\n' && !fPrefs.eof()) { if (c == '#') { if (debug) { cout << "comment character found\n"; } commentCharFlag = 1; while ( (c = fPrefs.get()) != '\n' && !fPrefs.eof() ) { if (debug) { cout << "looping until end of line\n"; } // cout << "c = " << c << "\n"; } break; } macroVarValue = macroVarValue + c; } // cout << "macroVar = " + macroVar + "\n"; // cout << "macroVarValue = " + macroVarValue + "\n"; // now do some assignments if (macroVar == "XMDS_CC") { cc = macroVarValue; if (verbose) { cout << "cc set to " + macroVarValue + "\n"; } } else if (macroVar == "XMDS_CFLAGS") { cflags = macroVarValue; if (verbose) { cout << "cflags set to " + macroVarValue + "\n"; } } else if (macroVar == "XMDS_LIBS") { clibs = macroVarValue; if (verbose) { cout << "clibs set to " + macroVarValue + "\n"; } } else if (macroVar == "XMDS_INCLUDES") { cincludes = macroVarValue; if (verbose) { cout << "cincludes set to " + macroVarValue + "\n"; } } else if (macroVar == "THREADLIBS") { cthreadlibs = macroVarValue; if (verbose) { cout << "cthreadlibs set to " + macroVarValue + "\n"; } } else if (macroVar == "MPICC") { mpicc = macroVarValue; if (verbose) { cout << "mpicc set to " + macroVarValue + "\n"; } } else if (macroVar == "MPICCFLAGS") { mpicflags = macroVarValue; if (verbose) { cout << "mpicflags set to " + macroVarValue + "\n"; } } else if (macroVar == "FFTW_LIBS") { fftwlibs = macroVarValue; if (verbose) { cout << "fftwlibs set to " + macroVarValue + "\n"; } } else if (macroVar == "FFTW_MPI_LIBS") { fftw_mpi_libs = macroVarValue; if (verbose) { cout << "fftw_mpi_libs set to " + macroVarValue + "\n"; } } } return 0; } // ******************************************************************** //! Routine to write to either stdout or file a template simulation script /*! @param outfilename The output filename to sent the template simulation script to */ void outputTemplate(const char* outfilename) { // this is the template text, at present taken directly from the // tutorial, tutTemplateStart.tex of the latex documentation // and relevant characters escaped so that they print out properly const char* templateText = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " no \n" " \n" " \n" " 1 2 \n" " \n" " \n" " no \n" " \n" " Scheduling \n" " yes \n" " yes \n" " yes \n" " yes \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " main \n" " \n" " \n" " (,) \n" " \n" " \n" " \n" " main \n" " complex \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " yes \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n"; // check to see if an output file was given if(outfilename==0) { // ok, so no input file specified, we therefore spit it out to stdout printf("%s",templateText); } else if (outfilename!=0) { // ok, we have an input file, open it, biff out the string, close it // btw, why am I using the old C syntax for this and not C++??? FILE *fp; fp = fopen(outfilename, "w"); if (fp == NULL) { // make sure can actually open the file printf("Unable to open output file: %s\n",outfilename); printf("Sending output to stdout\n\n"); printf("%s",templateText); } else { printf("This is xmds, version %s-%s\n", VERSION, RELEASE); printf("Copyright 2000-2004 Greg Collecutt, Joseph Hope, Paul Cochrane and others\n"); printf("xmds is available from http://www.xmds.org\n\n"); printf("Writing a template to file with filename: %s\n",outfilename); fprintf(fp, "%s", templateText); printf("Done!\n"); } fclose(fp); } } /* ******************************************************************** */ bool debugFlag = 0; //!< Print debugging information about xmds processes bool xmlDebugFlag = 0; //!< Print debugging information about xml parsing processes vector simulationText; //!< The text of the xmds simulation script vector simHeaderText; //!< The text of the xmds script's header vector simBodyText; //!< The text of the xmds script's body vector simFooterText; //!< The text of the xmds script's footer /*! @brief The main routine. @param argc The number of arguments to the program @param argv The "vector" of arguments to the program */ int main( int argc, char **argv) { bool verbose = 0; bool compileFlag = 1; bool templateGenFlag = 0; const char* infilename=0; int resp; while (1) { static struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, {"debug", no_argument, 0, 'd'}, {"nocompile", no_argument, 0, 'n'}, {"template", optional_argument, 0, 't'}, {"xmldebug", no_argument, 0, 'x'}, {0,0,0,0} }; int option_index = 0; resp = getopt_xmds_long(argc, argv, "hvdnxt", long_options, &option_index); if (resp == -1) { break; } switch (resp) { case 'h': display_usage(); return 0; case 'v': verbose = 1; break; case 'd': debugFlag = 1; break; case 'n': compileFlag = 0; break; case 't': templateGenFlag = 1; break; case 'x': xmlDebugFlag = 1; break; default: display_usage(); return 0; } } // process non-option command line elements if (optind_xmds < argc) { int fnameCount = 0; while (optind_xmds < argc) { fnameCount++; if (fnameCount > 1) { // error, input file name already exists printf("Error: multiple input files not allowed\n\n"); display_usage(); return 0; } // assign infilename pointer to the appropriate // member of the argv array infilename = argv[optind_xmds++]; } } // if asked to make a template, then just spit it out, either to file // or to stdout and then return nicely if (templateGenFlag) { // at present, we'll reuse the input file. I intend to change this to // use the getopt_xmds stuff later, so will have to do that at some stage. outputTemplate(infilename); return 0; } // check to see that an input file was given if(infilename==0) { // error, no input file was specified printf("Error: no input file specified!\n\n"); display_usage(); return 0; } if(verbose) { printf("xmds: inputfile = '%s'\n",infilename); } // create the XMLParser XMLParser myXMLParser; // now load xmds script into the DOMImplementation Document* theDocument=0; if(verbose) { printf("Parsing file '%s' ...\n",infilename); } try { theDocument=myXMLParser.parseFromFile(infilename); // now load the xmds file into memory for later use // I tried doing this in vanilla C++, but couldn't // it looks like it'll have to be in C for the most part FILE *fin; if ((fin = fopen(infilename,"r")) == NULL) { printf("Can't open the input xmds script file: %s\n" "Exiting\n" ,infilename); return 1; // and barf } // now grab the file one line at a time unsigned char temp; string tempString = ""; while (!feof(fin)) { temp = fgetc(fin); if (temp != '\n') { tempString += temp; } else { simulationText.push_back(tempString); tempString = ""; } } fclose(fin); if (debugFlag) { for (unsigned int i=0; i tag // then, go from the back, dropping anything that isn't a > symbol, // keep that, and then try and get a tag. // the header, (ie the (plus possibly more text) // and then the tag), the body (the rest of the simulationText // up until the footer, which is just the tag. // This ripping to bits is necessary so that we can piece together the // simulation script with the xsil output at the end of the simulation // without relying on system() calls. // search for the text "" // barf if we get to the end of the file, and still haven't found it. string simulationStartTag = ""; string simulationEndTag = ""; bool foundSimStartTag = false, foundSimEndTag = false; // go through the text and rip out the header, body and footer for (unsigned int i=0; i start tag when pulling to bits!\n"); } } if (simulationText[i].find(simulationEndTag) != string::npos) { foundSimEndTag = true; if (verbose) { printf("Found the end tag when pulling to bits!\n"); } } if (!foundSimStartTag && !foundSimEndTag) { simHeaderText.push_back(simulationText[i]); } if (foundSimStartTag && !foundSimEndTag) { simBodyText.push_back(simulationText[i]); } if (foundSimStartTag && foundSimEndTag) { simFooterText.push_back(simulationText[i]); } } // if we got to here and foundSimStart tag is still false, then barf appropriately if (!foundSimStartTag) { printf("Failed to find the string \"\" within the simulation text\n"); printf("Exiting\n"); // I'm sure we should do something more intelligent here... return 1; } if (debugFlag) { // have a look at the header if it exists cout << "-----------------------------\n"; cout << "The simulation header follows:\n"; for (unsigned int i=0; i\" within the simulation text\n"); printf("Exiting\n"); // I'm sure we should do something more intelligent here... return 1; } if (debugFlag) { // have a look at the footer if it exits cout << "-----------------------------\n"; cout << "The simulation footer follows:\n"; for (unsigned int i=0; idocumentElement()->nodeName() != "simulation") { printf("Error: Expecting root element in '%s' to be \n",infilename); printf("Exiting.\n"); return 1; } unsigned long xmdsBytePoint = myXMLParser.xmdsBytePoint(); // create the xmdsSimulation xmdsSimulation myxmdsSimulation(infilename,verbose,strcmp(MPICC,"")); if(verbose) { printf("Processing simulation ...\n"); } // print out some info about xmds printf("This is xmds, version %s-%s\n", myxmdsSimulation.parameters()->version.c_str(), myxmdsSimulation.parameters()->release.c_str()); printf("Copyright 2000-2004 Greg Collecutt, Joseph Hope, Paul Cochrane and others\n"); printf("xmds is available from http://www.xmds.org\n\n"); try { myxmdsSimulation.processElement(theDocument->documentElement()); } catch (xmdsException xmdsExceptionErr) { printf("Error: simulation element could not be processed\n"); printf("due to the following xmdsException:\n"); printf("%s",xmdsExceptionErr.getError()); printf("Exiting.\n"); return 1; } if(verbose) { printf("Writing output code ...\n"); } try { myxmdsSimulation.makeCode(xmdsBytePoint); } catch (xmdsException xmdsExceptionErr) { printf("Error: simulation failed to write output code\n"); printf("due to the following xmdsException:\n"); printf("%s",xmdsExceptionErr.getError()); printf("Exiting.\n"); return 1; } string cc = XMDS_CC; string cflags = XMDS_CFLAGS; string mpicc = MPICC; string mpicflags = MPICCFLAGS; string cincludes = XMDS_INCLUDES; string clibs = XMDS_LIBS; string fftwlibs = FFTW_LIBS; string fftw_mpi_libs = FFTW_MPI_LIBS; string cthreadlibs = THREADLIBS; // this is just some code to show what the defaults are if (verbose) { cout << "Defaults: (from when xmds was built)\n" << " cc = " << cc << "\n" << " cflags = " << cflags << "\n" << " mpicc = " << mpicc << "\n" << " mpicflags = " << mpicflags << "\n" << " cincludes = " << cincludes << "\n" << " clibs = " << clibs << "\n" << " fftwlibs = " << fftwlibs << "\n" << " fftw_mpi_libs = " << fftw_mpi_libs << "\n" << " cthreadlibs = " << cthreadlibs << "\n"; } // if the usePrefs flag is true then try to find the prefs file // and then try to parse it, falling back to the above values if // we fail if (myxmdsSimulation.parameters()->usePrefs) { if (verbose) { printf("Using user-defined preferences\n"); } // now try and open the file // first look in ~/.xmds/xmds.prefs // work out what the home directory is ifstream fIn, fPrefs; string findHomeString, homeStuff, homeDir, rmString; homeStuff = "home.stuff"; findHomeString = "echo $HOME > " + homeStuff; system(findHomeString.c_str()); fIn.open(homeStuff.c_str()); if (fIn.fail()) { // do something } fIn >> homeDir; fIn.close(); rmString = "rm " + homeStuff; system(rmString.c_str()); // ~/.xmds/xmds.prefs string prefsFname; prefsFname = homeDir + "/.xmds/xmds.prefs"; fPrefs.open(prefsFname.c_str()); if (!fPrefs.fail()) { if (verbose) { printf("Prefs file found: %s\n", prefsFname.c_str()); } // ok, now try and parse the sucker... parsePrefs(fPrefs, cc, cflags, clibs, cincludes, cthreadlibs, mpicc, mpicflags, fftwlibs, fftw_mpi_libs, verbose, debugFlag); fPrefs.close(); } // if that didn't work, try the local directory else if (fPrefs.fail()) { string localDir; if (verbose) { printf("Prefs file not found at %s\n", prefsFname.c_str()); printf("Trying in the local directory\n"); // work out what the local directory is, and report it system("echo $PWD > localDir.test"); fIn.open("localDir.test"); if (fIn.fail()) { // do something } fIn >> localDir; fIn.close(); system("rm localDir.test"); printf("The local directory is %s\n", localDir.c_str()); } prefsFname = "xmds.prefs"; ifstream fPrefs; // need to define fPrefs again (local to this block) fPrefs.open(prefsFname.c_str()); if (!fPrefs.fail()) { if (verbose) { printf("Prefs file found in local directory: %s\n", localDir.c_str()); } // ok, now try and parse the sucker... parsePrefs(fPrefs, cc, cflags, clibs, cincludes, cthreadlibs, mpicc, mpicflags, fftwlibs, fftw_mpi_libs, verbose, debugFlag); fPrefs.close(); } // if we get to here, and things have still failed, print a warning // and just use the defaults else if (fPrefs.fail() && verbose) { printf("Warning: no preferences file found. Using default values instead\n"); } } } if (!myxmdsSimulation.parameters()->usePrefs && verbose) { printf("Warning: User-defined preferences NOT being used. Using defaults instead\n"); } // this is just some code to show what the compilation values are after the prefs have been added if (verbose) { cout << "User defined preferences: (some will be the default values)\n" << "These are used by xmds to build the simulation\n" << " cc = " << cc << "\n" << " cflags = " << cflags << "\n" << " mpicc = " << mpicc << "\n" << " mpicflags = " << mpicflags << "\n" << " cincludes = " << cincludes << "\n" << " clibs = " << clibs << "\n" << " fftwlibs = " << fftwlibs << "\n" << " fftw_mpi_libs = " << fftw_mpi_libs << "\n" << " cthreadlibs = " << cthreadlibs << "\n"; } // now set up the system command to compile the simulation char command[1024]; if (compileFlag) { if(myxmdsSimulation.parameters()->usempi) { //Joe mark if((myxmdsSimulation.parameters()->nThreads>1)||(myxmdsSimulation.parameters()->mpiMethod=="Scheduling")) { printf("compiling for MPI parallel execution with threads...\n"); sprintf(command,"%s -D_REENTRANT -o %s %s.cc %s %s %s %s", mpicc.c_str(), myxmdsSimulation.parameters()->simulationName.c_str(), myxmdsSimulation.parameters()->simulationName.c_str(), mpicflags.c_str(), cincludes.c_str(),cthreadlibs.c_str(),fftw_mpi_libs.c_str()); printf(" %s\n",command); if(system(command)) { printf("compilation failed.\n"); return 1; } } else if(!(myxmdsSimulation.parameters()->stochastic)){ printf("compiling for MPI parallel execution for a nondeterministic simulation...\n"); sprintf(command,"%s -o %s %s.cc %s %s %s", mpicc.c_str(), myxmdsSimulation.parameters()->simulationName.c_str(), myxmdsSimulation.parameters()->simulationName.c_str(), mpicflags.c_str(), cincludes.c_str(),fftw_mpi_libs.c_str()); printf(" %s\n",command); if(system(command)) { printf("compilation failed.\n"); return 1; } } else { printf("compiling for MPI parallel execution ...\n"); sprintf(command,"%s -o %s %s.cc %s %s %s", mpicc.c_str(), myxmdsSimulation.parameters()->simulationName.c_str(), myxmdsSimulation.parameters()->simulationName.c_str(), mpicflags.c_str(), cincludes.c_str(),fftw_mpi_libs.c_str()); printf(" %s\n",command); if(system(command)) { printf("compilation failed.\n"); return 1; } } } else { if(myxmdsSimulation.parameters()->nThreads>1) { printf("compiling for threaded parallel execution...\n"); sprintf(command,"%s -D_REENTRANT %s -o %s %s.cc %s %s %s", cc.c_str(),cflags.c_str(), myxmdsSimulation.parameters()->simulationName.c_str(), myxmdsSimulation.parameters()->simulationName.c_str(), cincludes.c_str(),cthreadlibs.c_str(),fftwlibs.c_str()); printf(" %s\n",command); if(system(command)) { printf("compilation failed.\n"); return 1; } } else { printf("compiling ...\n"); sprintf(command,"%s %s -o %s %s.cc %s %s %s", cc.c_str(),cflags.c_str(), myxmdsSimulation.parameters()->simulationName.c_str(), myxmdsSimulation.parameters()->simulationName.c_str(), cincludes.c_str(),clibs.c_str(),fftwlibs.c_str()); printf(" %s\n",command); if(system(command)) { printf("compilation failed.\n"); return 1; } } } printf("\n%s ready to execute\n",myxmdsSimulation.parameters()->simulationName.c_str()); } return 0; }