//C++ // syntax: initpackage pkgName dir model-init-file #include #include #include #include #include #include // for access call, making this program Unix dependent. #include #include #include #include #include #include "modelSetup.h" using namespace std; const string STATIC_LIB = "static_local_mods"; const string INI_EXTERN = "extern"; const string CREATE = "create"; const string INCLUDE = "#include"; const string FUNCTIONMAP = "FunctionMap"; const string ENDIF = "#endif"; const string funcMapContainer = "XSFunctionMap"; const string funcBaseClassPtr = "XSModelFunction"; const string containerType = "ModelFunctionMap"; const string f77funcType("xsf77Call"); const string CXXfuncType("XSCCall"); const string ccfuncType("xsccCall"); const string nl = "\n"; const string nl2 = "\n\n"; const string externC = "extern \"C\""; const string closeBrac = "}"; const string comment1 = "//\n// Code auto-generated by initpackage (XSPEC12 local model package \n// code generator). Do not edit\n"; const string initSuffix = "_Init"; const string safeInitSuffix = "_SafeInit"; const string fileInitPrefix = "lpack_"; const string tclInterp = "tclInterp"; const string initTclSignature = "(Tcl_Interp* " + tclInterp + ")"; const string userInterfaceHeader = ""; const string randomizerIndicator("randomize.dat"); char VERSION[] = "1.0"; int main (int argc, char** argv) { bool isStatic = false; vector argument; if ( argc < 4 ) { cerr << "\n initpackage requires 3 arguments:\n\n\t" << "initpackage |" << randomizerIndicator << " \n\n" << " where the code for the package is in writable directory dir\n" << " and initFile contains function call & parameter specifications\n\n"; if (argc > 1 ) { if (!strncmp(argv[1],"-h",2)) exit(0); } else exit (-1); } for (int j = 1; j < argc; ++j) { argument.push_back(argv[j]); } const string packageTypeStr(lowerCase(argument[1])); bool isRandomize = packageTypeStr == randomizerIndicator; // dir needs to be a writable directory const string directory = expandDirectoryPath(argument[2]); if ( directory.length() == 0 ) exit(-1); string packageName(lowerCase(argument[0])); if ( packageName != argument[0]) { cerr << "*** Warning - package names are case-insensitive: name " << argument[0] << " forced to lower case\n"; } // Since a file named .cxx will be created, // there shouldn't be any other code files (.cxx, .c, .f, etc.) in the // directory with the same root name or conflicting .o files // may result. This check simply looks out for any existing // files beginning with ".". string preExistingFile; int status = checkPreExistingFiles(directory, packageName, preExistingFile); if (status > 0) { cerr << "***Error: Pre-existing file " << preExistingFile << " will cause a name conflict with\n an initpackage-generated file. Please rename this file or the package name." << endl; exit(-1); } else if (status < 0) { cerr << "***Error while opening directory " << directory << endl; exit(-1); } ClassInfoContainer classesInfo; if (isRandomize) { string fullInitFile; if (checkForInitFile(directory, randomizerIndicator, fullInitFile)) { cerr << " Unable to locate and read " << randomizerIndicator << " file in directory " << directory << "\n"; exit(-1); } processRandomizerDat(fullInitFile, classesInfo); if (classesInfo.empty()) { cerr << " No classes listed in randomizer.dat file.\n"; exit(-1); } createPackageInitFile(directory, packageName, string(""), classesInfo, false); } else { const string& modelInitFile = argument[1]; if (packageName == STATIC_LIB) { cout << "\n Creating a STATIC local models library." << endl; isStatic = true; } // check for init file in either current directory or a specified directory. string fullModelInitFile; if (checkForInitFile(directory, modelInitFile, fullModelInitFile)) { cerr << " 2nd argument, model initialization file " << modelInitFile << " must exist and be readable\n"; exit(-1); } addFortranIncFile(directory); createFunctionMapFiles(directory, packageName, fullModelInitFile); createPackageInitFile(directory, packageName, fullModelInitFile, classesInfo, isStatic); } cout << "Writing Makefile for package...\n" ; writeMakefile(directory, packageName, isStatic, isRandomize); cout << "...done" << endl; return 0; } void writeMakefile(const string& dir, const string& packageName, bool isStatic, bool isRandomize) { const string MAKETEMPLATE("xspackage.tmpl"); const string ROOT("HEADAS"); const string MAKEFILE("Makefile"); string make(dir + "/" + MAKEFILE); const string LIBSTYLE("HD_LIB_STYLE\t\t="); const string LIBFLAG("#library"); const string SRCFLAG("#source"); const string LIBNAMEFLAG("#lib file name"); const string CLNFLAG("#clean"); const string SOURCEF("HD_LIBRARY_SRC_f\t="); const string SOURCEC("HD_LIBRARY_SRC_c\t="); const string SOURCECC("HD_LIBRARY_SRC_C\t="); const string SOURCECXX("HD_LIBRARY_SRC_cxx\t="); const string SOURCECPP("HD_LIBRARY_SRC_cpp\t="); const string SOURCELCC("HD_LIBRARY_SRC_cc\t="); const string PACKAGE("PACKAGE\t\t= lib${HD_LIBRARY_ROOT}"); const string EXTRASHLIBMARKER("### Additional SHLIB_LIBS"); const string RANDSHLIBS("\t\t-lXSFit -lXSPlot -lXSUser -l${TCLREADLINE}"); const string TABS("\t\t\t "); const size_t NOTFOUND (string::npos); // debug ostream_iterator x(cout,"\n"); // fortran Code vector f77Code(getCodeList(dir,"f")); // c Code // debug copy(f77Code.begin(),f77Code.end(),x); vector cCode(getCodeList(dir,"c")); // debug copy(cCode.begin(),cCode.end(),x); // C++ Code. Allow for .cxx, .cpp, .C vector CXXCode(getCodeList(dir,"cxx")); vector CPPCode(getCodeList(dir,"cpp")); vector CCCode(getCodeList(dir,"C")); vector LCCode(getCodeList(dir,"cc")); // C++ Code. Allow for .cxx, .cpp, .C // vector cppCode(getCodeList(dir,"cxx")); // vector tmpCppCode(getCodeList(dir,"C")); // copy(tmpCppCode.begin(),tmpCppCode.end(),back_inserter(cppCode)); // tmpCppCode.clear(); // ok, ok, I don't really have to do this. // tmpCppCode = getCodeList(dir,"cpp"); // copy(tmpCppCode.begin(),tmpCppCode.end(),back_inserter(cppCode)); // look for the makefile template. It is either in the current // directory (pre-installation) or the $HEADAS/lib directory // (post-installation). string xsRoot(""); if ( getenv(ROOT.c_str()) == NULL ) { cerr << "*** Error: environment variable HEADAS must be properly set\n" <<" to run initpackage.\n"; exit(-1); } xsRoot = string(getenv(ROOT.c_str())) + string("/lib/"); string makeTemplate = xsRoot + MAKETEMPLATE; struct stat* tempChk = new struct stat; if ( stat(makeTemplate.c_str(),tempChk) ) { cerr << "*** Error: initpackage has not been installed correctly: " << "\n*** missing makefile template (should be in $HEADAS/lib)\n"; delete tempChk; exit(-1); } else delete tempChk; ifstream makefileTemplate(makeTemplate.c_str(),ios_base::in); ofstream makefile(make.c_str(),ios_base::out); // first half: loop and print preamble. string line(""); size_t seekCheck(NOTFOUND); do { getline(makefileTemplate,line); makefile << line << '\n'; seekCheck = line.find(LIBFLAG); } while ( seekCheck == NOTFOUND && makefileTemplate ) ; makefile << flush; makefile << '\n' << "HD_LIBRARY_ROOT\t\t= " << packageName << "\n"; const string buildType = isStatic ? string(" static") : string(" shared"); makefile << '\n' << LIBSTYLE << buildType <<"\n"; if ( seekCheck == NOTFOUND ) { cerr << "Fatal: Template makefile corrupted: target " << LIBFLAG << " missing\n"; exit (-2); } do { getline(makefileTemplate,line); makefile << line << '\n'; seekCheck = line.find(SRCFLAG); } while ( seekCheck == NOTFOUND && makefileTemplate ) ; if ( seekCheck == NOTFOUND ) { cerr << "Fatal: Template makefile corrupted: target " << SRCFLAG << " missing\n"; exit (-2); } makefile << flush; // process fortran files. Since fgets leaves a newline terminator // on its arguments we need to remove it. makefile << '\n' << SOURCEF << setw(1) << " "; if ( f77Code.size()) { for (size_t j = 0; j < f77Code.size(); ++j) { string code = f77Code[j].erase(f77Code[j].find('\n')); if (j > 0) makefile << TABS; makefile << code; if ( j != f77Code.size() - 1) { makefile << setw(1) << " \\\n"; } else makefile << endl << '\n'; } } else makefile << '\n' << endl ; makefile << SOURCEC << setw(1) << " "; if ( cCode.size()) { for (size_t j = 0; j < cCode.size(); ++j) { string code = cCode[j].erase(cCode[j].find('\n')); if (j > 0) makefile << TABS; makefile << code; if ( j != cCode.size() - 1) { makefile << setw(1) << " \\\n"; } else makefile << endl << '\n'; } } else makefile << '\n' << endl ; // there is always C++ code present. makefile << SOURCECC << setw(1) << " "; if ( CCCode.size()) { for (size_t j = 0; j < CCCode.size(); ++j) { string code = CCCode[j].erase(CCCode[j].find('\n')); if (j > 0) makefile << TABS; makefile << code; if ( j != CCCode.size() - 1) { makefile << setw(1) << " \\\n"; } else makefile << endl << '\n'; } } else makefile << '\n' << endl ; makefile << SOURCECXX << setw(1) << " "; if ( CXXCode.size()) { for (size_t j = 0; j < CXXCode.size(); ++j) { string code = CXXCode[j].erase(CXXCode[j].find('\n')); if (j > 0) makefile << TABS; makefile << code; if ( j != CXXCode.size() - 1) { makefile << setw(1) << " \\\n"; } else makefile << endl << '\n'; } } else makefile << '\n' << endl ; makefile << SOURCECPP << setw(1) << " "; if ( CPPCode.size()) { for (size_t j = 0; j < CPPCode.size(); ++j) { string code = CPPCode[j].erase(CPPCode[j].find('\n')); if (j > 0) makefile << TABS; makefile << code; if ( j != CPPCode.size() - 1) { makefile << setw(1) << " \\\n"; } else makefile << endl << '\n'; } } else makefile << '\n' << endl ; makefile << SOURCELCC << setw(1) << " "; if ( LCCode.size()) { for (size_t j = 0; j < LCCode.size(); ++j) { string code = LCCode[j].erase(LCCode[j].find('\n')); if (j > 0) makefile << TABS; makefile << code; if ( j != LCCode.size() - 1) { makefile << setw(1) << " \\\n"; } else makefile << endl << '\n'; } } else makefile << '\n' << endl ; do { getline(makefileTemplate,line); makefile << line << '\n'; seekCheck = line.find(LIBNAMEFLAG); } while (seekCheck == NOTFOUND && makefileTemplate) ; const string suffix = isStatic ? string(".a") : string("${SHLIB_SUFFIX}"); makefile << PACKAGE << suffix << '\n'; do { getline(makefileTemplate,line); string::size_type extraLibPos = line.find(EXTRASHLIBMARKER); if (extraLibPos != string::npos) { if (isRandomize) { makefile << RANDSHLIBS << '\n'; } } else makefile << line << '\n'; } while (makefileTemplate) ; } vector getCodeList(const string& dir, const string& suffix) { static const int BUF(4096); static const string LSCMD("/bin/ls"); std::vector codeList; ostringstream get; get << "cd " << dir << "; " << LSCMD << " *." << suffix; if ( FILE* list = popen(get.str().c_str(),"r") ) { char* buffer (new char[BUF]); while (fgets(buffer,BUF,list)) codeList.push_back(string(buffer)); delete [] buffer; } return codeList; } int checkPreExistingFiles(const string& dir, const string& packageName, string& existingFileName) { // Look for any file in dir whose name begins with ".", // and if found immediately exit returning the full name of the file. DIR *pDir=0; if ((pDir = opendir(dir.c_str())) == NULL) { return -1; } struct dirent *pEntry=0; bool isFound = false; const string rootName(fileInitPrefix + packageName + "."); while (!isFound && (pEntry = readdir(pDir)) != NULL) { const string fileName(pEntry->d_name); if (fileName.find(rootName) == 0) { existingFileName = fileName; isFound = true; } } closedir(pDir); return static_cast(isFound); } int checkForInitFile(const string& dir, const string& modInitFile, string& fullModelInitFile) { fullModelInitFile = dir + "/" + modInitFile; if ( access(fullModelInitFile.c_str(),R_OK|F_OK) && access(modInitFile.c_str(),R_OK|F_OK)) { return -1; } else { // one of them exists. If a fully qualified filename was // entered and is accessible, use it. if (access(fullModelInitFile.c_str(),R_OK|F_OK)) { fullModelInitFile = modInitFile; } } return 0; } void addFortranIncFile(const string& directory) { const string fortranHeaderFileIn("xspec.h"); const string fortranHeaderFileOut("xspec.inc"); const string installationKey("HEADAS"); // Check for fortran include file in specified directory. If not there, // attempt to copy from headas distribution. const string fullOutFHeaderFile(directory + "/" + fortranHeaderFileOut); if (access(fullOutFHeaderFile.c_str(), F_OK)) { bool copySucceeded = false; char *headasLoc = getenv(installationKey.c_str()); if (headasLoc) { string fullInFHeaderFile(headasLoc); if (fullInFHeaderFile[fullInFHeaderFile.length()-1] != '/') fullInFHeaderFile += "/"; fullInFHeaderFile += "include/" + fortranHeaderFileIn; if (!access(fullInFHeaderFile.c_str(), R_OK|F_OK)) { string copyCommand("cp "); copyCommand += fullInFHeaderFile + " " + fullOutFHeaderFile; copySucceeded = !system(copyCommand.c_str()); } } if (!copySucceeded) { // This is not a fatal error. cout << "\n***Warning: Fortran header include file xspec.inc not found" << "\n in " << directory << "\n Unable to copy it from headas installation." << std::endl; } else { cout << "\nFortran header include file xspec.inc will be copied into\n" << directory << std::endl; } } } void createFunctionMapFiles(const string& directory, const string& packageName, const string& fullModelInitFile) { string fullMapFileStem(directory + "/" + packageName + FUNCTIONMAP); string mapHead(fullMapFileStem + ".h"); string mapFile(fullMapFileStem + ".cxx"); int width(0); width = mapFile.size() - directory.size(); cout << "\n The following files will be created in directory: " << directory << ":\n\n" << " " << left << setw(width) << packageName + FUNCTIONMAP + ".h" << "\theader specification file for functions\n" << " " << left << setw(width) << packageName + FUNCTIONMAP + ".cxx" << "\tfunction adding model functions/subroutines\n" << " " << left << setw(width) << packageName + ".cxx" << "\tinitializer function called by command interpreter\n " << " " << left << setw(width) << " " << "\ton loading of package\n"; cout << flush; //open the function map generator output files ofstream packageHeaderFile(mapHead.c_str(),ios_base::out); ofstream packageCodeFile(mapFile.c_str(),ios_base::out); // write package header preamble packageHeaderFile << comment1; packageHeaderFile << "// Package: " << packageName << " Created :" << nl; packageHeaderFile << "// Function header: " << packageName << FUNCTIONMAP << ".h" <" << nl2 << "class " << funcBaseClassPtr << ";" << nl2 << INI_EXTERN << " " << containerType << " " << funcMapContainer << ";" << nl2 << "void " << CREATE << packageName << FUNCTIONMAP << "();" << nl2 << externC << " {" << nl2 << endl; // write package code preamble packageCodeFile << comment1 << "// Package: " << packageName << " Created :" << nl << "// Function body: " << packageName << FUNCTIONMAP << ".cxx" << nl2 << INCLUDE << " \"" << packageName << FUNCTIONMAP << ".h\"" << nl2 << INCLUDE << " <" << funcBaseClassPtr << ".h>" << nl2 << "void \n" << CREATE << packageName << FUNCTIONMAP << "()\n{" << nl2 << endl; // open the model init file we already checked access permission on this file // so we will assume packageModelInitFile exists. // ifstream packageModelInitFile(fullModelInitFile.c_str(),ios_base::in); // // process the file, and report on its contents. // cout << "\nGathering information from model initialization file: " << fullModelInitFile << "...\n\n"; vector numbersOfComponents(processModelFile(packageModelInitFile, packageHeaderFile,packageCodeFile,packageName)); cout << "... done\n" << setw(4) << numbersOfComponents[0] << " Additive models\n" << setw(4) << numbersOfComponents[1] << " Multiplicative models\n" << setw(4) << numbersOfComponents[2] << " Convolution models\n" << setw(4) << numbersOfComponents[3] << " Mixing models\n" << setw(4) << numbersOfComponents[4] << " Pile-up models found altogether." << endl; packageModelInitFile.close(); packageCodeFile << nl << closeBrac << endl; packageCodeFile.close(); packageHeaderFile << closeBrac << nl << endl; packageHeaderFile << nl << ENDIF << endl; packageHeaderFile.close(); } void createPackageInitFile(const string& directory, const string& packageName, const string& fullModelInitFile, const ClassInfoContainer& classesInfo, bool isStatic) { // write package initializer preamble const bool isRandomize = !classesInfo.empty(); string pkgInitFile(directory + "/" + fileInitPrefix + packageName + ".cxx"); string pkgInitCall(packageName); pkgInitCall[0] = toupper(pkgInitCall[0]); ofstream packageInitFile(pkgInitFile.c_str(),ios_base::out); packageInitFile << comment1 << "// Package: " << packageName << " Created :" << nl << "// Initializer: " << packageName << ".cxx" << nl2 << INCLUDE << " " << userInterfaceHeader << nl2; if (isRandomize) { packageInitFile << INCLUDE << " \n" << INCLUDE << " \n" << INCLUDE << " \n" << INCLUDE << " \n"; for (size_t i=0; i\n"; } if (isStatic) { packageInitFile << nl << externC << " int " << pkgInitCall << initSuffix << "();" << nl2; } else { packageInitFile << nl << externC << " int " << pkgInitCall << initSuffix << initTclSignature << ";" << nl << externC << " int " << pkgInitCall << safeInitSuffix << initTclSignature << ";" << nl2; } // function body here. cout << "\nWriting Initializer Code for package: " << packageName << "..." ; if (isStatic) { packageInitFile << "int " << pkgInitCall << initSuffix << "()\n{" << nl << setw(8) << " " << CREATE << packageName << FUNCTIONMAP << "();" << nl << setw(8) << " " <<"XSModelFunction::updateComponentList" << nl << setw(16) << "(\"" << fullModelInitFile << "\");" << nl << setw(8) << " " << "return TCL_OK;"; } else { packageInitFile << "int " << pkgInitCall << initSuffix << initTclSignature << "\n{" << nl << setw(8) << " " << "return " << pkgInitCall << safeInitSuffix << "(" << tclInterp << ")" << ";" << nl << "}" << nl2; packageInitFile << "int " << pkgInitCall << safeInitSuffix << initTclSignature << "\n{" << nl2 << setw(8) << " " << "char PACKAGE[] = \"" << packageName << "\";" << nl << setw(8) << " " << "char VERSION[] = \"1.0\";" << nl << setw(8) << " " << "Tcl_PkgProvide(tclInterp, PACKAGE, VERSION);" << nl << setw(8) << " "; if (isRandomize) { packageInitFile << nl << setw(8) << " "; packageInitFile << "// The Tcl pkg_mkIndex command will get in here while doing\n" < rand(0);" << nl << setw(11) << " " << "try" << nl << setw(11) << " " << "{" << nl; for (size_t i=0; iregisterRandomizingStrategy(rand->name(),rand.get());" < [] string::size_type endPos = lineString.find_first_of(WS, startPos); string::size_type nChars = (endPos == string::npos) ? string::npos : endPos - startPos; string className(lineString.substr(startPos, nChars)); string ctorArgs; if (endPos != string::npos) { startPos = lineString.find_first_not_of(WS, endPos); if (startPos != string::npos) { // Notice that we're now searching from the end. // Intermediate whitespace between startPos and endPos // will be passed along. endPos = lineString.find_last_not_of(WS); nChars = endPos - startPos + 1; ctorArgs = lineString.substr(startPos, nChars); } } classes.push_back(make_pair(className, ctorArgs)); } // end if line not blank } while (!datFile.eof()); }