cmOutputConverter.cxx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "cmOutputConverter.h"
  11. #include "cmAlgorithms.h"
  12. #include "cmake.h"
  13. #include <cmsys/System.h>
  14. #include <assert.h>
  15. cmOutputConverter::cmOutputConverter(cmState::Snapshot snapshot)
  16. : StateSnapshot(snapshot), LinkScriptShell(false)
  17. {
  18. }
  19. //----------------------------------------------------------------------------
  20. std::string
  21. cmOutputConverter::ConvertToOutputForExistingCommon(const std::string& remote,
  22. std::string const& result,
  23. OutputFormat format)
  24. {
  25. // If this is a windows shell, the result has a space, and the path
  26. // already exists, we can use a short-path to reference it without a
  27. // space.
  28. if(this->GetState()->UseWindowsShell() && result.find(' ') != result.npos &&
  29. cmSystemTools::FileExists(remote.c_str()))
  30. {
  31. std::string tmp;
  32. if(cmSystemTools::GetShortPath(remote, tmp))
  33. {
  34. return this->ConvertToOutputFormat(tmp, format);
  35. }
  36. }
  37. // Otherwise, leave it unchanged.
  38. return result;
  39. }
  40. //----------------------------------------------------------------------------
  41. std::string
  42. cmOutputConverter::ConvertToOutputForExisting(const std::string& remote,
  43. RelativeRoot local,
  44. OutputFormat format)
  45. {
  46. static_cast<void>(local);
  47. // Perform standard conversion.
  48. std::string result = this->ConvertToOutputFormat(remote, format);
  49. // Consider short-path.
  50. return this->ConvertToOutputForExistingCommon(remote, result, format);
  51. }
  52. //----------------------------------------------------------------------------
  53. std::string
  54. cmOutputConverter::ConvertToOutputForExisting(RelativeRoot remote,
  55. const std::string& local,
  56. OutputFormat format)
  57. {
  58. // Perform standard conversion.
  59. std::string result = this->Convert(remote, local, format, true);
  60. // Consider short-path.
  61. const char* remotePath = this->GetRelativeRootPath(remote);
  62. return this->ConvertToOutputForExistingCommon(remotePath, result, format);
  63. }
  64. //----------------------------------------------------------------------------
  65. const char* cmOutputConverter::GetRelativeRootPath(RelativeRoot relroot)
  66. {
  67. switch (relroot)
  68. {
  69. case HOME: return this->GetState()->GetSourceDirectory();
  70. case START: return this->StateSnapshot.GetCurrentSourceDirectory();
  71. case HOME_OUTPUT: return this->GetState()->GetBinaryDirectory();
  72. case START_OUTPUT: return this->StateSnapshot.GetCurrentBinaryDirectory();
  73. default: break;
  74. }
  75. return 0;
  76. }
  77. std::string cmOutputConverter::Convert(const std::string& source,
  78. RelativeRoot relative,
  79. OutputFormat output)
  80. {
  81. // Convert the path to a relative path.
  82. std::string result = source;
  83. switch (relative)
  84. {
  85. case HOME:
  86. result = this->ConvertToRelativePath(
  87. this->GetState()->GetSourceDirectoryComponents(), result);
  88. break;
  89. case START:
  90. result = this->ConvertToRelativePath(
  91. this->StateSnapshot.GetCurrentSourceDirectoryComponents(), result);
  92. break;
  93. case HOME_OUTPUT:
  94. result = this->ConvertToRelativePath(
  95. this->GetState()->GetBinaryDirectoryComponents(), result);
  96. break;
  97. case START_OUTPUT:
  98. result = this->ConvertToRelativePath(
  99. this->StateSnapshot.GetCurrentBinaryDirectoryComponents(), result);
  100. break;
  101. case FULL:
  102. result = cmSystemTools::CollapseFullPath(result);
  103. break;
  104. case NONE:
  105. break;
  106. }
  107. return this->ConvertToOutputFormat(result, output);
  108. }
  109. //----------------------------------------------------------------------------
  110. std::string cmOutputConverter::ConvertToOutputFormat(const std::string& source,
  111. OutputFormat output)
  112. {
  113. std::string result = source;
  114. // Convert it to an output path.
  115. if (output == MAKERULE)
  116. {
  117. result = cmSystemTools::ConvertToOutputPath(result.c_str());
  118. }
  119. else if(output == SHELL || output == WATCOMQUOTE)
  120. {
  121. // For the MSYS shell convert drive letters to posix paths, so
  122. // that c:/some/path becomes /c/some/path. This is needed to
  123. // avoid problems with the shell path translation.
  124. if(this->GetState()->UseMSYSShell() && !this->LinkScriptShell)
  125. {
  126. if(result.size() > 2 && result[1] == ':')
  127. {
  128. result[1] = result[0];
  129. result[0] = '/';
  130. }
  131. }
  132. if(this->GetState()->UseWindowsShell())
  133. {
  134. std::replace(result.begin(), result.end(), '/', '\\');
  135. }
  136. result = this->EscapeForShell(result, true, false, output == WATCOMQUOTE);
  137. }
  138. else if(output == RESPONSE)
  139. {
  140. result = this->EscapeForShell(result, false, false, false);
  141. }
  142. return result;
  143. }
  144. //----------------------------------------------------------------------------
  145. std::string cmOutputConverter::Convert(RelativeRoot remote,
  146. const std::string& local,
  147. OutputFormat output,
  148. bool optional)
  149. {
  150. const char* remotePath = this->GetRelativeRootPath(remote);
  151. // The relative root must have a path (i.e. not FULL or NONE)
  152. assert(remotePath != 0);
  153. if(!local.empty() && !optional)
  154. {
  155. std::vector<std::string> components;
  156. cmSystemTools::SplitPath(local, components);
  157. std::string result = this->ConvertToRelativePath(components, remotePath);
  158. return this->ConvertToOutputFormat(result, output);
  159. }
  160. return this->ConvertToOutputFormat(remotePath, output);
  161. }
  162. //----------------------------------------------------------------------------
  163. static bool cmOutputConverterNotAbove(const char* a, const char* b)
  164. {
  165. return (cmSystemTools::ComparePath(a, b) ||
  166. cmSystemTools::IsSubDirectory(a, b));
  167. }
  168. //----------------------------------------------------------------------------
  169. std::string
  170. cmOutputConverter::ConvertToRelativePath(const std::vector<std::string>& local,
  171. const std::string& in_remote,
  172. bool force)
  173. {
  174. // The path should never be quoted.
  175. assert(in_remote[0] != '\"');
  176. // The local path should never have a trailing slash.
  177. assert(!local.empty() && !(local[local.size()-1] == ""));
  178. // If the path is already relative then just return the path.
  179. if(!cmSystemTools::FileIsFullPath(in_remote.c_str()))
  180. {
  181. return in_remote;
  182. }
  183. if(!force)
  184. {
  185. // Skip conversion if the path and local are not both in the source
  186. // or both in the binary tree.
  187. std::string local_path = cmSystemTools::JoinPath(local);
  188. if(!((cmOutputConverterNotAbove(local_path.c_str(),
  189. this->StateSnapshot.GetRelativePathTopBinary()) &&
  190. cmOutputConverterNotAbove(in_remote.c_str(),
  191. this->StateSnapshot.GetRelativePathTopBinary())) ||
  192. (cmOutputConverterNotAbove(local_path.c_str(),
  193. this->StateSnapshot.GetRelativePathTopSource()) &&
  194. cmOutputConverterNotAbove(in_remote.c_str(),
  195. this->StateSnapshot.GetRelativePathTopSource()))))
  196. {
  197. return in_remote;
  198. }
  199. }
  200. // Identify the longest shared path component between the remote
  201. // path and the local path.
  202. std::vector<std::string> remote;
  203. cmSystemTools::SplitPath(in_remote, remote);
  204. unsigned int common=0;
  205. while(common < remote.size() &&
  206. common < local.size() &&
  207. cmSystemTools::ComparePath(remote[common],
  208. local[common]))
  209. {
  210. ++common;
  211. }
  212. // If no part of the path is in common then return the full path.
  213. if(common == 0)
  214. {
  215. return in_remote;
  216. }
  217. // If the entire path is in common then just return a ".".
  218. if(common == remote.size() &&
  219. common == local.size())
  220. {
  221. return ".";
  222. }
  223. // If the entire path is in common except for a trailing slash then
  224. // just return a "./".
  225. if(common+1 == remote.size() &&
  226. remote[common].empty() &&
  227. common == local.size())
  228. {
  229. return "./";
  230. }
  231. // Construct the relative path.
  232. std::string relative;
  233. // First add enough ../ to get up to the level of the shared portion
  234. // of the path. Leave off the trailing slash. Note that the last
  235. // component of local will never be empty because local should never
  236. // have a trailing slash.
  237. for(unsigned int i=common; i < local.size(); ++i)
  238. {
  239. relative += "..";
  240. if(i < local.size()-1)
  241. {
  242. relative += "/";
  243. }
  244. }
  245. // Now add the portion of the destination path that is not included
  246. // in the shared portion of the path. Add a slash the first time
  247. // only if there was already something in the path. If there was a
  248. // trailing slash in the input then the last iteration of the loop
  249. // will add a slash followed by an empty string which will preserve
  250. // the trailing slash in the output.
  251. if(!relative.empty() && !remote.empty())
  252. {
  253. relative += "/";
  254. }
  255. relative += cmJoin(cmRange(remote).advance(common), "/");
  256. // Finally return the path.
  257. return relative;
  258. }
  259. //----------------------------------------------------------------------------
  260. static bool cmOutputConverterIsShellOperator(const std::string& str)
  261. {
  262. static std::set<std::string> shellOperators;
  263. if(shellOperators.empty())
  264. {
  265. shellOperators.insert("<");
  266. shellOperators.insert(">");
  267. shellOperators.insert("<<");
  268. shellOperators.insert(">>");
  269. shellOperators.insert("|");
  270. shellOperators.insert("||");
  271. shellOperators.insert("&&");
  272. shellOperators.insert("&>");
  273. shellOperators.insert("1>");
  274. shellOperators.insert("2>");
  275. shellOperators.insert("2>&1");
  276. shellOperators.insert("1>&2");
  277. }
  278. return shellOperators.count(str) > 0;
  279. }
  280. //----------------------------------------------------------------------------
  281. std::string cmOutputConverter::EscapeForShell(const std::string& str,
  282. bool makeVars,
  283. bool forEcho,
  284. bool useWatcomQuote)
  285. {
  286. // Do not escape shell operators.
  287. if(cmOutputConverterIsShellOperator(str))
  288. {
  289. return str;
  290. }
  291. // Compute the flags for the target shell environment.
  292. int flags = 0;
  293. if(this->GetState()->UseWindowsVSIDE())
  294. {
  295. flags |= cmsysSystem_Shell_Flag_VSIDE;
  296. }
  297. else if(!this->LinkScriptShell)
  298. {
  299. flags |= cmsysSystem_Shell_Flag_Make;
  300. }
  301. if(makeVars)
  302. {
  303. flags |= cmsysSystem_Shell_Flag_AllowMakeVariables;
  304. }
  305. if(forEcho)
  306. {
  307. flags |= cmsysSystem_Shell_Flag_EchoWindows;
  308. }
  309. if(useWatcomQuote)
  310. {
  311. flags |= cmsysSystem_Shell_Flag_WatcomQuote;
  312. }
  313. if(this->GetState()->UseWatcomWMake())
  314. {
  315. flags |= cmsysSystem_Shell_Flag_WatcomWMake;
  316. }
  317. if(this->GetState()->UseMinGWMake())
  318. {
  319. flags |= cmsysSystem_Shell_Flag_MinGWMake;
  320. }
  321. if(this->GetState()->UseNMake())
  322. {
  323. flags |= cmsysSystem_Shell_Flag_NMake;
  324. }
  325. // Compute the buffer size needed.
  326. int size = (this->GetState()->UseWindowsShell() ?
  327. cmsysSystem_Shell_GetArgumentSizeForWindows(str.c_str(), flags) :
  328. cmsysSystem_Shell_GetArgumentSizeForUnix(str.c_str(), flags));
  329. // Compute the shell argument itself.
  330. std::vector<char> arg(size);
  331. if(this->GetState()->UseWindowsShell())
  332. {
  333. cmsysSystem_Shell_GetArgumentForWindows(str.c_str(), &arg[0], flags);
  334. }
  335. else
  336. {
  337. cmsysSystem_Shell_GetArgumentForUnix(str.c_str(), &arg[0], flags);
  338. }
  339. return std::string(&arg[0]);
  340. }
  341. //----------------------------------------------------------------------------
  342. std::string cmOutputConverter::EscapeForCMake(const std::string& str)
  343. {
  344. // Always double-quote the argument to take care of most escapes.
  345. std::string result = "\"";
  346. for(const char* c = str.c_str(); *c; ++c)
  347. {
  348. if(*c == '"')
  349. {
  350. // Escape the double quote to avoid ending the argument.
  351. result += "\\\"";
  352. }
  353. else if(*c == '$')
  354. {
  355. // Escape the dollar to avoid expanding variables.
  356. result += "\\$";
  357. }
  358. else if(*c == '\\')
  359. {
  360. // Escape the backslash to avoid other escapes.
  361. result += "\\\\";
  362. }
  363. else
  364. {
  365. // Other characters will be parsed correctly.
  366. result += *c;
  367. }
  368. }
  369. result += "\"";
  370. return result;
  371. }
  372. //----------------------------------------------------------------------------
  373. cmOutputConverter::FortranFormat
  374. cmOutputConverter::GetFortranFormat(const char* value)
  375. {
  376. FortranFormat format = FortranFormatNone;
  377. if(value && *value)
  378. {
  379. std::vector<std::string> fmt;
  380. cmSystemTools::ExpandListArgument(value, fmt);
  381. for(std::vector<std::string>::iterator fi = fmt.begin();
  382. fi != fmt.end(); ++fi)
  383. {
  384. if(*fi == "FIXED")
  385. {
  386. format = FortranFormatFixed;
  387. }
  388. if(*fi == "FREE")
  389. {
  390. format = FortranFormatFree;
  391. }
  392. }
  393. }
  394. return format;
  395. }
  396. void cmOutputConverter::SetLinkScriptShell(bool linkScriptShell)
  397. {
  398. this->LinkScriptShell = linkScriptShell;
  399. }
  400. cmState* cmOutputConverter::GetState() const
  401. {
  402. return this->StateSnapshot.GetState();
  403. }