cmCableWrapTclCommand.cxx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  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& args)
  106. {
  107. if(args.size() < 2)
  108. {
  109. this->SetError("called with incorrect number of arguments");
  110. return false;
  111. }
  112. // Prepare to iterate through the arguments.
  113. std::vector<std::string>::const_iterator arg = args.begin();
  114. // The first argument is the name of the target.
  115. m_TargetName = *arg++;
  116. // Create the new class set.
  117. m_CableClassSet = new cmCableClassSet(m_TargetName.c_str());
  118. // Add all the regular entries.
  119. for(; (arg != args.end()) && (*arg != "SOURCES_BEGIN"); ++arg)
  120. {
  121. m_CableClassSet->ParseAndAddElement(arg->c_str(), m_Makefile);
  122. }
  123. // Add any sources that are associated with all the members.
  124. if(arg != args.end())
  125. {
  126. for(++arg; arg != args.end(); ++arg)
  127. {
  128. m_CableClassSet->AddSource(arg->c_str());
  129. }
  130. }
  131. this->GenerateCableFiles();
  132. // Add the source list to the target.
  133. m_Makefile->GetTargets()[m_TargetName.c_str()].GetSourceLists().push_back(m_TargetName);
  134. return true;
  135. }
  136. /**
  137. * Generate the files that CABLE will use to generate the wrappers.
  138. */
  139. void cmCableWrapTclCommand::GenerateCableFiles() const
  140. {
  141. // Make sure the dependency generator is ready to go.
  142. m_MakeDepend->SetMakefile(m_Makefile);
  143. // Each wrapped class may have an associated "tag" that represents
  144. // an alternative name without funky C++ syntax in it. This makes
  145. // it easier to refer to the class in a Tcl script. We will also
  146. // use the tags to make easy-to-read, unique file names for each
  147. // class's wrapper. Count the number of times each tag is used.
  148. // Warn if a tag is used more than once.
  149. std::map<cmStdString, unsigned int> tagCounts;
  150. for(cmCableClassSet::CableClassMap::const_iterator
  151. c = m_CableClassSet->Begin(); c != m_CableClassSet->End(); ++c)
  152. {
  153. std::string tag = c->second->GetTag();
  154. if((++tagCounts[tag] > 1) && (tag != ""))
  155. {
  156. std::string message =
  157. "CABLE_WRAP_TCL has found two classes with the tag "+tag
  158. +" for target "+m_TargetName;
  159. cmSystemTools::Message(message.c_str(), "Warning");
  160. }
  161. }
  162. // Each class wrapper will be written into its own CABLE "group"
  163. // file. This should hold the names of the groups generated so that
  164. // the package configuration file can tell cable how to generate the
  165. // package initialization code.
  166. std::vector<std::string> groupTags;
  167. // Setup the output directory name and make sure it exists.
  168. std::string outDir = m_Makefile->GetCurrentOutputDirectory();
  169. cmSystemTools::MakeDirectory((outDir+"/Tcl").c_str());
  170. // Write out the cable configuration files with one class per group.
  171. // Try to name the groups based on their class's tag, but use an
  172. // index to disambiguate tag repeats (mostly used for empty tags).
  173. std::map<cmStdString, unsigned int> tagIndexes;
  174. for(cmCableClassSet::CableClassMap::const_iterator
  175. c = m_CableClassSet->Begin(); c != m_CableClassSet->End(); ++c)
  176. {
  177. // Construct the group's tag-based name, with index if necessary.
  178. std::string tag = c->second->GetTag();
  179. std::string groupTag;
  180. if(tagCounts[tag] > 1)
  181. {
  182. unsigned int tagIndex = tagIndexes[tag]++;
  183. std::strstream indexStrStream;
  184. indexStrStream << tagIndex << std::ends;
  185. std::string indexStr = indexStrStream.str();
  186. groupTag = "_"+indexStr;
  187. }
  188. if(tag != "")
  189. {
  190. groupTag += "_"+tag;
  191. }
  192. // Save this group tag in the list of tags for the main package
  193. // configuration file below.
  194. groupTags.push_back(groupTag);
  195. // Actually generate the class's configuration file.
  196. this->GenerateCableClassFiles(c->first.c_str(), *(c->second),
  197. groupTag.c_str());
  198. }
  199. // Construct the output file names.
  200. std::string packageConfigName = outDir+"/Tcl/"+m_TargetName+"_config.xml";
  201. std::string packageTclFileName = "Tcl/"+m_TargetName+"_tcl";
  202. std::string packageTclFullName = outDir+"/"+packageTclFileName;
  203. // Generate the main package configuration file for CABLE. This
  204. // just lists the "group" files generated above.
  205. cmGeneratedFileStream packageConfig(packageConfigName.c_str());
  206. if(packageConfig)
  207. {
  208. packageConfig <<
  209. "<CableConfiguration package=\"" << m_TargetName.c_str() << "\">\n";
  210. for(std::vector<std::string>::const_iterator g = groupTags.begin();
  211. g != groupTags.end(); ++g)
  212. {
  213. packageConfig <<
  214. " <Group name=\"" << m_TargetName.c_str() << g->c_str() << "\"/>\n";
  215. }
  216. packageConfig <<
  217. "</CableConfiguration>\n";
  218. packageConfig.close();
  219. }
  220. else
  221. {
  222. cmSystemTools::Error("Error opening CABLE configuration file for writing: ",
  223. packageConfigName.c_str());
  224. }
  225. // Generate the rule to run CABLE for the package configuration file.
  226. std::string command = "${CABLE}";
  227. m_Makefile->ExpandVariablesInString(command);
  228. std::vector<std::string> depends;
  229. depends.push_back(command);
  230. std::vector<std::string> commandArgs;
  231. commandArgs.push_back(packageConfigName);
  232. commandArgs.push_back("-tcl");
  233. std::string tmp = packageTclFullName+".cxx";
  234. commandArgs.push_back(tmp);
  235. depends.push_back(packageConfigName);
  236. std::vector<std::string> outputs;
  237. outputs.push_back(packageTclFullName+".cxx");
  238. m_Makefile->AddCustomCommand(packageConfigName.c_str(),
  239. command.c_str(),
  240. commandArgs,
  241. depends,
  242. outputs, m_TargetName.c_str());
  243. // Add the generated source to the package target's source list.
  244. cmSourceFile file;
  245. file.SetName(packageTclFileName.c_str(), outDir.c_str(), "cxx", false);
  246. // Set dependency hints.
  247. file.GetDepends().push_back("WrapTclFacility/wrapCalls.h");
  248. m_Makefile->AddSource(file, m_TargetName.c_str());
  249. }
  250. void cmCableWrapTclCommand::GenerateCableClassFiles(const char* name,
  251. const cmCableClass& c,
  252. const char* groupTag) const
  253. {
  254. std::string outDir = m_Makefile->GetCurrentOutputDirectory();
  255. std::string className = name;
  256. std::string groupName = m_TargetName+groupTag;
  257. std::string classConfigName = outDir+"/Tcl/"+groupName+"_config_tcl.xml";
  258. std::string classCxxName = outDir+"/Tcl/"+groupName+"_cxx.cc";
  259. std::string classXmlName = outDir+"/Tcl/"+groupName+"_cxx.xml";
  260. std::string classTclFileName = "Tcl/"+groupName+"_tcl";
  261. std::string classTclFullName = outDir+"/"+classTclFileName;
  262. cmGeneratedFileStream classConfig(classConfigName.c_str());
  263. if(classConfig)
  264. {
  265. classConfig <<
  266. "<CableConfiguration source=\"" << classXmlName.c_str() << "\" "
  267. "group=\"" << groupName.c_str() << "\">\n";
  268. for(cmCableClass::Sources::const_iterator source = c.SourcesBegin();
  269. source != c.SourcesEnd(); ++source)
  270. {
  271. classConfig <<
  272. " <Header name=\"" << source->c_str() << "\"/>\n";
  273. }
  274. classConfig <<
  275. " <Class name=\"_wrap_::wrapper::Wrapper\">\n";
  276. if(c.GetTag() != "")
  277. {
  278. classConfig <<
  279. " <AlternateName name=\"" << c.GetTag().c_str() << "\"/>\n";
  280. }
  281. classConfig <<
  282. " </Class>\n"
  283. "</CableConfiguration>\n";
  284. classConfig.close();
  285. }
  286. else
  287. {
  288. cmSystemTools::Error("Error opening CABLE configuration file for writing: ",
  289. classConfigName.c_str());
  290. }
  291. cmGeneratedFileStream classCxx(classCxxName.c_str());
  292. if(classCxx)
  293. {
  294. for(cmCableClass::Sources::const_iterator source = c.SourcesBegin();
  295. source != c.SourcesEnd(); ++source)
  296. {
  297. classCxx <<
  298. "#include \"" << source->c_str() << "\"\n";
  299. }
  300. classCxx <<
  301. "\n"
  302. "namespace _wrap_\n"
  303. "{\n"
  304. "\n"
  305. "struct wrapper\n"
  306. "{\n"
  307. " typedef ::" << className.c_str() << " Wrapper;\n"
  308. "};\n"
  309. "\n"
  310. "template <typename T> void Eat(T) {}\n"
  311. "\n"
  312. "void InstantiateMemberDeclarations()\n"
  313. "{\n"
  314. " Eat(sizeof(wrapper::Wrapper));\n"
  315. "}\n"
  316. "\n"
  317. "}\n";
  318. classCxx.close();
  319. }
  320. else
  321. {
  322. cmSystemTools::Error("Error opening file for writing: ",
  323. classCxxName.c_str());
  324. }
  325. // Generate the rule to have GCC-XML parse the classes to be wrapped.
  326. {
  327. std::string command = this->GetGccXmlFromCache();
  328. std::vector<std::string> depends;
  329. depends.push_back(command);
  330. // Get the dependencies of the file.
  331. const cmDependInformation* dependInfo =
  332. m_MakeDepend->FindDependencies(classCxxName.c_str());
  333. if(dependInfo)
  334. {
  335. for(cmDependInformation::DependencySet::const_iterator d =
  336. dependInfo->m_DependencySet.begin();
  337. d != dependInfo->m_DependencySet.end(); ++d)
  338. {
  339. // Make sure the full path is given. If not, the dependency was
  340. // not found.
  341. if((*d)->m_FullPath != "")
  342. {
  343. depends.push_back((*d)->m_FullPath);
  344. }
  345. }
  346. }
  347. std::vector<std::string> commandArgs;
  348. this->AddGccXmlFlagsFromCache(commandArgs);
  349. {
  350. std::string tmp = "-I";
  351. tmp += m_Makefile->GetStartDirectory();
  352. commandArgs.push_back(tmp.c_str());
  353. }
  354. const std::vector<std::string>& includes =
  355. m_Makefile->GetIncludeDirectories();
  356. for(std::vector<std::string>::const_iterator i = includes.begin();
  357. i != includes.end(); ++i)
  358. {
  359. std::string tmp = "-I";
  360. tmp += i->c_str();
  361. m_Makefile->ExpandVariablesInString(tmp);
  362. commandArgs.push_back(tmp.c_str());
  363. }
  364. std::string tmp = "-fxml=";
  365. tmp += classXmlName;
  366. commandArgs.push_back(tmp);
  367. commandArgs.push_back(classCxxName);
  368. std::vector<std::string> outputs;
  369. outputs.push_back(classXmlName);
  370. m_Makefile->AddCustomCommand(classCxxName.c_str(),
  371. command.c_str(),
  372. commandArgs,
  373. depends,
  374. outputs, m_TargetName.c_str());
  375. }
  376. // Generate the rule to run cable on the GCC-XML output to generate wrappers.
  377. {
  378. std::string command = this->GetCableFromCache();
  379. std::vector<std::string> depends;
  380. depends.push_back(command);
  381. std::vector<std::string > commandArgs;
  382. commandArgs.push_back(classConfigName);
  383. commandArgs.push_back("-tcl");
  384. std::string tmp = classTclFullName+".cxx";
  385. commandArgs.push_back(tmp);
  386. depends.push_back(classConfigName);
  387. depends.push_back(classXmlName);
  388. std::vector<std::string> outputs;
  389. outputs.push_back(classTclFullName+".cxx");
  390. m_Makefile->AddCustomCommand(classConfigName.c_str(),
  391. command.c_str(),
  392. commandArgs, depends,
  393. outputs, m_TargetName.c_str());
  394. }
  395. // Add the generated source to the package's source list.
  396. cmSourceFile file;
  397. file.SetName(classTclFileName.c_str(), outDir.c_str(), "cxx", false);
  398. // Set dependency hints.
  399. for(cmCableClass::Sources::const_iterator source = c.SourcesBegin();
  400. source != c.SourcesEnd(); ++source)
  401. {
  402. file.GetDepends().push_back(*source);
  403. }
  404. file.GetDepends().push_back("WrapTclFacility/wrapCalls.h");
  405. m_Makefile->AddSource(file, m_TargetName.c_str());
  406. }
  407. /**
  408. * Get the "GCCXML" cache entry value. If there is no cache entry for GCCXML,
  409. * one will be created and initialized to NOTFOUND.
  410. */
  411. std::string cmCableWrapTclCommand::GetGccXmlFromCache() const
  412. {
  413. const char* gccxml =
  414. m_Makefile->GetDefinition("GCCXML");
  415. if(gccxml)
  416. { return gccxml; }
  417. m_Makefile->AddCacheDefinition("GCCXML",
  418. "NOTFOUND",
  419. "Path to GCC-XML executable.",
  420. cmCacheManager::FILEPATH);
  421. return "NOTFOUND";
  422. }
  423. /**
  424. * Get the "GCCXML_FLAGS" cache entry value. If there is no cache
  425. * entry for GCCXML_FLAGS, one will be created and initialized "".
  426. */
  427. std::string cmCableWrapTclCommand::GetGccXmlFlagsFromCache() const
  428. {
  429. const char* gccxmlFlags =
  430. m_Makefile->GetDefinition("GCCXML_FLAGS");
  431. if(gccxmlFlags)
  432. { return gccxmlFlags; }
  433. m_Makefile->AddCacheDefinition(
  434. "GCCXML_FLAGS",
  435. "",
  436. "Flags to GCC-XML to get it to parse the native compiler's headers.",
  437. cmCacheManager::STRING);
  438. return "";
  439. }
  440. /**
  441. * Get the "CABLE" cache entry value. If there is no cache entry for CABLE,
  442. * one will be created and initialized to NOTFOUND.
  443. */
  444. std::string cmCableWrapTclCommand::GetCableFromCache() const
  445. {
  446. const char* cable =
  447. m_Makefile->GetDefinition("CABLE");
  448. if(cable)
  449. { return cable; }
  450. m_Makefile->AddCacheDefinition("CABLE",
  451. "NOTFOUND",
  452. "Path to CABLE executable.",
  453. cmCacheManager::FILEPATH);
  454. return "NOTFOUND";
  455. }
  456. /**
  457. * Parse flags from the result of GetGccXmlFlagsFromCache() and push
  458. * them onto the back of the given vector, in order. This uses an
  459. * instance of cmGccXmlFlagsParser associated with the makefile so
  460. * that parsing need only be done once.
  461. */
  462. void
  463. cmCableWrapTclCommand
  464. ::AddGccXmlFlagsFromCache(std::vector<std::string>& resultArgs) const
  465. {
  466. cmGccXmlFlagsParser* parser = 0;
  467. // See if the instance already exists with the parsed flags.
  468. cmData* data = m_Makefile->LookupData("cmGccXmlFlagsParser");
  469. if(data)
  470. {
  471. // Yes, use it.
  472. parser = static_cast<cmGccXmlFlagsParser*>(data);
  473. }
  474. else
  475. {
  476. // No, create the instance and ask it to parse the flags.
  477. parser = new cmGccXmlFlagsParser("cmGccXmlFlagsParser");
  478. m_Makefile->RegisterData(parser);
  479. parser->Parse(this->GetGccXmlFlagsFromCache().c_str());
  480. parser->Parse(m_Makefile->GetDefineFlags());
  481. }
  482. // Use the parsed flags in the single instance.
  483. parser->AddParsedFlags(resultArgs);
  484. }