Browse Source

BUG: Fix Xcode linker language

Xcode does not seem to support direct requests for using the linker for
a particular language.  It always infers the linker using the languages
in the source files.  When no user source files compile with target's
linker language we add one to help Xcode pick the linker.

A typical use case is when a C executable links to a C++ archive.  The
executable has no C++ source files but we need to use the C++ linker.
Brad King 16 years ago
parent
commit
a232dbe8d2
3 changed files with 62 additions and 3 deletions
  1. 3 3
      Source/cmGlobalGenerator.h
  2. 57 0
      Source/cmGlobalXCodeGenerator.cxx
  3. 2 0
      Source/cmGlobalXCodeGenerator.h

+ 3 - 3
Source/cmGlobalGenerator.h

@@ -317,6 +317,9 @@ protected:
   // This is computed just before local generators generate.
   cmTargetManifest TargetManifest;
 
+  // All targets in the entire project.
+  std::map<cmStdString,cmTarget *> TotalTargets;
+
 private:
   float FirstTimeProgress;
   // If you add a new map here, make sure it is copied
@@ -328,9 +331,6 @@ private:
   std::map<cmStdString, cmStdString> ExtensionToLanguage;
   std::map<cmStdString, int> LanguageToLinkerPreference; 
 
-  // this is used to improve performance
-  std::map<cmStdString,cmTarget *> TotalTargets;
-
   // Record hashes for rules and outputs.
   struct RuleHash { char Data[32]; };
   std::map<cmStdString, RuleHash> RuleHashes;

+ 57 - 0
Source/cmGlobalXCodeGenerator.cxx

@@ -239,6 +239,7 @@ void cmGlobalXCodeGenerator::Generate()
     // add ALL_BUILD, INSTALL, etc
     this->AddExtraTargets(root, it->second);
     }
+  this->ForceLinkerLanguages();
   this->cmGlobalGenerator::Generate();
   for(it = this->ProjectMap.begin(); it!= this->ProjectMap.end(); ++it)
     { 
@@ -968,6 +969,62 @@ cmGlobalXCodeGenerator::CreateXCodeTargets(cmLocalGenerator* gen,
     }
 }
 
+//----------------------------------------------------------------------------
+void cmGlobalXCodeGenerator::ForceLinkerLanguages()
+{
+  // This makes sure all targets link using the proper language.
+  for(std::map<cmStdString, cmTarget*>::const_iterator
+        ti = this->TotalTargets.begin(); ti != this->TotalTargets.end(); ++ti)
+    {
+    this->ForceLinkerLanguage(*ti->second);
+    }
+}
+
+//----------------------------------------------------------------------------
+void cmGlobalXCodeGenerator::ForceLinkerLanguage(cmTarget& cmtarget)
+{
+  // This matters only for targets that link.
+  if(cmtarget.GetType() != cmTarget::EXECUTABLE &&
+     cmtarget.GetType() != cmTarget::SHARED_LIBRARY &&
+     cmtarget.GetType() != cmTarget::MODULE_LIBRARY)
+    {
+    return;
+    }
+
+  const char* llang = cmtarget.GetLinkerLanguage("NOCONFIG");
+  if(!llang) { return; }
+
+  // If the language is compiled as a source trust Xcode to link with it.
+  cmTarget::LinkImplementation const* impl =
+    cmtarget.GetLinkImplementation("NOCONFIG");
+  for(std::vector<std::string>::const_iterator li = impl->Languages.begin();
+      li != impl->Languages.end(); ++li)
+    {
+    if(*li == llang) { return; }
+    }
+
+  // Add an empty source file to the target that compiles with the
+  // linker language.  This should convince Xcode to choose the proper
+  // language.
+  cmMakefile* mf = cmtarget.GetMakefile();
+  std::string fname = mf->GetCurrentOutputDirectory();
+  fname += cmake::GetCMakeFilesDirectory();
+  fname += "/";
+  fname += cmtarget.GetName();
+  fname += "-CMakeForceLinker";
+  fname += ".";
+  fname += cmSystemTools::LowerCase(llang);
+  {
+  cmGeneratedFileStream fout(fname.c_str());
+  fout << "\n";
+  }
+  if(cmSourceFile* sf = mf->GetOrCreateSource(fname.c_str()))
+    {
+    sf->SetProperty("LANGUAGE", llang);
+    cmtarget.AddSourceFile(sf);
+    }
+}
+
 //----------------------------------------------------------------------------
 bool cmGlobalXCodeGenerator::IsHeaderFile(cmSourceFile* sf)
 {

+ 2 - 0
Source/cmGlobalXCodeGenerator.h

@@ -128,6 +128,8 @@ private:
   cmXCodeObject* CreateObjectReference(cmXCodeObject*);
   cmXCodeObject* CreateXCodeTarget(cmTarget& target,
                                    cmXCodeObject* buildPhases);
+  void ForceLinkerLanguages();
+  void ForceLinkerLanguage(cmTarget& cmtarget);
   const char* GetTargetFileType(cmTarget& cmtarget);
   const char* GetTargetProductType(cmTarget& cmtarget);
   std::string AddConfigurations(cmXCodeObject* target, cmTarget& cmtarget);