| /*========================================================================= |
| |
| Program: CMake - Cross-Platform Makefile Generator |
| Module: $RCSfile$ |
| Language: C++ |
| Date: $Date$ |
| Version: $Revision$ |
| |
| Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. |
| See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. |
| |
| This software is distributed WITHOUT ANY WARRANTY; without even |
| the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
| PURPOSE. See the above copyright notices for more information. |
| |
| =========================================================================*/ |
| #include "cmVTKMakeInstantiatorCommand.h" |
| |
| #include "cmCacheManager.h" |
| #include "cmGeneratedFileStream.h" |
| #include "cmSourceFile.h" |
| |
| bool |
| cmVTKMakeInstantiatorCommand |
| ::InitialPass(std::vector<std::string> const& argsIn) |
| { |
| if(argsIn.size() < 3) |
| { |
| this->SetError("called with incorrect number of arguments"); |
| return false; |
| } |
| std::vector<std::string> args; |
| this->Makefile->ExpandSourceListArguments(argsIn, args, 2); |
| std::string sourceListValue; |
| |
| this->ClassName = args[0]; |
| |
| std::vector<cmStdString> inSourceLists; |
| this->ExportMacro = "-"; |
| bool includesMode = false; |
| bool oldVersion = true; |
| |
| // Find the path of the files to be generated. |
| std::string filePath = this->Makefile->GetCurrentOutputDirectory(); |
| std::string headerPath = filePath; |
| |
| // Check whether to use the old or new form. |
| if(this->Makefile->GetDefinition("VTK_USE_INSTANTIATOR_NEW")) |
| { |
| oldVersion = false; |
| } |
| |
| for(unsigned int i=2;i < args.size();++i) |
| { |
| if(args[i] == "HEADER_LOCATION") |
| { |
| includesMode = false; |
| if(++i < args.size()) |
| { |
| headerPath = args[i]; |
| } |
| else |
| { |
| this->SetError("HEADER_LOCATION option used without value."); |
| return false; |
| } |
| } |
| else if(args[i] == "EXPORT_MACRO") |
| { |
| includesMode = false; |
| if(++i < args.size()) |
| { |
| this->ExportMacro = args[i]; |
| } |
| else |
| { |
| this->SetError("EXPORT_MACRO option used without value."); |
| return false; |
| } |
| } |
| else if(args[i] == "INCLUDES") |
| { |
| includesMode = true; |
| } |
| // If not an option, it must be another input source list name or |
| // an include file. |
| else |
| { |
| if(!includesMode) |
| { |
| inSourceLists.push_back(args[i]); |
| } |
| else |
| { |
| this->Includes.push_back(args[i]); |
| } |
| } |
| } |
| |
| if(this->ExportMacro == "-") |
| { |
| this->SetError("No EXPORT_MACRO option given."); |
| return false; |
| } |
| |
| for(std::vector<cmStdString>::const_iterator s = inSourceLists.begin(); |
| s != inSourceLists.end(); ++s) |
| { |
| std::string srcName = cmSystemTools::GetFilenameWithoutExtension(*s); |
| cmSourceFile *sf = this->Makefile->GetSource(s->c_str()); |
| |
| // Wrap-excluded and abstract classes do not have a New() method. |
| // vtkIndent and vtkTimeStamp are special cases and are not |
| // vtkObject subclasses. |
| if( |
| (!sf || (!sf->GetPropertyAsBool("WRAP_EXCLUDE") && |
| !sf->GetPropertyAsBool("ABSTRACT"))) && |
| ((srcName != "vtkIndent") && (srcName != "vtkTimeStamp"))) |
| { |
| this->Classes.push_back(srcName); |
| } |
| } |
| |
| // Generate the header with the class declaration. |
| { |
| std::string fileName = this->ClassName + ".h"; |
| std::string fullName = headerPath+"/"+fileName; |
| |
| // Generate the output file with copy-if-different. |
| cmGeneratedFileStream fout(fullName.c_str()); |
| fout.SetCopyIfDifferent(true); |
| |
| // Actually generate the code in the file. |
| if(!oldVersion) |
| { |
| this->GenerateHeaderFile(fout); |
| } |
| else |
| { |
| this->OldGenerateHeaderFile(fout); |
| } |
| } |
| |
| // Generate the implementation file. |
| { |
| std::string fileName = this->ClassName + ".cxx"; |
| std::string fullName = filePath+"/"+fileName; |
| |
| // Generate the output file with copy-if-different. |
| { |
| cmGeneratedFileStream fout(fullName.c_str()); |
| fout.SetCopyIfDifferent(true); |
| |
| // Actually generate the code in the file. |
| if(!oldVersion) |
| { |
| this->GenerateImplementationFile(fout); |
| } |
| else |
| { |
| this->OldGenerateImplementationFile(fout); |
| } |
| } |
| |
| // Add the generated source file into the source list. |
| cmSourceFile file; |
| file.SetProperty("WRAP_EXCLUDE","1"); |
| file.SetProperty("ABSTRACT","0"); |
| file.SetName(fileName.c_str(), filePath.c_str(), |
| this->Makefile->GetSourceExtensions(), |
| this->Makefile->GetHeaderExtensions()); |
| this->Makefile->AddSource(file); |
| sourceListValue += file.GetSourceName() + ".cxx"; |
| } |
| |
| if(oldVersion) |
| { |
| int groupSize = 10; |
| size_t numClasses = this->Classes.size(); |
| size_t numFullBlocks = numClasses / groupSize; |
| size_t lastBlockSize = numClasses % groupSize; |
| size_t numBlocks = numFullBlocks + ((lastBlockSize>0)? 1:0); |
| |
| // Generate the files with the ::New() calls to each class. These |
| // are done in groups to keep the translation unit size smaller. |
| for(unsigned int block=0; block < numBlocks;++block) |
| { |
| std::string fileName = this->OldGenerateCreationFileName(block); |
| std::string fullName = filePath+"/"+fileName; |
| |
| // Generate the output file with copy-if-different. |
| { |
| cmGeneratedFileStream fout(fullName.c_str()); |
| fout.SetCopyIfDifferent(true); |
| |
| size_t thisBlockSize = |
| (block < numFullBlocks)? groupSize:lastBlockSize; |
| |
| // Actually generate the code in the file. |
| this->OldGenerateCreationFile(fout, |
| block*groupSize, |
| static_cast<int>(thisBlockSize)); |
| } |
| |
| // Add the generated source file into the source list. |
| cmSourceFile file; |
| file.SetProperty("WRAP_EXCLUDE","1"); |
| file.SetProperty("ABSTRACT","0"); |
| file.SetName(fileName.c_str(), filePath.c_str(), |
| this->Makefile->GetSourceExtensions(), |
| this->Makefile->GetHeaderExtensions()); |
| this->Makefile->AddSource(file); |
| sourceListValue += ";"; |
| sourceListValue += file.GetSourceName() + ".cxx"; |
| } |
| } |
| |
| this->Makefile->AddDefinition(args[1].c_str(), sourceListValue.c_str()); |
| return true; |
| } |
| |
| // Generates the class header file with the definition of the class |
| // and its initializer class. |
| void |
| cmVTKMakeInstantiatorCommand |
| ::GenerateHeaderFile(std::ostream& os) |
| { |
| os << |
| "#ifndef __" << this->ClassName.c_str() << "_h\n" |
| "#define __" << this->ClassName.c_str() << "_h\n" |
| "\n" |
| "#include \"vtkInstantiator.h\"\n"; |
| for(unsigned int i=0;i < this->Includes.size();++i) |
| { |
| os << "#include \"" << this->Includes[i].c_str() << "\"\n"; |
| } |
| |
| // Write the instantiator class definition. |
| os << |
| "\n" |
| "class " << this->ExportMacro.c_str() |
| << " " << this->ClassName.c_str() << "\n" |
| "{\n" |
| "public:\n" |
| " " << this->ClassName.c_str() << "();\n" |
| " ~" << this->ClassName.c_str() << "();\n" |
| "private:\n" |
| " static void ClassInitialize();\n" |
| " static void ClassFinalize();\n" |
| " static unsigned int Count;\n" |
| "};\n" |
| "\n"; |
| |
| // Write the initialization instance to make sure the creation |
| // functions get registered when this generated header is included. |
| os << |
| "static " |
| << this->ClassName.c_str() << " " |
| << this->ClassName.c_str() << "Initializer;\n" |
| "\n" |
| "#endif\n"; |
| } |
| |
| // Generates the file with the implementation of the class. All |
| // methods except the actual object creation functions are generated |
| // here. |
| void |
| cmVTKMakeInstantiatorCommand |
| ::GenerateImplementationFile(std::ostream& os) |
| { |
| // Include the instantiator class header. |
| os << |
| "#include \"" << this->ClassName.c_str() << ".h\"\n" |
| "\n"; |
| |
| // Write the extern declarations for all the creation functions. |
| for(unsigned int i=0;i < this->Classes.size();++i) |
| { |
| os << "extern vtkObject* vtkInstantiator" << |
| this->Classes[i].c_str() << "New();\n"; |
| } |
| |
| // Write the ClassInitialize method to register all the creation functions. |
| os << |
| "\n" |
| "void " << this->ClassName.c_str() << "::ClassInitialize()\n" |
| "{\n"; |
| |
| for(unsigned int i=0;i < this->Classes.size();++i) |
| { |
| os << " vtkInstantiator::RegisterInstantiator(\"" |
| << this->Classes[i].c_str() << "\", vtkInstantiator" |
| << this->Classes[i].c_str() << "New);\n"; |
| } |
| |
| // Write the ClassFinalize method to unregister all the creation functions. |
| os << |
| "}\n" |
| "\n" |
| "void " << this->ClassName.c_str() << "::ClassFinalize()\n" |
| "{\n"; |
| |
| for(unsigned int i=0;i < this->Classes.size();++i) |
| { |
| os << " vtkInstantiator::UnRegisterInstantiator(\"" |
| << this->Classes[i].c_str() << "\", vtkInstantiator" |
| << this->Classes[i].c_str() << "New);\n"; |
| } |
| |
| // Write the constructor and destructor of the initializer class to |
| // call the ClassInitialize and ClassFinalize methods at the right |
| // time. |
| os << |
| "}\n" |
| "\n" << |
| this->ClassName.c_str() << "::" << this->ClassName.c_str() << "()\n" |
| "{\n" |
| " if(++" << this->ClassName.c_str() << "::Count == 1)\n" |
| " { " << this->ClassName.c_str() << "::ClassInitialize(); }\n" |
| "}\n" |
| "\n" << |
| this->ClassName.c_str() << "::~" << this->ClassName.c_str() << "()\n" |
| "{\n" |
| " if(--" << this->ClassName.c_str() << "::Count == 0)\n" |
| " { " << this->ClassName.c_str() << "::ClassFinalize(); }\n" |
| "}\n" |
| "\n" |
| "// Number of translation units that include this class's header.\n" |
| "// Purposely not initialized. Default is static initialization to 0.\n" |
| "unsigned int " << this->ClassName.c_str() << "::Count;\n"; |
| } |
| |
| std::string |
| cmVTKMakeInstantiatorCommand::OldGenerateCreationFileName(unsigned int block) |
| { |
| cmOStringStream nameStr; |
| nameStr << this->ClassName.c_str() << block << ".cxx"; |
| std::string result = nameStr.str(); |
| return result; |
| } |
| |
| // Generates a file that includes the headers of the classes it knows |
| // how to create and provides functions which create the classes with |
| // the New() method. |
| void |
| cmVTKMakeInstantiatorCommand |
| ::OldGenerateCreationFile(std::ostream& os, unsigned int groupStart, |
| unsigned int groupSize) |
| { |
| // Need to include header of generated class. |
| os << |
| "#include \"" << this->ClassName.c_str() << ".h\"\n" |
| "\n"; |
| |
| // Include class files. |
| for(unsigned int i=0;i < groupSize;++i) |
| { |
| os << "#include \"" << this->Classes[groupStart+i].c_str() << ".h\"\n"; |
| } |
| |
| os << |
| "\n"; |
| |
| // Write the create function implementations. |
| for(unsigned int i=0;i < groupSize;++i) |
| { |
| os << "vtkObject* " << this->ClassName.c_str() << "::Create_" |
| << this->Classes[groupStart+i].c_str() << "() { return " |
| << this->Classes[groupStart+i].c_str() << "::New(); }\n"; |
| } |
| } |
| |
| // Generates the class header file with the definition of the class |
| // and its initializer class. |
| void |
| cmVTKMakeInstantiatorCommand |
| ::OldGenerateHeaderFile(std::ostream& os) |
| { |
| os << |
| "#ifndef __" << this->ClassName.c_str() << "_h\n" |
| "#define __" << this->ClassName.c_str() << "_h\n" |
| "\n" |
| "#include \"vtkInstantiator.h\"\n"; |
| for(unsigned int i=0;i < this->Includes.size();++i) |
| { |
| os << "#include \"" << this->Includes[i].c_str() << "\"\n"; |
| } |
| os << |
| "\n" |
| "class " << this->ClassName.c_str() << "Initialize;\n" |
| "\n" |
| "class " << this->ExportMacro.c_str() << " " |
| << this->ClassName.c_str() << "\n" |
| "{\n" |
| " friend class " << this->ClassName.c_str() << "Initialize;\n" |
| "\n" |
| " static void ClassInitialize();\n" |
| " static void ClassFinalize();\n" |
| "\n"; |
| |
| for(unsigned int i=0;i < this->Classes.size();++i) |
| { |
| os << " static vtkObject* Create_" |
| << this->Classes[i].c_str() << "();\n"; |
| } |
| |
| // Write the initializer class to make sure the creation functions |
| // get registered when this generated header is included. |
| os << |
| "};\n" |
| "\n" |
| "class " << this->ExportMacro.c_str() << " " |
| << this->ClassName.c_str() << "Initialize\n" |
| "{\n" |
| "public:\n" |
| " " << this->ClassName.c_str() << "Initialize();\n" |
| " ~" << this->ClassName.c_str() << "Initialize();\n" |
| "private:\n" |
| " static unsigned int Count;\n" |
| "};\n" |
| "\n" |
| "static " << this->ClassName.c_str() << "Initialize " |
| << this->ClassName.c_str() << "Initializer;\n" |
| "\n" |
| "#endif\n"; |
| } |
| |
| // Generates the file with the implementation of the class. All |
| // methods except the actual object creation functions are generated |
| // here. |
| void cmVTKMakeInstantiatorCommand |
| ::OldGenerateImplementationFile(std::ostream& os) |
| { |
| // Write the ClassInitialize method to register all the creation functions. |
| os << |
| "#include \"" << this->ClassName.c_str() << ".h\"\n" |
| "\n" |
| "void " << this->ClassName.c_str() << "::ClassInitialize()\n" |
| "{\n"; |
| |
| for(unsigned int i=0;i < this->Classes.size();++i) |
| { |
| os << " vtkInstantiator::RegisterInstantiator(\"" |
| << this->Classes[i].c_str() << "\", " |
| << this->ClassName.c_str() << "::Create_" |
| << this->Classes[i].c_str() << ");\n"; |
| } |
| |
| // Write the ClassFinalize method to unregister all the creation functions. |
| os << |
| "}\n" |
| "\n" |
| "void " << this->ClassName.c_str() << "::ClassFinalize()\n" |
| "{\n"; |
| |
| for(unsigned int i=0;i < this->Classes.size();++i) |
| { |
| os << " vtkInstantiator::UnRegisterInstantiator(\"" |
| << this->Classes[i].c_str() << "\", " |
| << this->ClassName.c_str() << "::Create_" |
| << this->Classes[i].c_str() << ");\n"; |
| } |
| |
| // Write the constructor and destructor of the initializer class to |
| // call the ClassInitialize and ClassFinalize methods at the right |
| // time. |
| os << |
| "}\n" |
| "\n" << |
| this->ClassName.c_str() << "Initialize::" << |
| this->ClassName.c_str() << "Initialize()\n" |
| "{\n" |
| " if(++" << this->ClassName.c_str() << "Initialize::Count == 1)\n" |
| " { " << this->ClassName.c_str() << "::ClassInitialize(); }\n" |
| "}\n" |
| "\n" << |
| this->ClassName.c_str() << "Initialize::~" << |
| this->ClassName.c_str() << "Initialize()\n" |
| "{\n" |
| " if(--" << this->ClassName.c_str() << "Initialize::Count == 0)\n" |
| " { " << this->ClassName.c_str() << "::ClassFinalize(); }\n" |
| "}\n" |
| "\n" |
| "// Number of translation units that include this class's header.\n" |
| "// Purposely not initialized. Default is static initialization to 0.\n" |
| "unsigned int " << this->ClassName.c_str() << "Initialize::Count;\n"; |
| } |