/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile$ Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) 2002 Insight Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm 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 "cmNMakeMakefileGenerator.h" #include "cmMakefile.h" #include "cmStandardIncludes.h" #include "cmSystemTools.h" #include "cmSourceFile.h" #include "cmMakeDepend.h" #include "cmCacheManager.h" #include "cmGeneratedFileStream.h" #include "windows.h" cmNMakeMakefileGenerator::cmNMakeMakefileGenerator() { this->SetLibraryPathOption("@CMAKE_C_LIBPATH_FLAG@"); // Use @ here this->SetObjectFileExtension("$(CMAKE_OBJECT_FILE_SUFFIX)"); this->SetExecutableExtension("$(CMAKE_EXECUTABLE_SUFFIX)"); this->SetLibraryPrefix(""); this->SetStaticLibraryExtension("$(CMAKE_STATICLIB_SUFFIX)"); this->SetSharedLibraryExtension("$(CMAKE_SHLIB_SUFFIX)"); } cmNMakeMakefileGenerator::~cmNMakeMakefileGenerator() { } // convert to windows short paths if there are spaces // in path std::string cmNMakeMakefileGenerator::ShortPath(const char* path) { std::string ret = path; // if there are no spaces in path, then just // call ConvertToOutputPath if(ret.find(' ') == std::string::npos) { return this->ConvertToOutputPath(path); } // if there are spaces then call GetShortPathName to get rid of them char *buffer = new char[strlen(path)+1]; if(GetShortPathName(path, buffer, static_cast(strlen(path)+1)) != 0) { ret = buffer; } else { // if GetShortPathName failed for some reason use // ConvertToOutputPath instead which will at least escape the spaces ret = this->ConvertToOutputPath(path); } delete [] buffer; return ret; } // convert a command to a short path if it has spaces // this separates the arguments from the command and puts // them back together std::string cmNMakeMakefileGenerator::ShortPathCommand(const char* command) { if(!strchr(command, ' ')) { return command; } cmRegularExpression reg("^\"([^\"]*)\"(.*)"); if(reg.find(command)) { std::string c = reg.match(1); cmRegularExpression removeIntDir("(.*)/\\$\\(IntDir\\)(.*)"); if(removeIntDir.find(c)) { c = removeIntDir.match(1) + removeIntDir.match(2); } c = this->ShortPath(c.c_str()); std::string ret = c; std::string args = reg.match(2); ret += args; return ret; } return command; } void cmNMakeMakefileGenerator::ComputeSystemInfo() { // now load the settings if(!m_Makefile->GetDefinition("CMAKE_ROOT")) { cmSystemTools::Error( "CMAKE_ROOT has not been defined, bad GUI or driver program"); return; } std::string fpath = m_Makefile->GetDefinition("CMAKE_ROOT"); fpath += "/Templates/CMakeNMakeWindowsSystemConfig.cmake"; m_Makefile->ReadListFile(NULL,fpath.c_str()); } void cmNMakeMakefileGenerator::OutputMakeVariables(std::ostream& fout) { fout << "# NMake Makefile generated by cmake\n"; const char* variables = "# general variables used in the makefile\n" "\n" "# Path to cmake\n" "MAKESILENT = /nologo\n" "CMAKE_STANDARD_WINDOWS_LIBRARIES = @CMAKE_STANDARD_WINDOWS_LIBRARIES@\n" "CMAKE_C_FLAGS = @CMAKE_C_FLAGS@ @BUILD_FLAGS@\n" "CMAKE_C_LINK_EXECUTABLE_FLAG = @CMAKE_C_LINK_EXECUTABLE_FLAG@\n" "CMAKE_CXX_FLAGS = @CMAKE_CXX_FLAGS@ @BUILD_FLAGS@\n" "CMAKE_LINKER_FLAGS = @CMAKE_LINKER_FLAGS@ @LINKER_BUILD_FLAGS@\n" "CMAKE_LINKER_SHARED_LIBRARY_FLAG = @CMAKE_LINKER_SHARED_LIBRARY_FLAG@\n" "CMAKE_LIBRARY_MANAGER_FLAGS = @CMAKE_LIBRARY_MANAGER_FLAGS@\n" "CMAKE_OBJECT_FILE_SUFFIX = @CMAKE_OBJECT_FILE_SUFFIX@\n" "CMAKE_EXECUTABLE_SUFFIX = @CMAKE_EXECUTABLE_SUFFIX@\n" "CMAKE_STATICLIB_SUFFIX = @CMAKE_STATICLIB_SUFFIX@\n" "CMAKE_SHLIB_SUFFIX = @CMAKE_SHLIB_SUFFIX@\n" "!IF \"$(OS)\" == \"Windows_NT\"\n" "NULL=\n" "!ELSE \n" "NULL=nul\n" "!ENDIF \n" "RM = del\n"; std::string buildType = "CMAKE_CXX_FLAGS_"; buildType += m_Makefile->GetDefinition("CMAKE_BUILD_TYPE"); buildType = cmSystemTools::UpperCase(buildType); m_Makefile->AddDefinition("BUILD_FLAGS", m_Makefile->GetDefinition( buildType.c_str())); buildType = "CMAKE_LINKER_FLAGS_"; buildType += m_Makefile->GetDefinition("CMAKE_BUILD_TYPE"); buildType = cmSystemTools::UpperCase(buildType); m_Makefile->AddDefinition("LINKER_BUILD_FLAGS", m_Makefile->GetDefinition( buildType.c_str())); std::string replaceVars = variables; m_Makefile->ExpandVariablesInString(replaceVars); fout << replaceVars.c_str(); std::string ccompiler = m_Makefile->GetDefinition("CMAKE_C_COMPILER"); fout << "CMAKE_C_COMPILER = " << this->ConvertToOutputPath(ccompiler.c_str()) << "\n"; std::string cxxcompiler = m_Makefile->GetDefinition("CMAKE_CXX_COMPILER"); fout << "CMAKE_CXX_COMPILER = " << this->ConvertToOutputPath(cxxcompiler.c_str()) << "\n"; std::string linker = m_Makefile->GetDefinition("CMAKE_LINKER"); fout << "CMAKE_LINKER = " << this->ConvertToOutputPath(linker.c_str()) << "\n"; std::string lib_manager = m_Makefile->GetDefinition("CMAKE_LIBRARY_MANAGER"); fout << "CMAKE_LIBRARY_MANAGER = " << this->ConvertToOutputPath(lib_manager.c_str()) << "\n"; std::string cmakecommand = m_Makefile->GetDefinition("CMAKE_COMMAND"); fout << "CMAKE_COMMAND = " << this->ConvertToOutputPath(cmakecommand.c_str()) << "\n"; fout << "CMAKE_CURRENT_SOURCE = " << ShortPath(m_Makefile->GetStartDirectory() ) << "\n"; fout << "CMAKE_CURRENT_BINARY = " << ShortPath(m_Makefile->GetStartOutputDirectory()) << "\n"; fout << "CMAKE_SOURCE_DIR = " << ShortPath(m_Makefile->GetHomeDirectory()) << "\n"; fout << "CMAKE_BINARY_DIR = " << ShortPath(m_Makefile->GetHomeOutputDirectory() ) << "\n"; // Output Include paths fout << "INCLUDE_FLAGS = "; std::vector& includes = m_Makefile->GetIncludeDirectories(); std::vector::iterator i; fout << "-I" << this->ConvertToOutputPath(m_Makefile->GetStartDirectory()) << " "; for(i = includes.begin(); i != includes.end(); ++i) { std::string include = *i; // Don't output a -I for the standard include path "/usr/include". // This can cause problems with certain standard library // implementations because the wrong headers may be found first. fout << "-I" << this->ConvertToOutputPath(i->c_str()).c_str() << " "; } fout << m_Makefile->GetDefineFlags(); fout << "\n\n"; } void cmNMakeMakefileGenerator::BuildInSubDirectory(std::ostream& fout, const char* directory, const char* target1, const char* target2) { if(target1) { std::string dir = this->ConvertToOutputPath(directory); fout << "\tif not exist \"" << dir << "\\$(NULL)\"" << " " << "$(MAKE) $(MAKESILENT) rebuild_cache\n" << "\techo Building " << target1 << " in directory " << directory << "\n" << "\tcd " << dir << "\n" << "\t$(MAKE) -$(MAKEFLAGS) $(MAKESILENT) " << target1 << "\n"; } if(target2) { fout << "\techo Building " << target2 << " in directory " << directory << "\n"; fout << "\t$(MAKE) -$(MAKEFLAGS) $(MAKESILENT) " << target2 << "\n"; } std::string currentDir = m_Makefile->GetCurrentOutputDirectory(); fout << "\tcd " << this->ConvertToOutputPath(currentDir.c_str()) << "\n\n"; } // This needs to be overriden because nmake requires commands to be quoted // if the are full paths to the executable???? void cmNMakeMakefileGenerator::OutputMakeRule(std::ostream& fout, const char* comment, const char* target, const char* depends, const char* command, const char* command2, const char* command3, const char* command4) { if(!target) { cmSystemTools::Error("no target for OutputMakeRule"); return; } std::string replace; if(comment) { replace = comment; m_Makefile->ExpandVariablesInString(replace); fout << "#---------------------------------------------------------\n"; fout << "# " << comment; fout << "\n#\n"; } fout << "\n"; replace = target; m_Makefile->ExpandVariablesInString(replace); replace = this->ConvertToOutputPath(replace.c_str()); fout << replace.c_str() << ": "; if(depends) { replace = depends; m_Makefile->ExpandVariablesInString(replace); fout << replace.c_str(); } fout << "\n"; if(command) { replace = ShortPathCommand(command); m_Makefile->ExpandVariablesInString(replace); if(replace[0] != '-' && replace.find("echo") != 0 && replace.find("$(MAKE)") != 0) { fout << "\t" << "echo " << replace.c_str() << "\n"; } fout << "\t" << replace.c_str() << "\n"; } if(command2) { replace = ShortPathCommand(command2); m_Makefile->ExpandVariablesInString(replace); if(replace[0] != '-' && replace.find("echo") != 0 && replace.find("$(MAKE)") != 0) { fout << "\t" << "echo " << replace.c_str() << "\n"; } fout << "\t" << replace.c_str() << "\n"; } if(command3) { replace = ShortPathCommand(command3); m_Makefile->ExpandVariablesInString(replace); if(replace[0] != '-' && replace.find("echo") != 0 && replace.find("$(MAKE)") != 0) { fout << "\t" << "echo " << replace.c_str() << "\n"; } fout << "\t" << replace.c_str() << "\n"; } if(command4) { replace = ShortPathCommand(command4); m_Makefile->ExpandVariablesInString(replace); if(replace[0] != '-' && replace.find("echo") != 0 && replace.find("$(MAKE)") != 0) { fout << "\t" << "echo " << replace.c_str() << "\n"; } fout << "\t" << replace.c_str() << "\n"; } fout << "\n"; } void cmNMakeMakefileGenerator:: OutputBuildObjectFromSource(std::ostream& fout, const char* shortName, const cmSourceFile& source, const char* extraCompileFlags, bool shared) { // Header files shouldn't have build rules. if(source.IsAHeaderFileOnly()) return; std::string comment = "Build "; std::string objectFile = std::string(shortName) + this->GetOutputExtension(source.GetSourceExtension().c_str()); comment += objectFile + " From "; comment += source.GetFullPath(); std::string compileCommand; std::string ext = source.GetSourceExtension(); if(ext == "c" ) { compileCommand = "$(CMAKE_C_COMPILER) $(CMAKE_C_FLAGS) "; compileCommand += extraCompileFlags; if(shared) { compileCommand += "$(CMAKE_SHLIB_CFLAGS) "; } compileCommand += "$(INCLUDE_FLAGS) -c "; compileCommand += this->ConvertToOutputPath(source.GetFullPath().c_str()); // Need to get the definition here because this value might have // trailing space (since it is directly prepended to the filename) std::string output_object_file_flag = m_Makefile->GetDefinition("CMAKE_C_OUTPUT_OBJECT_FILE_FLAG"); m_Makefile->ExpandVariablesInString(output_object_file_flag); compileCommand += " " + output_object_file_flag; compileCommand += objectFile; } else if (ext == "rc") { compileCommand = "$(RC) /fo\""; compileCommand += objectFile; compileCommand += "\" "; compileCommand += this->ConvertToOutputPath(source.GetFullPath().c_str()); } else if (ext == "def") { // no rule to output for this one return; } // assume c++ if not c rc or def else { compileCommand = "$(CMAKE_CXX_COMPILER) $(CMAKE_CXX_FLAGS) "; compileCommand += extraCompileFlags; if(shared) { compileCommand += "$(CMAKE_SHLIB_CFLAGS) "; } compileCommand += "$(INCLUDE_FLAGS) -c "; compileCommand += this->ConvertToOutputPath(source.GetFullPath().c_str()); // Need to get the definition here because this value might have // trailing space (since it is directly prepended to the filename) std::string output_object_file_flag = m_Makefile->GetDefinition("CMAKE_C_OUTPUT_OBJECT_FILE_FLAG"); m_Makefile->ExpandVariablesInString(output_object_file_flag); compileCommand += " " + output_object_file_flag; compileCommand += objectFile; } this->OutputMakeRule(fout, comment.c_str(), objectFile.c_str(), this->ConvertToOutputPath( source.GetFullPath().c_str()).c_str(), compileCommand.c_str()); } void cmNMakeMakefileGenerator::OutputSharedLibraryRule(std::ostream& fout, const char* name, const cmTarget &t) { std::string target = m_LibraryOutputPath + name + m_SharedLibraryExtension; std::string depend = "$("; depend += this->CreateMakeVariable(name, "_SRC_OBJS"); depend += ") $(" + this->CreateMakeVariable(name, "_DEPEND_LIBS") + ")"; // Need to get the definition here because this value might have // trailing space (since it is directly prepended to the filename) std::string linker_output_file_flag = m_Makefile->GetDefinition("CMAKE_LINKER_OUTPUT_FILE_FLAG"); m_Makefile->ExpandVariablesInString(linker_output_file_flag); std::string command = "$(CMAKE_LINKER) $(CMAKE_LINKER_SHARED_LIBRARY_FLAG)"; bool hide_param = m_Makefile->IsOn("CMAKE_LINKER_HIDE_PARAMETERS"); if (hide_param) { command += " @<<\n\t"; } command += " $(CMAKE_LINKER_FLAGS) " + linker_output_file_flag; std::string dllpath = m_LibraryOutputPath + std::string(name) + m_SharedLibraryExtension; command += this->ConvertToOutputPath(dllpath.c_str()); command += " $(" + this->CreateMakeVariable(name, "_SRC_OBJS") + ") "; std::strstream linklibs; this->OutputLinkLibraries(linklibs, name, t); linklibs << std::ends; command += linklibs.str(); delete [] linklibs.str(); const std::vector& sources = t.GetSourceFiles(); for(std::vector::const_iterator i = sources.begin(); i != sources.end(); ++i) { if(i->GetSourceExtension() == "def") { command += "/DEF:"; command += i->GetFullPath(); } } command += "\n"; if (hide_param) { command += "<<\n"; } std::string customCommands = this->CreateTargetRules(t, name); const char* cc = 0; if(customCommands.size() > 0) { cc = customCommands.c_str(); } this->OutputMakeRule(fout, "rules for a shared library", target.c_str(), depend.c_str(), command.c_str(), cc); } void cmNMakeMakefileGenerator::OutputModuleLibraryRule(std::ostream& fout, const char* name, const cmTarget &target) { this->OutputSharedLibraryRule(fout, name, target); } void cmNMakeMakefileGenerator::OutputStaticLibraryRule(std::ostream& fout, const char* name, const cmTarget &t) { std::string target = m_LibraryOutputPath + std::string(name) + m_StaticLibraryExtension; std::string depend = "$("; depend += this->CreateMakeVariable(name, "_SRC_OBJS") + ") "; // Need to get the definition here because this value might have // trailing space (since it is directly prepended to the filename) std::string library_manager_output_file_flag = m_Makefile->GetDefinition("CMAKE_LIBRARY_MANAGER_OUTPUT_FILE_FLAG"); m_Makefile->ExpandVariablesInString(library_manager_output_file_flag); std::string command = "$(CMAKE_LIBRARY_MANAGER) $(CMAKE_LIBRARY_MANAGER_FLAGS) @<<\n\t " + library_manager_output_file_flag; std::string libpath = m_LibraryOutputPath + std::string(name) + m_StaticLibraryExtension; command += this->ConvertToOutputPath(libpath.c_str()); command += " $("; command += this->CreateMakeVariable(name, "_SRC_OBJS") + ")"; command += "\n<<\n"; std::string comment = "rule to build static library: "; comment += name; std::string customCommands = this->CreateTargetRules(t, name); const char* cc = 0; if(customCommands.size() > 0) { cc = customCommands.c_str(); } this->OutputMakeRule(fout, comment.c_str(), target.c_str(), depend.c_str(), command.c_str(), cc); } void cmNMakeMakefileGenerator::OutputExecutableRule(std::ostream& fout, const char* name, const cmTarget &t) { std::string target = m_ExecutableOutputPath + name; target += m_ExecutableExtension; std::string depend = "$("; depend += this->CreateMakeVariable(name, "_SRC_OBJS") + ") $(" + this->CreateMakeVariable(name, "_DEPEND_LIBS") + ")"; std::string command = "$(CMAKE_CXX_COMPILER) $(CMAKE_CXX_FLAGS) "; command += "$(" + this->CreateMakeVariable(name, "_SRC_OBJS") + ") "; std::string path = m_ExecutableOutputPath + name + m_ExecutableExtension; // Need to get the definition here because this value might have // trailing space (since it is directly prepended to the filename) std::string output_executable_file_flag = m_Makefile->GetDefinition("CMAKE_C_OUTPUT_EXECUTABLE_FILE_FLAG"); m_Makefile->ExpandVariablesInString(output_executable_file_flag); command += " " + output_executable_file_flag + this->ConvertToOutputPath(path.c_str()); command += " $(CMAKE_C_LINK_EXECUTABLE_FLAG) "; if(t.GetType() == cmTarget::WIN32_EXECUTABLE) { command += " /subsystem:windows "; } std::strstream linklibs; this->OutputLinkLibraries(linklibs, 0, t); linklibs << std::ends; command += linklibs.str(); std::string comment = "rule to build executable: "; comment += name; std::string customCommands = this->CreateTargetRules(t, name); const char* cc = 0; if(customCommands.size() > 0) { cc = customCommands.c_str(); } this->OutputMakeRule(fout, comment.c_str(), target.c_str(), depend.c_str(), command.c_str(), cc); } void cmNMakeMakefileGenerator::OutputLinkLibraries(std::ostream& fout, const char* targetLibrary, const cmTarget &tgt) { // Try to emit each search path once std::set emitted; // Embed runtime search paths if possible and if required. // collect all the flags needed for linking libraries // Do not try if there is no library path option (it is set to -L or // -LIBPATH for some linker, but some others do not even support link // search path). std::string linkLibs; // Expand content because this value might have // trailing space (since it is directly prepended to the filename) std::string lib_path_opt = m_LibraryPathOption; m_Makefile->ExpandVariablesInString(lib_path_opt); if (lib_path_opt.size()) { std::vector& libdirs = m_Makefile->GetLinkDirectories(); for(std::vector::iterator libDir = libdirs.begin(); libDir != libdirs.end(); ++libDir) { std::string libpath = ShortPath(libDir->c_str()); if(emitted.insert(libpath).second) { linkLibs += lib_path_opt; this->ConvertToOutputPath(libpath.c_str()); linkLibs += libpath; linkLibs += " "; } } } std::string librariesLinked; const cmTarget::LinkLibraries& libs = tgt.GetLinkLibraries(); for(cmTarget::LinkLibraries::const_iterator lib = libs.begin(); lib != libs.end(); ++lib) { // Don't link the library against itself! if(targetLibrary && (lib->first == targetLibrary)) continue; // ** should fix this later, it should check to see if this is // a debug build and add the library // don't look at debug libraries // if (lib->second == cmTarget::DEBUG) continue; // skip zero size library entries, this may happen // if a variable expands to nothing. if (lib->first.size() == 0) continue; if(emitted.insert(lib->first).second) { std::string regexp = ".*\\"; regexp += m_Makefile->GetDefinition("CMAKE_STATICLIB_SUFFIX"); regexp += "$"; cmRegularExpression reg(regexp.c_str()); // if it ends in .lib, then it is a full path and should // be escaped, and does not need .lib added if(reg.find(lib->first)) { librariesLinked += ShortPath(lib->first.c_str()); librariesLinked += " "; } else { librariesLinked += m_LibraryLinkOption; librariesLinked += lib->first; librariesLinked += m_StaticLibraryExtension + " "; } } } linkLibs += librariesLinked; fout << linkLibs; fout << "$(CMAKE_STANDARD_WINDOWS_LIBRARIES) "; } std::string cmNMakeMakefileGenerator::GetOutputExtension(const char* s) { std::string sourceExtension = s; if(sourceExtension == "def") { return ""; } if(sourceExtension == "ico" || sourceExtension == "rc2") { return ""; } if(sourceExtension == "rc") { return ".res"; } return m_ObjectFileExtension; } void cmNMakeMakefileGenerator::OutputIncludeMakefile(std::ostream& fout, const char* file) { fout << "!include " << file << "\n"; } bool cmNMakeMakefileGenerator::SamePath(const char* path1, const char* path2) { // first check to see if they are the same anyway if (strcmp(path1, path2) == 0) { return true; } // next short path and lower case both of them for the compare return cmSystemTools::LowerCase(ShortPath(path1)) == cmSystemTools::LowerCase(ShortPath(path2)); } void cmNMakeMakefileGenerator::OutputBuildLibraryInDir(std::ostream& fout, const char* path, const char* , const char* fullpath) { std::string currentDir = this->ConvertToOutputPath(m_Makefile->GetCurrentOutputDirectory()); std::string wpath = this->ConvertToOutputPath(path); std::string wfullpath = this->ConvertToOutputPath(fullpath); fout << wfullpath << ":\n\tcd " << wpath << "\n" << "\t$(MAKE) -$(MAKEFLAGS) $(MAKESILENT) cmake.depends\n" << "\t$(MAKE) -$(MAKEFLAGS) $(MAKESILENT) cmake.check_depends\n" << "\t$(MAKE) -$(MAKEFLAGS) $(MAKESILENT) -f cmake.check_depends\n" << "\t$(MAKE) $(MAKESILENT) " << wfullpath << "\n\tcd " << currentDir << "\n"; } std::string cmNMakeMakefileGenerator::ConvertToOutputPath(const char* s) { return cmSystemTools::ConvertToOutputPath(s); } std::string cmNMakeMakefileGenerator::CreateMakeVariable(const char* s, const char* s2) { std::string ret= std::string(s) + std::string(s2); cmSystemTools::ReplaceString(ret, "-", "_"); return ret; }