diff --git a/Modules/Platform/Linux-GNU-Fortran.cmake b/Modules/Platform/Linux-GNU-Fortran.cmake new file mode 100644 index 000000000..ba13a4381 --- /dev/null +++ b/Modules/Platform/Linux-GNU-Fortran.cmake @@ -0,0 +1 @@ +SET(CMAKE_Fortran_MODDIR_FLAG -J) diff --git a/Modules/Platform/Linux-SunPro-Fortran.cmake b/Modules/Platform/Linux-SunPro-Fortran.cmake index 16ac62c9f..699483926 100644 --- a/Modules/Platform/Linux-SunPro-Fortran.cmake +++ b/Modules/Platform/Linux-SunPro-Fortran.cmake @@ -12,3 +12,5 @@ SET(CMAKE_Fortran_FLAGS_DEBUG_INIT "-g") SET(CMAKE_Fortran_FLAGS_MINSIZEREL_INIT "-xO2 -xspace -DNDEBUG") SET(CMAKE_Fortran_FLAGS_RELEASE_INIT "-xO3 -DNDEBUG") SET(CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT "-g -xO2") +SET(CMAKE_Fortran_MODDIR_FLAG "-moddir=") +SET(CMAKE_Fortran_MODPATH_FLAG "-M") diff --git a/Modules/Platform/Linux-ifort.cmake b/Modules/Platform/Linux-ifort.cmake index 04dcd6242..dc46dd51c 100644 --- a/Modules/Platform/Linux-ifort.cmake +++ b/Modules/Platform/Linux-ifort.cmake @@ -5,3 +5,4 @@ SET(CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS "-i_dynamic") SET(CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG "-Wl,-rpath,") SET(CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG_SEP ":") SET(CMAKE_SHARED_LIBRARY_SONAME_Fortran_FLAG "-Wl,-soname,") +SET(CMAKE_Fortran_MODDIR_FLAG "-module ") diff --git a/Source/cmDependsFortran.cxx b/Source/cmDependsFortran.cxx index 30b2d0cb2..aeb37e75a 100644 --- a/Source/cmDependsFortran.cxx +++ b/Source/cmDependsFortran.cxx @@ -194,6 +194,23 @@ bool cmDependsFortran::Finalize(std::ostream& makeDepends, // Prepare the module search process. this->LocateModules(); + // Get the directory in which stamp files will be stored. + const char* stamp_dir = + this->LocalGenerator->GetMakefile()->GetCurrentOutputDirectory(); + + // Get the directory in which module files will be created. + const char* mod_dir; + cmMakefile* mf = this->LocalGenerator->GetMakefile(); + if(const char* target_mod_dir = + mf->GetDefinition("CMAKE_Fortran_TARGET_MODULE_DIR")) + { + mod_dir = target_mod_dir; + } + else + { + mod_dir = stamp_dir; + } + // Actually write dependencies to the streams. typedef cmDependsFortranInternals::ObjectInfoMap ObjectInfoMap; ObjectInfoMap const& objInfo = this->Internal->ObjectInfo; @@ -201,6 +218,7 @@ bool cmDependsFortran::Finalize(std::ostream& makeDepends, i != objInfo.end(); ++i) { if(!this->WriteDependenciesReal(i->first.c_str(), i->second, + mod_dir, stamp_dir, makeDepends, internalDepends)) { return false; @@ -227,16 +245,35 @@ bool cmDependsFortran::Finalize(std::ostream& makeDepends, fcName += "/cmake_clean_Fortran.cmake"; cmGeneratedFileStream fcStream(fcName.c_str()); fcStream << "# Remove fortran modules provided by this target.\n"; - fcStream << "FILE(REMOVE\n"; + fcStream << "FILE(REMOVE"; for(std::set::const_iterator i = provides.begin(); i != provides.end(); ++i) { - std::string mod_upper = cmSystemTools::UpperCase(*i); - std::string mod_lower = *i; - fcStream << " \"" << mod_lower << ".mod\"" - << " \"" << mod_lower << ".mod.stamp\"" - << " \"" << mod_upper << ".mod\"" - << " \"" << mod_upper << ".mod.stamp\"\n"; + std::string mod_upper = mod_dir; + mod_upper += "/"; + mod_upper += cmSystemTools::UpperCase(*i); + mod_upper += ".mod"; + std::string mod_lower = mod_dir; + mod_lower += "/"; + mod_lower += *i; + mod_lower += ".mod"; + std::string stamp = stamp_dir; + stamp += "/"; + stamp += *i; + stamp += ".mod.stamp"; + fcStream << "\n"; + fcStream << " \"" << + this->LocalGenerator->Convert(mod_lower.c_str(), + cmLocalGenerator::START_OUTPUT) + << "\"\n"; + fcStream << " \"" << + this->LocalGenerator->Convert(mod_upper.c_str(), + cmLocalGenerator::START_OUTPUT) + << "\"\n"; + fcStream << " \"" << + this->LocalGenerator->Convert(stamp.c_str(), + cmLocalGenerator::START_OUTPUT) + << "\"\n"; } fcStream << " )\n"; } @@ -304,19 +341,19 @@ void cmDependsFortran::LocateModules() //---------------------------------------------------------------------------- void cmDependsFortran::MatchLocalModules() { - const char* moduleDir = + const char* stampDir = this->LocalGenerator->GetMakefile()->GetCurrentOutputDirectory(); std::set const& provides = this->Internal->TargetProvides; for(std::set::const_iterator i = provides.begin(); i != provides.end(); ++i) { - this->ConsiderModule(i->c_str(), moduleDir); + this->ConsiderModule(i->c_str(), stampDir); } } //---------------------------------------------------------------------------- void cmDependsFortran::MatchRemoteModules(std::istream& fin, - const char* moduleDir) + const char* stampDir) { std::string line; bool doing_provides = false; @@ -332,7 +369,7 @@ void cmDependsFortran::MatchRemoteModules(std::istream& fin, { if(doing_provides) { - this->ConsiderModule(line.c_str()+1, moduleDir); + this->ConsiderModule(line.c_str()+1, stampDir); } } else if(line == "provides") @@ -348,7 +385,7 @@ void cmDependsFortran::MatchRemoteModules(std::istream& fin, //---------------------------------------------------------------------------- void cmDependsFortran::ConsiderModule(const char* name, - const char* moduleDir) + const char* stampDir) { // Locate each required module. typedef cmDependsFortranInternals::TargetRequiresMap TargetRequiresMap; @@ -358,7 +395,7 @@ void cmDependsFortran::ConsiderModule(const char* name, required->second.empty()) { // The module is provided by a CMake target. It will have a stamp file. - std::string stampFile = moduleDir; + std::string stampFile = stampDir; stampFile += "/"; stampFile += name; stampFile += ".mod.stamp"; @@ -371,6 +408,7 @@ bool cmDependsFortran ::WriteDependenciesReal(const char *obj, cmDependsFortranSourceInfo const& info, + const char* mod_dir, const char* stamp_dir, std::ostream& makeDepends, std::ostream& internalDepends) { @@ -379,10 +417,6 @@ cmDependsFortran // Get the source file for this object. const char* src = info.Source.c_str(); - // Get the directory in which stamp files will be stored. - std::string mod_dir = - this->LocalGenerator->GetMakefile()->GetCurrentOutputDirectory(); - // Write the include dependencies to the output stream. internalDepends << obj << std::endl; internalDepends << " " << src << std::endl; @@ -414,7 +448,7 @@ cmDependsFortran // The module is provided by a different source in the same // target. Add the proxy dependency to make sure the other // source builds first. - std::string proxy = mod_dir; + std::string proxy = stamp_dir; proxy += "/"; proxy += *i; proxy += ".mod.proxy"; @@ -460,7 +494,7 @@ cmDependsFortran for(std::set::const_iterator i = info.Provides.begin(); i != info.Provides.end(); ++i) { - std::string proxy = mod_dir; + std::string proxy = stamp_dir; proxy += "/"; proxy += *i; proxy += ".mod.proxy"; @@ -493,7 +527,7 @@ cmDependsFortran this->LocalGenerator->Convert(modFile.c_str(), cmLocalGenerator::HOME_OUTPUT, cmLocalGenerator::SHELL); - std::string stampFile = mod_dir; + std::string stampFile = stamp_dir; stampFile += "/"; stampFile += m; stampFile += ".mod.stamp"; diff --git a/Source/cmDependsFortran.h b/Source/cmDependsFortran.h index c98bad34d..e8c4a7899 100644 --- a/Source/cmDependsFortran.h +++ b/Source/cmDependsFortran.h @@ -60,8 +60,8 @@ protected: // Find all the modules required by the target. void LocateModules(); void MatchLocalModules(); - void MatchRemoteModules(std::istream& fin, const char* moduleDir); - void ConsiderModule(const char* name, const char* moduleDir); + void MatchRemoteModules(std::istream& fin, const char* stampDir); + void ConsiderModule(const char* name, const char* stampDir); bool FindModule(std::string const& name, std::string& module); // Implement writing/checking methods required by superclass. @@ -72,6 +72,7 @@ protected: // Actually write the depenencies to the streams. bool WriteDependenciesReal(const char *obj, cmDependsFortranSourceInfo const& info, + const char* mod_dir, const char* stamp_dir, std::ostream& makeDepends, std::ostream& internalDepends); diff --git a/Source/cmDocumentVariables.cxx b/Source/cmDocumentVariables.cxx index b19bf1c0d..99628f717 100644 --- a/Source/cmDocumentVariables.cxx +++ b/Source/cmDocumentVariables.cxx @@ -679,6 +679,15 @@ void cmDocumentVariables::DefineVariables(cmake* cm) false, "Variables that Control the Build"); + cm->DefineProperty + ("CMAKE_Fortran_MODULE_DIRECTORY", cmProperty::VARIABLE, + "Fortran module output directory.", + "This variable is used to initialize the " + "Fortran_MODULE_DIRECTORY property on all the targets. " + "See that target property for additional information.", + false, + "Variables that Control the Build"); + cm->DefineProperty ("CMAKE_LIBRARY_OUTPUT_DIRECTORY", cmProperty::VARIABLE, "Where to put all the LIBRARY targets when built.", diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 13c1b6427..0e9ccc547 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -36,6 +36,7 @@ cmMakefileTargetGenerator::cmMakefileTargetGenerator() this->InfoFileStream = 0; this->FlagFileStream = 0; this->CustomCommandDriver = OnBuild; + this->FortranModuleDirectoryComputed = false; } cmMakefileTargetGenerator * @@ -268,6 +269,12 @@ void cmMakefileTargetGenerator::WriteTargetLanguageFlags() ->AddLanguageFlags(flags, lang, this->LocalGenerator->ConfigurationName.c_str()); + // Fortran-specific flags computed for this target. + if(*l == "Fortran") + { + this->AddFortranFlags(flags); + } + // Add shared-library flags if needed. this->LocalGenerator->AddSharedFlags(flags, lang, shared); @@ -823,6 +830,15 @@ void cmMakefileTargetGenerator::WriteTargetDependRules() << " )\n"; } + // Check for a target-specific module output directory. + if(const char* mdir = this->GetFortranModuleDirectory()) + { + *this->InfoFileStream + << "\n" + << "# Fortran module output directory.\n" + << "SET(CMAKE_Fortran_TARGET_MODULE_DIR \"" << mdir << "\")\n"; + } + // and now write the rule to use it std::vector depends; std::vector commands; @@ -1423,3 +1439,82 @@ cmMakefileTargetGenerator link_command += " --verbose=$(VERBOSE)"; makefile_commands.push_back(link_command); } + +//---------------------------------------------------------------------------- +const char* cmMakefileTargetGenerator::GetFortranModuleDirectory() +{ + // Compute the module directory. + if(!this->FortranModuleDirectoryComputed) + { + const char* target_mod_dir = + this->Target->GetProperty("Fortran_MODULE_DIRECTORY"); + const char* moddir_flag = + this->Makefile->GetDefinition("CMAKE_Fortran_MODDIR_FLAG"); + if(target_mod_dir && moddir_flag) + { + // Compute the full path to the module directory. + if(cmSystemTools::FileIsFullPath(target_mod_dir)) + { + // Already a full path. + this->FortranModuleDirectory = target_mod_dir; + } + else + { + // Interpret relative to the current output directory. + this->FortranModuleDirectory = + this->Makefile->GetCurrentOutputDirectory(); + this->FortranModuleDirectory += "/"; + this->FortranModuleDirectory += target_mod_dir; + } + + // Make sure the module output directory exists. + cmSystemTools::MakeDirectory(this->FortranModuleDirectory.c_str()); + } + this->FortranModuleDirectoryComputed = true; + } + + // Return the computed directory. + if(this->FortranModuleDirectory.empty()) + { + return 0; + } + else + { + return this->FortranModuleDirectory.c_str(); + } +} + +//---------------------------------------------------------------------------- +void cmMakefileTargetGenerator::AddFortranFlags(std::string& flags) +{ + // Add a module output directory flag if necessary. + if(const char* mod_dir = this->GetFortranModuleDirectory()) + { + const char* moddir_flag = + this->Makefile->GetRequiredDefinition("CMAKE_Fortran_MODDIR_FLAG"); + std::string modflag = moddir_flag; + modflag += this->Convert(mod_dir, + cmLocalGenerator::START_OUTPUT, + cmLocalGenerator::SHELL); + this->LocalGenerator->AppendFlags(flags, modflag.c_str()); + } + + // If there is a separate module path flag then duplicate the + // include path with it. This compiler does not search the include + // path for modules. + if(const char* modpath_flag = + this->Makefile->GetDefinition("CMAKE_Fortran_MODPATH_FLAG")) + { + std::vector includes; + this->LocalGenerator->GetIncludeDirectories(includes); + for(std::vector::const_iterator idi = includes.begin(); + idi != includes.end(); ++idi) + { + std::string flg = modpath_flag; + flg += this->Convert(idi->c_str(), + cmLocalGenerator::NONE, + cmLocalGenerator::SHELL); + this->LocalGenerator->AppendFlags(flags, flg.c_str()); + } + } +} diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h index aa452c94b..e997a0a41 100644 --- a/Source/cmMakefileTargetGenerator.h +++ b/Source/cmMakefileTargetGenerator.h @@ -184,6 +184,14 @@ protected: typedef std::map MultipleOutputPairsType; MultipleOutputPairsType MultipleOutputPairs; + // Target-wide Fortran module output directory. + bool FortranModuleDirectoryComputed; + std::string FortranModuleDirectory; + const char* GetFortranModuleDirectory(); + + // Compute target-specific Fortran language flags. + void AddFortranFlags(std::string& flags); + //================================================================== // Convenience routines that do nothing more than forward to // implementaitons diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index daca41f15..747f16955 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -284,6 +284,15 @@ void cmTarget::DefineProperties(cmake *cm) "exported symbols and then used for linking. " "All Windows-based systems including Cygwin are DLL platforms."); + cm->DefineProperty + ("Fortran_MODULE_DIRECTORY", cmProperty::TARGET, + "Specify output directory for Fortran modules provided by the target.", + "If the target contains Fortran source files that provide modules " + "and the compiler supports a module output directory this specifies " + "the directory in which the modules will be placed. " + "When this property is not set the modules will be placed in the " + "build directory corresponding to the target's source directory."); + cm->DefineProperty ("XCODE_ATTRIBUTE_", cmProperty::TARGET, "Set Xcode target attributes directly.", @@ -403,6 +412,7 @@ void cmTarget::SetMakefile(cmMakefile* mf) this->SetPropertyDefault("ARCHIVE_OUTPUT_DIRECTORY", 0); this->SetPropertyDefault("LIBRARY_OUTPUT_DIRECTORY", 0); this->SetPropertyDefault("RUNTIME_OUTPUT_DIRECTORY", 0); + this->SetPropertyDefault("Fortran_MODULE_DIRECTORY", 0); // Collect the set of configuration types. std::vector configNames; diff --git a/Tests/Fortran/CMakeLists.txt b/Tests/Fortran/CMakeLists.txt index de1c82464..3a299b2a9 100644 --- a/Tests/Fortran/CMakeLists.txt +++ b/Tests/Fortran/CMakeLists.txt @@ -23,9 +23,7 @@ IF(CMAKE_Fortran_COMPILER_SUPPORTS_F90) in_interface/main.f90 in_interface/module.f90) - IF(CMAKE_Fortran_COMPILER_ID MATCHES GNU) - SET(TEST_MODULE_DEPENDS 1) - ENDIF(CMAKE_Fortran_COMPILER_ID MATCHES GNU) + SET(TEST_MODULE_DEPENDS 1) ENDIF(CMAKE_Fortran_COMPILER_SUPPORTS_F90) IF(TEST_MODULE_DEPENDS) @@ -57,6 +55,13 @@ IF(TEST_MODULE_DEPENDS) ) ADD_CUSTOM_TARGET(ExternalTarget ALL DEPENDS ${testf_BINARY_DIR}/ExternalProject) + # Test module output directory if available. + IF(CMAKE_Fortran_MODDIR_FLAG) + SET(Library_MODDIR "${testf_BINARY_DIR}/Library/modules") + ELSE(CMAKE_Fortran_MODDIR_FLAG) + SET(Library_MODDIR "${testf_BINARY_DIR}/Library") + ENDIF(CMAKE_Fortran_MODDIR_FLAG) + ADD_SUBDIRECTORY(Library) ADD_SUBDIRECTORY(Executable) ENDIF(TEST_MODULE_DEPENDS) diff --git a/Tests/Fortran/Executable/CMakeLists.txt b/Tests/Fortran/Executable/CMakeLists.txt index 7596ff1fc..40114e4c5 100644 --- a/Tests/Fortran/Executable/CMakeLists.txt +++ b/Tests/Fortran/Executable/CMakeLists.txt @@ -1,4 +1,4 @@ -include_directories(${testf_BINARY_DIR}/Library) +include_directories(${Library_MODDIR}) include_directories(${testf_BINARY_DIR}/External) link_directories(${testf_BINARY_DIR}/External) diff --git a/Tests/Fortran/Library/CMakeLists.txt b/Tests/Fortran/Library/CMakeLists.txt index 8d6290004..fe1368a07 100644 --- a/Tests/Fortran/Library/CMakeLists.txt +++ b/Tests/Fortran/Library/CMakeLists.txt @@ -1,3 +1,11 @@ +INCLUDE_DIRECTORIES(${Library_MODDIR}) ADD_LIBRARY(subdir_mods a.f90 b.f90) ADD_EXECUTABLE(subdir_exe main.f90) TARGET_LINK_LIBRARIES(subdir_exe subdir_mods) + +# Test module output directory if available. +IF(CMAKE_Fortran_MODDIR_FLAG) + SET_TARGET_PROPERTIES(subdir_mods PROPERTIES + Fortran_MODULE_DIRECTORY modules + ) +ENDIF(CMAKE_Fortran_MODDIR_FLAG)