cmCableWrapTclCommand.cxx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. /*=========================================================================
  2. Program: Insight Segmentation & Registration Toolkit
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Insight Consortium. All rights reserved.
  8. See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
  9. This software is distributed WITHOUT ANY WARRANTY; without even
  10. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  11. PURPOSE. See the above copyright notices for more information.
  12. =========================================================================*/
  13. #include "cmCableWrapTclCommand.h"
  14. #include "cmCacheManager.h"
  15. #include "cmTarget.h"
  16. #include "cmGeneratedFileStream.h"
  17. #include "cmMakeDepend.h"
  18. #include "cmData.h"
  19. /**
  20. * One instance of this is associated with the makefile to hold a
  21. * vector of the GCC-XML flags pre-parsed from the string in the
  22. * cache. The first cmCableWrapTclCommand to need it creates the
  23. * instance. The others find it and use it.
  24. */
  25. class cmCableWrapTclCommand::cmGccXmlFlagsParser: public cmData
  26. {
  27. public:
  28. cmGccXmlFlagsParser(const char* name): cmData(name) {}
  29. void Parse(const char*);
  30. void AddParsedFlags(std::vector<std::string>& resultArgs);
  31. private:
  32. void AddFlag(const std::string&);
  33. std::vector<std::string> m_Flags;
  34. };
  35. void cmCableWrapTclCommand::cmGccXmlFlagsParser::Parse(const char* in_flags)
  36. {
  37. // Prepare a work string for searching.
  38. std::string flags = in_flags;
  39. // Look for " -" separating arguments.
  40. // The first argument starts at the first "-" character.
  41. std::string::size_type leftPos = flags.find_first_of("-");
  42. if(leftPos == std::string::npos) { return; }
  43. std::string::size_type rightPos = flags.find(" -", leftPos);
  44. while(rightPos != std::string::npos)
  45. {
  46. // Pull out and store this argument.
  47. this->AddFlag(flags.substr(leftPos, rightPos-leftPos));
  48. // The next argument starts at the '-' from the previously found " -".
  49. leftPos = rightPos+1;
  50. rightPos = flags.find(" -", leftPos);
  51. }
  52. // Pull out and store the last argument.
  53. this->AddFlag(flags.substr(leftPos, std::string::npos));
  54. }
  55. void
  56. cmCableWrapTclCommand::cmGccXmlFlagsParser
  57. ::AddParsedFlags(std::vector<std::string>& resultArgs)
  58. {
  59. for(std::vector<std::string>::const_iterator flag = m_Flags.begin();
  60. flag != m_Flags.end(); ++flag)
  61. {
  62. resultArgs.push_back(*flag);
  63. }
  64. }
  65. /**
  66. * Used by Parse() to insert a parsed flag. Strips trailing whitespace from
  67. * the argument.
  68. *
  69. * Includes a hack to split "-o /dev/null" into two arguments since
  70. * the parser only splits arguments with " -" occurrences.
  71. */
  72. void
  73. cmCableWrapTclCommand::cmGccXmlFlagsParser
  74. ::AddFlag(const std::string& flag)
  75. {
  76. std::string tmp = flag.substr(0, flag.find_last_not_of(" \t")+1);
  77. if(tmp == "-o /dev/null")
  78. {
  79. m_Flags.push_back("-o");
  80. m_Flags.push_back("/dev/null");
  81. }
  82. else if(tmp == "-D__int64='long long'")
  83. {
  84. m_Flags.push_back("-D__int64='long");
  85. m_Flags.push_back("long'");
  86. }
  87. else
  88. {
  89. m_Flags.push_back(tmp);
  90. }
  91. }
  92. cmCableWrapTclCommand::cmCableWrapTclCommand():
  93. m_CableClassSet(NULL), m_MakeDepend(new cmMakeDepend)
  94. {
  95. }
  96. cmCableWrapTclCommand::~cmCableWrapTclCommand()
  97. {
  98. if(m_CableClassSet)
  99. {
  100. delete m_CableClassSet;
  101. }
  102. delete m_MakeDepend;
  103. }
  104. // cmCableWrapTclCommand
  105. bool cmCableWrapTclCommand::InitialPass(std::vector<std::string> const& argsIn)
  106. {
  107. if(argsIn.size() < 2)
  108. {
  109. this->SetError("called with incorrect number of arguments");
  110. return false;
  111. }
  112. std::vector<std::string> args;
  113. cmSystemTools::ExpandListArguments(argsIn, args);
  114. // Prepare to iterate through the arguments.
  115. std::vector<std::string>::const_iterator arg = args.begin();
  116. // The first argument is the name of the target.
  117. m_TargetName = *arg++;
  118. // Create the new class set.
  119. m_CableClassSet = new cmCableClassSet(m_TargetName.c_str());
  120. // Add all the regular entries.
  121. for(; (arg != args.end()) && (*arg != "SOURCES_BEGIN"); ++arg)
  122. {
  123. m_CableClassSet->ParseAndAddElement(arg->c_str(), m_Makefile);
  124. }
  125. // Add any sources that are associated with all the members.
  126. if(arg != args.end())
  127. {
  128. for(++arg; arg != args.end(); ++arg)
  129. {
  130. m_CableClassSet->AddSource(arg->c_str());
  131. }
  132. }
  133. this->GenerateCableFiles();
  134. // Add the source list to the target.
  135. m_Makefile->GetTargets()[m_TargetName.c_str()].GetSourceLists().push_back(m_TargetName);
  136. return true;
  137. }
  138. /**
  139. * Generate the files that CABLE will use to generate the wrappers.
  140. */
  141. void cmCableWrapTclCommand::GenerateCableFiles() const
  142. {
  143. // Make sure the dependency generator is ready to go.
  144. m_MakeDepend->SetMakefile(m_Makefile);
  145. // Each wrapped class may have an associated "tag" that represents
  146. // an alternative name without funky C++ syntax in it. This makes
  147. // it easier to refer to the class in a Tcl script. We will also
  148. // use the tags to make easy-to-read, unique file names for each
  149. // class's wrapper. Count the number of times each tag is used.
  150. // Warn if a tag is used more than once.
  151. std::map<cmStdString, unsigned int> tagCounts;
  152. for(cmCableClassSet::CableClassMap::const_iterator
  153. c = m_CableClassSet->Begin(); c != m_CableClassSet->End(); ++c)
  154. {
  155. std::string tag = c->second->GetTag();
  156. if((++tagCounts[tag] > 1) && (tag != ""))
  157. {
  158. std::string message =
  159. "CABLE_WRAP_TCL has found two classes with the tag "+tag
  160. +" for target "+m_TargetName;
  161. cmSystemTools::Message(message.c_str(), "Warning");
  162. }
  163. }
  164. // Each class wrapper will be written into its own CABLE "group"
  165. // file. This should hold the names of the groups generated so that
  166. // the package configuration file can tell cable how to generate the
  167. // package initialization code.
  168. std::vector<std::string> groupTags;
  169. // Setup the output directory name and make sure it exists.
  170. std::string outDir = m_Makefile->GetCurrentOutputDirectory();
  171. cmSystemTools::MakeDirectory((outDir+"/Tcl").c_str());
  172. // Write out the cable configuration files with one class per group.
  173. // Try to name the groups based on their class's tag, but use an
  174. // index to disambiguate tag repeats (mostly used for empty tags).
  175. std::map<cmStdString, unsigned int> tagIndexes;
  176. for(cmCableClassSet::CableClassMap::const_iterator
  177. c = m_CableClassSet->Begin(); c != m_CableClassSet->End(); ++c)
  178. {
  179. // Construct the group's tag-based name, with index if necessary.
  180. std::string tag = c->second->GetTag();
  181. std::string groupTag;
  182. if(tagCounts[tag] > 1)
  183. {
  184. unsigned int tagIndex = tagIndexes[tag]++;
  185. std::strstream indexStrStream;
  186. indexStrStream << tagIndex << std::ends;
  187. std::string indexStr = indexStrStream.str();
  188. groupTag = "_"+indexStr;
  189. }
  190. if(tag != "")
  191. {
  192. groupTag += "_"+tag;
  193. }
  194. // Save this group tag in the list of tags for the main package
  195. // configuration file below.
  196. groupTags.push_back(groupTag);
  197. // Actually generate the class's configuration file.
  198. this->GenerateCableClassFiles(c->first.c_str(), *(c->second),
  199. groupTag.c_str());
  200. }
  201. // Construct the output file names.
  202. std::string packageConfigName = outDir+"/Tcl/"+m_TargetName+"_config.xml";
  203. std::string packageTclFileName = "Tcl/"+m_TargetName+"_tcl";
  204. std::string packageTclFullName = outDir+"/"+packageTclFileName;
  205. // Generate the main package configuration file for CABLE. This
  206. // just lists the "group" files generated above.
  207. cmGeneratedFileStream packageConfig(packageConfigName.c_str());
  208. if(packageConfig)
  209. {
  210. packageConfig <<
  211. "<CableConfiguration package=\"" << m_TargetName.c_str() << "\">\n";
  212. for(std::vector<std::string>::const_iterator g = groupTags.begin();
  213. g != groupTags.end(); ++g)
  214. {
  215. packageConfig <<
  216. " <Group name=\"" << m_TargetName.c_str() << g->c_str() << "\"/>\n";
  217. }
  218. packageConfig <<
  219. "</CableConfiguration>\n";
  220. packageConfig.close();
  221. }
  222. else
  223. {
  224. cmSystemTools::Error("Error opening CABLE configuration file for writing: ",
  225. packageConfigName.c_str());
  226. }
  227. // Generate the rule to run CABLE for the package configuration file.
  228. std::string command = "${CABLE}";
  229. m_Makefile->ExpandVariablesInString(command);
  230. std::vector<std::string> depends;
  231. depends.push_back(command);
  232. std::vector<std::string> commandArgs;
  233. commandArgs.push_back(packageConfigName);
  234. commandArgs.push_back("-tcl");
  235. std::string tmp = packageTclFullName+".cxx";
  236. commandArgs.push_back(tmp);
  237. depends.push_back(packageConfigName);
  238. std::vector<std::string> outputs;
  239. outputs.push_back(packageTclFullName+".cxx");
  240. m_Makefile->AddCustomCommand(packageConfigName.c_str(),
  241. command.c_str(),
  242. commandArgs,
  243. depends,
  244. outputs, m_TargetName.c_str());
  245. // Add the generated source to the package target's source list.
  246. cmSourceFile file;
  247. file.SetName(packageTclFileName.c_str(), outDir.c_str(), "cxx", false);
  248. // Set dependency hints.
  249. file.GetDepends().push_back("WrapTclFacility/wrapCalls.h");
  250. m_Makefile->AddSource(file, m_TargetName.c_str());
  251. }
  252. void cmCableWrapTclCommand::GenerateCableClassFiles(const char* name,
  253. const cmCableClass& c,
  254. const char* groupTag) const
  255. {
  256. std::string outDir = m_Makefile->GetCurrentOutputDirectory();
  257. std::string className = name;
  258. std::string groupName = m_TargetName+groupTag;
  259. std::string classConfigName = outDir+"/Tcl/"+groupName+"_config_tcl.xml";
  260. std::string classCxxName = outDir+"/Tcl/"+groupName+"_cxx.cc";
  261. std::string classXmlName = outDir+"/Tcl/"+groupName+"_cxx.xml";
  262. std::string classTclFileName = "Tcl/"+groupName+"_tcl";
  263. std::string classTclFullName = outDir+"/"+classTclFileName;
  264. cmGeneratedFileStream classConfig(classConfigName.c_str());
  265. if(classConfig)
  266. {
  267. classConfig <<
  268. "<CableConfiguration source=\"" << classXmlName.c_str() << "\" "
  269. "group=\"" << groupName.c_str() << "\">\n";
  270. for(cmCableClass::Sources::const_iterator source = c.SourcesBegin();
  271. source != c.SourcesEnd(); ++source)
  272. {
  273. classConfig <<
  274. " <Header name=\"" << source->c_str() << "\"/>\n";
  275. }
  276. classConfig <<
  277. " <Class name=\"_wrap_::wrapper::Wrapper\">\n";
  278. if(c.GetTag() != "")
  279. {
  280. classConfig <<
  281. " <AlternateName name=\"" << c.GetTag().c_str() << "\"/>\n";
  282. }
  283. classConfig <<
  284. " </Class>\n"
  285. "</CableConfiguration>\n";
  286. classConfig.close();
  287. }
  288. else
  289. {
  290. cmSystemTools::Error("Error opening CABLE configuration file for writing: ",
  291. classConfigName.c_str());
  292. }
  293. cmGeneratedFileStream classCxx(classCxxName.c_str());
  294. if(classCxx)
  295. {
  296. for(cmCableClass::Sources::const_iterator source = c.SourcesBegin();
  297. source != c.SourcesEnd(); ++source)
  298. {
  299. classCxx <<
  300. "#include \"" << source->c_str() << "\"\n";
  301. }
  302. classCxx <<
  303. "\n"
  304. "namespace _wrap_\n"
  305. "{\n"
  306. "\n"
  307. "struct wrapper\n"
  308. "{\n"
  309. " typedef ::" << className.c_str() << " Wrapper;\n"
  310. "};\n"
  311. "\n"
  312. "template <typename T> void Eat(T) {}\n"
  313. "\n"
  314. "void InstantiateMemberDeclarations()\n"
  315. "{\n"
  316. " Eat(sizeof(wrapper::Wrapper));\n"
  317. "}\n"
  318. "\n"
  319. "}\n";
  320. classCxx.close();
  321. }
  322. else
  323. {
  324. cmSystemTools::Error("Error opening file for writing: ",
  325. classCxxName.c_str());
  326. }
  327. // Generate the rule to have GCC-XML parse the classes to be wrapped.
  328. {
  329. std::string command = this->GetGccXmlFromCache();
  330. std::vector<std::string> depends;
  331. depends.push_back(command);
  332. // Get the dependencies of the file.
  333. const cmDependInformation* dependInfo =
  334. m_MakeDepend->FindDependencies(classCxxName.c_str());
  335. if(dependInfo)
  336. {
  337. for(cmDependInformation::DependencySet::const_iterator d =
  338. dependInfo->m_DependencySet.begin();
  339. d != dependInfo->m_DependencySet.end(); ++d)
  340. {
  341. // Make sure the full path is given. If not, the dependency was
  342. // not found.
  343. if((*d)->m_FullPath != "")
  344. {
  345. depends.push_back((*d)->m_FullPath);
  346. }
  347. }
  348. }
  349. std::vector<std::string> commandArgs;
  350. this->AddGccXmlFlagsFromCache(commandArgs);
  351. {
  352. std::string tmp = "-I";
  353. tmp += m_Makefile->GetStartDirectory();
  354. commandArgs.push_back(tmp.c_str());
  355. }
  356. const std::vector<std::string>& includes =
  357. m_Makefile->GetIncludeDirectories();
  358. for(std::vector<std::string>::const_iterator i = includes.begin();
  359. i != includes.end(); ++i)
  360. {
  361. std::string tmp = "-I";
  362. tmp += i->c_str();
  363. m_Makefile->ExpandVariablesInString(tmp);
  364. commandArgs.push_back(tmp.c_str());
  365. }
  366. std::string tmp = "-fxml=";
  367. tmp += classXmlName;
  368. commandArgs.push_back(tmp);
  369. commandArgs.push_back(classCxxName);
  370. std::vector<std::string> outputs;
  371. outputs.push_back(classXmlName);
  372. m_Makefile->AddCustomCommand(classCxxName.c_str(),
  373. command.c_str(),
  374. commandArgs,
  375. depends,
  376. outputs, m_TargetName.c_str());
  377. }
  378. // Generate the rule to run cable on the GCC-XML output to generate wrappers.
  379. {
  380. std::string command = this->GetCableFromCache();
  381. std::vector<std::string> depends;
  382. depends.push_back(command);
  383. std::vector<std::string > commandArgs;
  384. commandArgs.push_back(classConfigName);
  385. commandArgs.push_back("-tcl");
  386. std::string tmp = classTclFullName+".cxx";
  387. commandArgs.push_back(tmp);
  388. depends.push_back(classConfigName);
  389. depends.push_back(classXmlName);
  390. std::vector<std::string> outputs;
  391. outputs.push_back(classTclFullName+".cxx");
  392. m_Makefile->AddCustomCommand(classConfigName.c_str(),
  393. command.c_str(),
  394. commandArgs, depends,
  395. outputs, m_TargetName.c_str());
  396. }
  397. // Add the generated source to the package's source list.
  398. cmSourceFile file;
  399. file.SetName(classTclFileName.c_str(), outDir.c_str(), "cxx", false);
  400. // Set dependency hints.
  401. for(cmCableClass::Sources::const_iterator source = c.SourcesBegin();
  402. source != c.SourcesEnd(); ++source)
  403. {
  404. file.GetDepends().push_back(*source);
  405. }
  406. file.GetDepends().push_back("WrapTclFacility/wrapCalls.h");
  407. m_Makefile->AddSource(file, m_TargetName.c_str());
  408. }
  409. /**
  410. * Get the "GCCXML" cache entry value. If there is no cache entry for GCCXML,
  411. * one will be created and initialized to NOTFOUND.
  412. */
  413. std::string cmCableWrapTclCommand::GetGccXmlFromCache() const
  414. {
  415. const char* gccxml =
  416. m_Makefile->GetDefinition("GCCXML");
  417. if(gccxml)
  418. { return gccxml; }
  419. m_Makefile->AddCacheDefinition("GCCXML",
  420. "NOTFOUND",
  421. "Path to GCC-XML executable.",
  422. cmCacheManager::FILEPATH);
  423. return "NOTFOUND";
  424. }
  425. /**
  426. * Get the "GCCXML_FLAGS" cache entry value. If there is no cache
  427. * entry for GCCXML_FLAGS, one will be created and initialized "".
  428. */
  429. std::string cmCableWrapTclCommand::GetGccXmlFlagsFromCache() const
  430. {
  431. const char* gccxmlFlags =
  432. m_Makefile->GetDefinition("GCCXML_FLAGS");
  433. if(gccxmlFlags)
  434. { return gccxmlFlags; }
  435. m_Makefile->AddCacheDefinition(
  436. "GCCXML_FLAGS",
  437. "",
  438. "Flags to GCC-XML to get it to parse the native compiler's headers.",
  439. cmCacheManager::STRING);
  440. return "";
  441. }
  442. /**
  443. * Get the "CABLE" cache entry value. If there is no cache entry for CABLE,
  444. * one will be created and initialized to NOTFOUND.
  445. */
  446. std::string cmCableWrapTclCommand::GetCableFromCache() const
  447. {
  448. const char* cable =
  449. m_Makefile->GetDefinition("CABLE");
  450. if(cable)
  451. { return cable; }
  452. m_Makefile->AddCacheDefinition("CABLE",
  453. "NOTFOUND",
  454. "Path to CABLE executable.",
  455. cmCacheManager::FILEPATH);
  456. return "NOTFOUND";
  457. }
  458. /**
  459. * Parse flags from the result of GetGccXmlFlagsFromCache() and push
  460. * them onto the back of the given vector, in order. This uses an
  461. * instance of cmGccXmlFlagsParser associated with the makefile so
  462. * that parsing need only be done once.
  463. */
  464. void
  465. cmCableWrapTclCommand
  466. ::AddGccXmlFlagsFromCache(std::vector<std::string>& resultArgs) const
  467. {
  468. cmGccXmlFlagsParser* parser = 0;
  469. // See if the instance already exists with the parsed flags.
  470. cmData* data = m_Makefile->LookupData("cmGccXmlFlagsParser");
  471. if(data)
  472. {
  473. // Yes, use it.
  474. parser = static_cast<cmGccXmlFlagsParser*>(data);
  475. }
  476. else
  477. {
  478. // No, create the instance and ask it to parse the flags.
  479. parser = new cmGccXmlFlagsParser("cmGccXmlFlagsParser");
  480. m_Makefile->RegisterData(parser);
  481. parser->Parse(this->GetGccXmlFlagsFromCache().c_str());
  482. parser->Parse(m_Makefile->GetDefineFlags());
  483. }
  484. // Use the parsed flags in the single instance.
  485. parser->AddParsedFlags(resultArgs);
  486. }