Imported Upstream version 2.8.9
[platform/upstream/cmake.git] / Source / cmInstallTargetGenerator.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
8   This software is distributed WITHOUT ANY WARRANTY; without even the
9   implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10   See the License for more information.
11 ============================================================================*/
12 #include "cmInstallTargetGenerator.h"
13
14 #include "cmComputeLinkInformation.h"
15 #include "cmGlobalGenerator.h"
16 #include "cmLocalGenerator.h"
17 #include "cmMakefile.h"
18 #include "cmake.h"
19
20 #include <assert.h>
21
22 //----------------------------------------------------------------------------
23 cmInstallTargetGenerator
24 ::cmInstallTargetGenerator(cmTarget& t, const char* dest, bool implib,
25                            const char* file_permissions,
26                            std::vector<std::string> const& configurations,
27                            const char* component, bool optional):
28   cmInstallGenerator(dest, configurations, component), Target(&t),
29   ImportLibrary(implib), FilePermissions(file_permissions), Optional(optional)
30 {
31   this->ActionsPerConfig = true;
32   this->NamelinkMode = NamelinkModeNone;
33   this->Target->SetHaveInstallRule(true);
34 }
35
36 //----------------------------------------------------------------------------
37 cmInstallTargetGenerator
38 ::~cmInstallTargetGenerator()
39 {
40 }
41
42 //----------------------------------------------------------------------------
43 void cmInstallTargetGenerator::GenerateScript(std::ostream& os)
44 {
45   // Warn if installing an exclude-from-all target.
46   if(this->Target->GetPropertyAsBool("EXCLUDE_FROM_ALL"))
47     {
48     cmOStringStream msg;
49     msg << "WARNING: Target \"" << this->Target->GetName()
50         << "\" has EXCLUDE_FROM_ALL set and will not be built by default "
51         << "but an install rule has been provided for it.  CMake does "
52         << "not define behavior for this case.";
53     cmSystemTools::Message(msg.str().c_str(), "Warning");
54     }
55
56   // Perform the main install script generation.
57   this->cmInstallGenerator::GenerateScript(os);
58 }
59
60 //----------------------------------------------------------------------------
61 void cmInstallTargetGenerator::GenerateScriptForConfig(std::ostream& os,
62                                                        const char* config,
63                                                        Indent const& indent)
64 {
65   // Compute the build tree directory from which to copy the target.
66   std::string fromDirConfig;
67   if(this->Target->NeedRelinkBeforeInstall(config))
68     {
69     fromDirConfig = this->Target->GetMakefile()->GetStartOutputDirectory();
70     fromDirConfig += cmake::GetCMakeFilesDirectory();
71     fromDirConfig += "/CMakeRelink.dir/";
72     }
73   else
74     {
75     fromDirConfig = this->Target->GetDirectory(config, this->ImportLibrary);
76     fromDirConfig += "/";
77     }
78   std::string toDir = this->GetInstallDestination();
79   toDir += "/";
80
81   // Compute the list of files to install for this target.
82   std::vector<std::string> filesFrom;
83   std::vector<std::string> filesTo;
84   std::string literal_args;
85   cmTarget::TargetType targetType = this->Target->GetType();
86   cmInstallType type = cmInstallType();
87   switch(targetType)
88     {
89     case cmTarget::EXECUTABLE: type = cmInstallType_EXECUTABLE; break;
90     case cmTarget::STATIC_LIBRARY: type = cmInstallType_STATIC_LIBRARY; break;
91     case cmTarget::SHARED_LIBRARY: type = cmInstallType_SHARED_LIBRARY; break;
92     case cmTarget::MODULE_LIBRARY: type = cmInstallType_MODULE_LIBRARY; break;
93     case cmTarget::OBJECT_LIBRARY:
94     case cmTarget::UTILITY:
95     case cmTarget::GLOBAL_TARGET:
96     case cmTarget::UNKNOWN_LIBRARY:
97       this->Target->GetMakefile()->IssueMessage(cmake::INTERNAL_ERROR,
98         "cmInstallTargetGenerator created with non-installable target.");
99       return;
100     }
101   if(targetType == cmTarget::EXECUTABLE)
102     {
103     // There is a bug in cmInstallCommand if this fails.
104     assert(this->NamelinkMode == NamelinkModeNone);
105
106     std::string targetName;
107     std::string targetNameReal;
108     std::string targetNameImport;
109     std::string targetNamePDB;
110     this->Target->GetExecutableNames(targetName, targetNameReal,
111                                      targetNameImport, targetNamePDB,
112                                      config);
113     if(this->ImportLibrary)
114       {
115       std::string from1 = fromDirConfig + targetNameImport;
116       std::string to1 = toDir + targetNameImport;
117       filesFrom.push_back(from1);
118       filesTo.push_back(to1);
119       std::string targetNameImportLib;
120       if(this->Target->GetImplibGNUtoMS(targetNameImport,
121                                         targetNameImportLib))
122         {
123         filesFrom.push_back(fromDirConfig + targetNameImportLib);
124         filesTo.push_back(toDir + targetNameImportLib);
125         }
126
127       // An import library looks like a static library.
128       type = cmInstallType_STATIC_LIBRARY;
129       }
130     else
131       {
132       std::string from1 = fromDirConfig + targetName;
133       std::string to1 = toDir + targetName;
134
135       // Handle OSX Bundles.
136       if(this->Target->IsAppBundleOnApple())
137         {
138         // Install the whole app bundle directory.
139         type = cmInstallType_DIRECTORY;
140         literal_args += " USE_SOURCE_PERMISSIONS";
141         from1 += ".app";
142
143         // Tweaks apply to the binary inside the bundle.
144         to1 += ".app/Contents/MacOS/";
145         to1 += targetName;
146         }
147       else
148         {
149         // Tweaks apply to the real file, so list it first.
150         if(targetNameReal != targetName)
151           {
152           std::string from2 = fromDirConfig + targetNameReal;
153           std::string to2 = toDir += targetNameReal;
154           filesFrom.push_back(from2);
155           filesTo.push_back(to2);
156           }
157         }
158
159       filesFrom.push_back(from1);
160       filesTo.push_back(to1);
161       }
162     }
163   else
164     {
165     std::string targetName;
166     std::string targetNameSO;
167     std::string targetNameReal;
168     std::string targetNameImport;
169     std::string targetNamePDB;
170     this->Target->GetLibraryNames(targetName, targetNameSO, targetNameReal,
171                                   targetNameImport, targetNamePDB,
172                                   config);
173     if(this->ImportLibrary)
174       {
175       // There is a bug in cmInstallCommand if this fails.
176       assert(this->NamelinkMode == NamelinkModeNone);
177
178       std::string from1 = fromDirConfig + targetNameImport;
179       std::string to1 = toDir + targetNameImport;
180       filesFrom.push_back(from1);
181       filesTo.push_back(to1);
182       std::string targetNameImportLib;
183       if(this->Target->GetImplibGNUtoMS(targetNameImport,
184                                         targetNameImportLib))
185         {
186         filesFrom.push_back(fromDirConfig + targetNameImportLib);
187         filesTo.push_back(toDir + targetNameImportLib);
188         }
189
190       // An import library looks like a static library.
191       type = cmInstallType_STATIC_LIBRARY;
192       }
193     else if(this->Target->IsFrameworkOnApple())
194       {
195       // There is a bug in cmInstallCommand if this fails.
196       assert(this->NamelinkMode == NamelinkModeNone);
197
198       // Install the whole framework directory.
199       type = cmInstallType_DIRECTORY;
200       literal_args += " USE_SOURCE_PERMISSIONS";
201       std::string from1 = fromDirConfig + targetName + ".framework";
202
203       // Tweaks apply to the binary inside the bundle.
204       std::string to1 = toDir + targetName;
205       to1 += ".framework/Versions/";
206       to1 += this->Target->GetFrameworkVersion();
207       to1 += "/";
208       to1 += targetName;
209
210       filesFrom.push_back(from1);
211       filesTo.push_back(to1);
212       }
213     else
214       {
215       bool haveNamelink = false;
216
217       // Library link name.
218       std::string fromName = fromDirConfig + targetName;
219       std::string toName = toDir + targetName;
220
221       // Library interface name.
222       std::string fromSOName;
223       std::string toSOName;
224       if(targetNameSO != targetName)
225         {
226         haveNamelink = true;
227         fromSOName = fromDirConfig + targetNameSO;
228         toSOName = toDir + targetNameSO;
229         }
230
231       // Library implementation name.
232       std::string fromRealName;
233       std::string toRealName;
234       if(targetNameReal != targetName &&
235          targetNameReal != targetNameSO)
236         {
237         haveNamelink = true;
238         fromRealName = fromDirConfig + targetNameReal;
239         toRealName = toDir + targetNameReal;
240         }
241
242       // Add the names based on the current namelink mode.
243       if(haveNamelink)
244         {
245         // With a namelink we need to check the mode.
246         if(this->NamelinkMode == NamelinkModeOnly)
247           {
248           // Install the namelink only.
249           filesFrom.push_back(fromName);
250           filesTo.push_back(toName);
251           }
252         else
253           {
254           // Install the real file if it has its own name.
255           if(!fromRealName.empty())
256             {
257             filesFrom.push_back(fromRealName);
258             filesTo.push_back(toRealName);
259             }
260
261           // Install the soname link if it has its own name.
262           if(!fromSOName.empty())
263             {
264             filesFrom.push_back(fromSOName);
265             filesTo.push_back(toSOName);
266             }
267
268           // Install the namelink if it is not to be skipped.
269           if(this->NamelinkMode != NamelinkModeSkip)
270             {
271             filesFrom.push_back(fromName);
272             filesTo.push_back(toName);
273             }
274           }
275         }
276       else
277         {
278         // Without a namelink there will be only one file.  Install it
279         // if this is not a namelink-only rule.
280         if(this->NamelinkMode != NamelinkModeOnly)
281           {
282           filesFrom.push_back(fromName);
283           filesTo.push_back(toName);
284           }
285         }
286       }
287     }
288
289   // If this fails the above code is buggy.
290   assert(filesFrom.size() == filesTo.size());
291
292   // Skip this rule if no files are to be installed for the target.
293   if(filesFrom.empty())
294     {
295     return;
296     }
297
298   // Add pre-installation tweaks.
299   this->AddTweak(os, indent, config, filesTo,
300                  &cmInstallTargetGenerator::PreReplacementTweaks);
301
302   // Write code to install the target file.
303   const char* no_dir_permissions = 0;
304   const char* no_rename = 0;
305   bool optional = this->Optional || this->ImportLibrary;
306   this->AddInstallRule(os, type, filesFrom,
307                        optional,
308                        this->FilePermissions.c_str(), no_dir_permissions,
309                        no_rename, literal_args.c_str(),
310                        indent);
311
312   // Add post-installation tweaks.
313   this->AddTweak(os, indent, config, filesTo,
314                  &cmInstallTargetGenerator::PostReplacementTweaks);
315 }
316
317 //----------------------------------------------------------------------------
318 std::string
319 cmInstallTargetGenerator::GetInstallFilename(const char* config) const
320 {
321   NameType nameType = this->ImportLibrary? NameImplib : NameNormal;
322   return
323     cmInstallTargetGenerator::GetInstallFilename(this->Target, config,
324                                                  nameType);
325 }
326
327 //----------------------------------------------------------------------------
328 std::string cmInstallTargetGenerator::GetInstallFilename(cmTarget* target,
329                                                          const char* config,
330                                                          NameType nameType)
331 {
332   std::string fname;
333   // Compute the name of the library.
334   if(target->GetType() == cmTarget::EXECUTABLE)
335     {
336     std::string targetName;
337     std::string targetNameReal;
338     std::string targetNameImport;
339     std::string targetNamePDB;
340     target->GetExecutableNames(targetName, targetNameReal,
341                                targetNameImport, targetNamePDB,
342                                config);
343     if(nameType == NameImplib)
344       {
345       // Use the import library name.
346       if(!target->GetImplibGNUtoMS(targetNameImport, fname,
347                                    "${CMAKE_IMPORT_LIBRARY_SUFFIX}"))
348         {
349         fname = targetNameImport;
350         }
351       }
352     else if(nameType == NameReal)
353       {
354       // Use the canonical name.
355       fname = targetNameReal;
356       }
357     else
358       {
359       // Use the canonical name.
360       fname = targetName;
361       }
362     }
363   else
364     {
365     std::string targetName;
366     std::string targetNameSO;
367     std::string targetNameReal;
368     std::string targetNameImport;
369     std::string targetNamePDB;
370     target->GetLibraryNames(targetName, targetNameSO, targetNameReal,
371                             targetNameImport, targetNamePDB, config);
372     if(nameType == NameImplib)
373       {
374       // Use the import library name.
375       if(!target->GetImplibGNUtoMS(targetNameImport, fname,
376                                    "${CMAKE_IMPORT_LIBRARY_SUFFIX}"))
377         {
378         fname = targetNameImport;
379         }
380       }
381     else if(nameType == NameSO)
382       {
383       // Use the soname.
384       fname = targetNameSO;
385       }
386     else if(nameType == NameReal)
387       {
388       // Use the real name.
389       fname = targetNameReal;
390       }
391     else
392       {
393       // Use the canonical name.
394       fname = targetName;
395       }
396     }
397
398   return fname;
399 }
400
401 //----------------------------------------------------------------------------
402 void
403 cmInstallTargetGenerator
404 ::AddTweak(std::ostream& os, Indent const& indent, const char* config,
405            std::string const& file, TweakMethod tweak)
406 {
407   cmOStringStream tw;
408   (this->*tweak)(tw, indent.Next(), config, file);
409   std::string tws = tw.str();
410   if(!tws.empty())
411     {
412     os << indent << "IF(EXISTS \"" << file << "\" AND\n"
413        << indent << "   NOT IS_SYMLINK \"" << file << "\")\n";
414     os << tws;
415     os << indent << "ENDIF()\n";
416     }
417 }
418
419 //----------------------------------------------------------------------------
420 void
421 cmInstallTargetGenerator
422 ::AddTweak(std::ostream& os, Indent const& indent, const char* config,
423            std::vector<std::string> const& files, TweakMethod tweak)
424 {
425   if(files.size() == 1)
426     {
427     // Tweak a single file.
428     this->AddTweak(os, indent, config, this->GetDestDirPath(files[0]), tweak);
429     }
430   else
431     {
432     // Generate a foreach loop to tweak multiple files.
433     cmOStringStream tw;
434     this->AddTweak(tw, indent.Next(), config, "${file}", tweak);
435     std::string tws = tw.str();
436     if(!tws.empty())
437       {
438       Indent indent2 = indent.Next().Next();
439       os << indent << "FOREACH(file\n";
440       for(std::vector<std::string>::const_iterator i = files.begin();
441           i != files.end(); ++i)
442         {
443         os << indent2 << "\"" << this->GetDestDirPath(*i) << "\"\n";
444         }
445       os << indent2 << ")\n";
446       os << tws;
447       os << indent << "ENDFOREACH()\n";
448       }
449     }
450 }
451
452 //----------------------------------------------------------------------------
453 std::string cmInstallTargetGenerator::GetDestDirPath(std::string const& file)
454 {
455   // Construct the path of the file on disk after installation on
456   // which tweaks may be performed.
457   std::string toDestDirPath = "$ENV{DESTDIR}";
458   if(file[0] != '/' && file[0] != '$')
459     {
460     toDestDirPath += "/";
461     }
462   toDestDirPath += file;
463   return toDestDirPath;
464 }
465
466 //----------------------------------------------------------------------------
467 void cmInstallTargetGenerator::PreReplacementTweaks(std::ostream& os,
468                                                     Indent const& indent,
469                                                     const char* config,
470                                                     std::string const& file)
471 {
472   this->AddRPathCheckRule(os, indent, config, file);
473 }
474
475 //----------------------------------------------------------------------------
476 void cmInstallTargetGenerator::PostReplacementTweaks(std::ostream& os,
477                                                      Indent const& indent,
478                                                      const char* config,
479                                                      std::string const& file)
480 {
481   this->AddInstallNamePatchRule(os, indent, config, file);
482   this->AddChrpathPatchRule(os, indent, config, file);
483   this->AddRanlibRule(os, indent, file);
484   this->AddStripRule(os, indent, file);
485 }
486
487 //----------------------------------------------------------------------------
488 void
489 cmInstallTargetGenerator
490 ::AddInstallNamePatchRule(std::ostream& os, Indent const& indent,
491                           const char* config, std::string const& toDestDirPath)
492 {
493   if(this->ImportLibrary ||
494      !(this->Target->GetType() == cmTarget::SHARED_LIBRARY ||
495        this->Target->GetType() == cmTarget::MODULE_LIBRARY ||
496        this->Target->GetType() == cmTarget::EXECUTABLE))
497     {
498     return;
499     }
500
501   // Fix the install_name settings in installed binaries.
502   std::string installNameTool =
503     this->Target->GetMakefile()->GetSafeDefinition("CMAKE_INSTALL_NAME_TOOL");
504
505   if(!installNameTool.size())
506     {
507     return;
508     }
509
510   // Build a map of build-tree install_name to install-tree install_name for
511   // shared libraries linked to this target.
512   std::map<cmStdString, cmStdString> install_name_remap;
513   if(cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config))
514     {
515     std::set<cmTarget*> const& sharedLibs = cli->GetSharedLibrariesLinked();
516     for(std::set<cmTarget*>::const_iterator j = sharedLibs.begin();
517         j != sharedLibs.end(); ++j)
518       {
519       cmTarget* tgt = *j;
520
521       // The install_name of an imported target does not change.
522       if(tgt->IsImported())
523         {
524         continue;
525         }
526
527       // If the build tree and install tree use different path
528       // components of the install_name field then we need to create a
529       // mapping to be applied after installation.
530       std::string for_build = tgt->GetInstallNameDirForBuildTree(config);
531       std::string for_install = tgt->GetInstallNameDirForInstallTree(config);
532       if(for_build != for_install)
533         {
534         // The directory portions differ.  Append the filename to
535         // create the mapping.
536         std::string fname =
537           this->GetInstallFilename(tgt, config, NameSO);
538
539         // Map from the build-tree install_name.
540         for_build += fname;
541
542         // Map to the install-tree install_name.
543         for_install += fname;
544
545         // Store the mapping entry.
546         install_name_remap[for_build] = for_install;
547         }
548       }
549     }
550
551   // Edit the install_name of the target itself if necessary.
552   std::string new_id;
553   if(this->Target->GetType() == cmTarget::SHARED_LIBRARY)
554     {
555     std::string for_build =
556       this->Target->GetInstallNameDirForBuildTree(config);
557     std::string for_install =
558       this->Target->GetInstallNameDirForInstallTree(config);
559
560     if(this->Target->IsFrameworkOnApple() && for_install.empty())
561       {
562       // Frameworks seem to have an id corresponding to their own full
563       // path.
564       // ...
565       // for_install = fullDestPath_without_DESTDIR_or_name;
566       }
567
568     // If the install name will change on installation set the new id
569     // on the installed file.
570     if(for_build != for_install)
571       {
572       // Prepare to refer to the install-tree install_name.
573       new_id = for_install;
574       new_id += this->GetInstallFilename(this->Target, config, NameSO);
575       }
576     }
577
578   // Write a rule to run install_name_tool to set the install-tree
579   // install_name value and references.
580   if(!new_id.empty() || !install_name_remap.empty())
581     {
582     os << indent << "EXECUTE_PROCESS(COMMAND \"" << installNameTool;
583     os << "\"";
584     if(!new_id.empty())
585       {
586       os << "\n" << indent << "  -id \"" << new_id << "\"";
587       }
588     for(std::map<cmStdString, cmStdString>::const_iterator
589           i = install_name_remap.begin();
590         i != install_name_remap.end(); ++i)
591       {
592       os << "\n" << indent << "  -change \""
593          << i->first << "\" \"" << i->second << "\"";
594       }
595     os << "\n" << indent << "  \"" << toDestDirPath << "\")\n";
596     }
597 }
598
599 //----------------------------------------------------------------------------
600 void
601 cmInstallTargetGenerator
602 ::AddRPathCheckRule(std::ostream& os, Indent const& indent,
603                     const char* config, std::string const& toDestDirPath)
604 {
605   // Skip the chrpath if the target does not need it.
606   if(this->ImportLibrary || !this->Target->IsChrpathUsed(config))
607     {
608     return;
609     }
610
611   // Get the link information for this target.
612   // It can provide the RPATH.
613   cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config);
614   if(!cli)
615     {
616     return;
617     }
618
619   // Get the install RPATH from the link information.
620   std::string newRpath = cli->GetChrpathString();
621
622   // Write a rule to remove the installed file if its rpath is not the
623   // new rpath.  This is needed for existing build/install trees when
624   // the installed rpath changes but the file is not rebuilt.
625   os << indent << "FILE(RPATH_CHECK\n"
626      << indent << "     FILE \"" << toDestDirPath << "\"\n"
627      << indent << "     RPATH \"" << newRpath << "\")\n";
628 }
629
630 //----------------------------------------------------------------------------
631 void
632 cmInstallTargetGenerator
633 ::AddChrpathPatchRule(std::ostream& os, Indent const& indent,
634                       const char* config, std::string const& toDestDirPath)
635 {
636   // Skip the chrpath if the target does not need it.
637   if(this->ImportLibrary || !this->Target->IsChrpathUsed(config))
638     {
639     return;
640     }
641
642   // Get the link information for this target.
643   // It can provide the RPATH.
644   cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config);
645   if(!cli)
646     {
647     return;
648     }
649
650   // Construct the original rpath string to be replaced.
651   std::string oldRpath = cli->GetRPathString(false);
652
653   // Get the install RPATH from the link information.
654   std::string newRpath = cli->GetChrpathString();
655
656   // Skip the rule if the paths are identical
657   if(oldRpath == newRpath)
658     {
659     return;
660     }
661
662   // Write a rule to run chrpath to set the install-tree RPATH
663   if(newRpath.empty())
664     {
665     os << indent << "FILE(RPATH_REMOVE\n"
666        << indent << "     FILE \"" << toDestDirPath << "\")\n";
667     }
668   else
669     {
670     os << indent << "FILE(RPATH_CHANGE\n"
671        << indent << "     FILE \"" << toDestDirPath << "\"\n"
672        << indent << "     OLD_RPATH \"" << oldRpath << "\"\n"
673        << indent << "     NEW_RPATH \"" << newRpath << "\")\n";
674     }
675 }
676
677 //----------------------------------------------------------------------------
678 void
679 cmInstallTargetGenerator::AddStripRule(std::ostream& os,
680                                        Indent const& indent,
681                                        const std::string& toDestDirPath)
682 {
683
684   // don't strip static libraries, because it removes the only symbol table
685   // they have so you can't link to them anymore
686   if(this->Target->GetType() == cmTarget::STATIC_LIBRARY)
687     {
688     return;
689     }
690
691   // Don't handle OSX Bundles.
692   if(this->Target->GetMakefile()->IsOn("APPLE") &&
693      this->Target->GetPropertyAsBool("MACOSX_BUNDLE"))
694     {
695     return;
696     }
697
698   if(! this->Target->GetMakefile()->IsSet("CMAKE_STRIP"))
699     {
700     return;
701     }
702
703   os << indent << "IF(CMAKE_INSTALL_DO_STRIP)\n";
704   os << indent << "  EXECUTE_PROCESS(COMMAND \""
705      << this->Target->GetMakefile()->GetDefinition("CMAKE_STRIP")
706      << "\" \"" << toDestDirPath << "\")\n";
707   os << indent << "ENDIF(CMAKE_INSTALL_DO_STRIP)\n";
708 }
709
710 //----------------------------------------------------------------------------
711 void
712 cmInstallTargetGenerator::AddRanlibRule(std::ostream& os,
713                                         Indent const& indent,
714                                         const std::string& toDestDirPath)
715 {
716   // Static libraries need ranlib on this platform.
717   if(this->Target->GetType() != cmTarget::STATIC_LIBRARY)
718     {
719     return;
720     }
721
722   // Perform post-installation processing on the file depending
723   // on its type.
724   if(!this->Target->GetMakefile()->IsOn("APPLE"))
725     {
726     return;
727     }
728
729   std::string ranlib =
730     this->Target->GetMakefile()->GetRequiredDefinition("CMAKE_RANLIB");
731   if(ranlib.empty())
732     {
733     return;
734     }
735
736   os << indent << "EXECUTE_PROCESS(COMMAND \""
737      << ranlib << "\" \"" << toDestDirPath << "\")\n";
738 }