cmCableWrapTclCommand.cxx 17 KB

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