/*
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";
// 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;
}