packaging: Initial packaging
[platform/upstream/cmake.git] / Source / cmExportCommand.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 "cmExportCommand.h"
13 #include "cmGlobalGenerator.h"
14 #include "cmLocalGenerator.h"
15 #include "cmGeneratedFileStream.h"
16 #include "cmake.h"
17
18 #include <cmsys/RegularExpression.hxx>
19
20 #include "cmExportBuildFileGenerator.h"
21
22 #if defined(__HAIKU__)
23 #include <StorageKit.h>
24 #endif
25
26 cmExportCommand::cmExportCommand()
27 :cmCommand()
28 ,ArgumentGroup()
29 ,Targets(&Helper, "TARGETS")
30 ,Append(&Helper, "APPEND", &ArgumentGroup)
31 ,Namespace(&Helper, "NAMESPACE", &ArgumentGroup)
32 ,Filename(&Helper, "FILE", &ArgumentGroup)
33 ,ExportOld(&Helper, "EXPORT_LINK_INTERFACE_LIBRARIES", &ArgumentGroup)
34 {
35   // at first TARGETS
36   this->Targets.Follows(0);
37   // and after that the other options in any order
38   this->ArgumentGroup.Follows(&this->Targets);
39 }
40
41
42 // cmExportCommand
43 bool cmExportCommand
44 ::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
45 {
46   if(args.size() < 2 )
47     {
48     this->SetError("called with too few arguments");
49     return false;
50     }
51
52   if(args[0] == "PACKAGE")
53     {
54     return this->HandlePackage(args);
55     }
56
57   std::vector<std::string> unknownArgs;
58   this->Helper.Parse(&args, &unknownArgs);
59
60   if (!unknownArgs.empty())
61     {
62     this->SetError("Unknown arguments.");
63     return false;
64     }
65
66   if (this->Targets.WasFound() == false)
67     {
68     this->SetError("TARGETS option missing.");
69     return false;
70     }
71
72   if(!this->Filename.WasFound())
73     {
74     this->SetError("FILE <filename> option missing.");
75     return false;
76     }
77
78   // Make sure the file has a .cmake extension.
79   if(cmSystemTools::GetFilenameLastExtension(this->Filename.GetCString())
80      != ".cmake")
81     {
82     cmOStringStream e;
83     e << "FILE option given filename \"" << this->Filename.GetString()
84       << "\" which does not have an extension of \".cmake\".\n";
85     this->SetError(e.str().c_str());
86     return false;
87     }
88
89   // Get the file to write.
90   std::string fname = this->Filename.GetString();
91   if(cmSystemTools::FileIsFullPath(fname.c_str()))
92     {
93     if(!this->Makefile->CanIWriteThisFile(fname.c_str()))
94       {
95       cmOStringStream e;
96       e << "FILE option given filename \"" << fname
97         << "\" which is in the source tree.\n";
98       this->SetError(e.str().c_str());
99       return false;
100       }
101     }
102   else
103     {
104     // Interpret relative paths with respect to the current build dir.
105     fname = this->Makefile->GetCurrentOutputDirectory();
106     fname += "/";
107     fname += this->Filename.GetString();
108     }
109
110   // Collect the targets to be exported.
111   std::vector<cmTarget*> targets;
112   for(std::vector<std::string>::const_iterator
113       currentTarget = this->Targets.GetVector().begin();
114       currentTarget != this->Targets.GetVector().end();
115       ++currentTarget)
116     {
117     if (this->Makefile->IsAlias(currentTarget->c_str()))
118       {
119       cmOStringStream e;
120       e << "given ALIAS target \"" << *currentTarget
121         << "\" which may not be exported.";
122       this->SetError(e.str().c_str());
123       return false;
124       }
125
126     if(cmTarget* target =
127        this->Makefile->GetLocalGenerator()->
128        GetGlobalGenerator()->FindTarget(0, currentTarget->c_str()))
129       {
130       if((target->GetType() == cmTarget::EXECUTABLE) ||
131          (target->GetType() == cmTarget::STATIC_LIBRARY) ||
132          (target->GetType() == cmTarget::SHARED_LIBRARY) ||
133          (target->GetType() == cmTarget::MODULE_LIBRARY))
134         {
135         targets.push_back(target);
136         }
137       else if(target->GetType() == cmTarget::OBJECT_LIBRARY)
138         {
139         cmOStringStream e;
140         e << "given OBJECT library \"" << *currentTarget
141           << "\" which may not be exported.";
142         this->SetError(e.str().c_str());
143         return false;
144         }
145       else
146         {
147         cmOStringStream e;
148         e << "given target \"" << *currentTarget
149           << "\" which is not an executable or library.";
150         this->SetError(e.str().c_str());
151         return false;
152         }
153       }
154     else
155       {
156       cmOStringStream e;
157       e << "given target \"" << *currentTarget
158         << "\" which is not built by this project.";
159       this->SetError(e.str().c_str());
160       return false;
161       }
162     }
163
164   // Setup export file generation.
165   cmExportBuildFileGenerator ebfg;
166   ebfg.SetExportFile(fname.c_str());
167   ebfg.SetNamespace(this->Namespace.GetCString());
168   ebfg.SetAppendMode(this->Append.IsEnabled());
169   ebfg.SetExports(&targets);
170   ebfg.SetCommand(this);
171   ebfg.SetExportOld(this->ExportOld.IsEnabled());
172
173   // Compute the set of configurations exported.
174   std::vector<std::string> configurationTypes;
175   this->Makefile->GetConfigurations(configurationTypes);
176   if(!configurationTypes.empty())
177     {
178     for(std::vector<std::string>::const_iterator
179           ci = configurationTypes.begin();
180         ci != configurationTypes.end(); ++ci)
181       {
182       ebfg.AddConfiguration(ci->c_str());
183       }
184     }
185   else
186     {
187     ebfg.AddConfiguration("");
188     }
189
190   // Generate the import file.
191   if(!ebfg.GenerateImportFile() && this->ErrorMessage.empty())
192     {
193     this->SetError("could not write export file.");
194     return false;
195     }
196
197   // Report generated error message if any.
198   if(!this->ErrorMessage.empty())
199     {
200     this->SetError(this->ErrorMessage.c_str());
201     return false;
202     }
203
204   return true;
205 }
206
207 //----------------------------------------------------------------------------
208 bool cmExportCommand::HandlePackage(std::vector<std::string> const& args)
209 {
210   // Parse PACKAGE mode arguments.
211   enum Doing { DoingNone, DoingPackage };
212   Doing doing = DoingPackage;
213   std::string package;
214   for(unsigned int i=1; i < args.size(); ++i)
215     {
216     if(doing == DoingPackage)
217       {
218       package = args[i];
219       doing = DoingNone;
220       }
221     else
222       {
223       cmOStringStream e;
224       e << "PACKAGE given unknown argument: " << args[i];
225       this->SetError(e.str().c_str());
226       return false;
227       }
228     }
229
230   // Verify the package name.
231   if(package.empty())
232     {
233     this->SetError("PACKAGE must be given a package name.");
234     return false;
235     }
236   const char* packageExpr = "^[A-Za-z0-9_.-]+$";
237   cmsys::RegularExpression packageRegex(packageExpr);
238   if(!packageRegex.find(package.c_str()))
239     {
240     cmOStringStream e;
241     e << "PACKAGE given invalid package name \"" << package << "\".  "
242       << "Package names must match \"" << packageExpr << "\".";
243     this->SetError(e.str().c_str());
244     return false;
245     }
246
247   // We store the current build directory in the registry as a value
248   // named by a hash of its own content.  This is deterministic and is
249   // unique with high probability.
250   const char* outDir = this->Makefile->GetCurrentOutputDirectory();
251   std::string hash = cmSystemTools::ComputeStringMD5(outDir);
252 #if defined(_WIN32) && !defined(__CYGWIN__)
253   this->StorePackageRegistryWin(package, outDir, hash.c_str());
254 #else
255   this->StorePackageRegistryDir(package, outDir, hash.c_str());
256 #endif
257
258   return true;
259 }
260
261 #if defined(_WIN32) && !defined(__CYGWIN__)
262 # include <windows.h>
263 # undef GetCurrentDirectory
264 //----------------------------------------------------------------------------
265 void cmExportCommand::ReportRegistryError(std::string const& msg,
266                                           std::string const& key,
267                                           long err)
268 {
269   cmOStringStream e;
270   e << msg << "\n"
271     << "  HKEY_CURRENT_USER\\" << key << "\n";
272   char winmsg[1024];
273   if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
274                    FORMAT_MESSAGE_IGNORE_INSERTS, 0, err,
275                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
276                    winmsg, 1024, 0) > 0)
277     {
278     e << "Windows reported:\n"
279       << "  " << winmsg;
280     }
281   this->Makefile->IssueMessage(cmake::WARNING, e.str());
282 }
283
284 //----------------------------------------------------------------------------
285 void cmExportCommand::StorePackageRegistryWin(std::string const& package,
286                                               const char* content,
287                                               const char* hash)
288 {
289   std::string key = "Software\\Kitware\\CMake\\Packages\\";
290   key += package;
291   HKEY hKey;
292   LONG err = RegCreateKeyEx(HKEY_CURRENT_USER,
293                             key.c_str(), 0, 0, REG_OPTION_NON_VOLATILE,
294                             KEY_SET_VALUE, 0, &hKey, 0);
295   if(err != ERROR_SUCCESS)
296     {
297     this->ReportRegistryError(
298       "Cannot create/open registry key", key, err);
299     return;
300     }
301   err = RegSetValueEx(hKey, hash, 0, REG_SZ, (BYTE const*)content,
302                       static_cast<DWORD>(strlen(content)+1));
303   RegCloseKey(hKey);
304   if(err != ERROR_SUCCESS)
305     {
306     cmOStringStream msg;
307     msg << "Cannot set registry value \"" << hash << "\" under key";
308     this->ReportRegistryError(msg.str(), key, err);
309     return;
310     }
311 }
312 #else
313 //----------------------------------------------------------------------------
314 void cmExportCommand::StorePackageRegistryDir(std::string const& package,
315                                               const char* content,
316                                               const char* hash)
317 {
318 #if defined(__HAIKU__)
319   BPath dir;
320   if (find_directory(B_USER_SETTINGS_DIRECTORY, &dir) != B_OK)
321     {
322     return;
323     }
324   dir.Append("cmake/packages");
325   dir.Append(package.c_str());
326   std::string fname = dir.Path();
327 #else
328   const char* home = cmSystemTools::GetEnv("HOME");
329   if(!home)
330     {
331     return;
332     }
333   std::string fname = home;
334   cmSystemTools::ConvertToUnixSlashes(fname);
335   fname += "/.cmake/packages/";
336   fname += package;
337 #endif
338   cmSystemTools::MakeDirectory(fname.c_str());
339   fname += "/";
340   fname += hash;
341   if(!cmSystemTools::FileExists(fname.c_str()))
342     {
343     cmGeneratedFileStream entry(fname.c_str(), true);
344     if(entry)
345       {
346       entry << content << "\n";
347       }
348     else
349       {
350       cmOStringStream e;
351       e << "Cannot create package registry file:\n"
352         << "  " << fname << "\n"
353         << cmSystemTools::GetLastSystemError() << "\n";
354       this->Makefile->IssueMessage(cmake::WARNING, e.str());
355       }
356     }
357 }
358 #endif