PageRenderTime 26ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/compiler/package.cpp

https://github.com/zsj888/hiphop-php
C++ | 474 lines | 390 code | 57 blank | 27 comment | 112 complexity | c6010854aa5d42089e8614531647b616 MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010 Facebook, Inc. (http://www.facebook.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include <compiler/package.h>
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #include <unistd.h>
  20. #include <dirent.h>
  21. #include <util/process.h>
  22. #include <util/util.h>
  23. #include <compiler/analysis/analysis_result.h>
  24. #include <compiler/parser/parser.h>
  25. #include <util/logger.h>
  26. #include <util/json.h>
  27. #include <compiler/analysis/symbol_table.h>
  28. #include <compiler/analysis/variable_table.h>
  29. #include <compiler/option.h>
  30. #include <util/db_conn.h>
  31. #include <util/db_query.h>
  32. #include <util/exception.h>
  33. #include <util/preprocess.h>
  34. using namespace HPHP;
  35. using namespace std;
  36. ///////////////////////////////////////////////////////////////////////////////
  37. // statics
  38. void (*Package::m_hookHandler)(Package *package, const char *path,
  39. HphpHookUniqueId id);
  40. ///////////////////////////////////////////////////////////////////////////////
  41. Package::Package(const char *root, bool bShortTags /* = true */,
  42. bool bAspTags /* = false */)
  43. : m_bShortTags(bShortTags), m_bAspTags(bAspTags), m_files(4000),
  44. m_lineCount(0), m_charCount(0) {
  45. m_root = root;
  46. if (!m_root.empty() && m_root[m_root.size() - 1] != '/') m_root += "/";
  47. m_ar = AnalysisResultPtr(new AnalysisResult());
  48. m_fileCache = FileCachePtr(new FileCache());
  49. }
  50. void Package::addAllFiles(bool force) {
  51. if (Option::PackageDirectories.empty() && Option::PackageFiles.empty()) {
  52. addDirectory("/", force);
  53. } else {
  54. for (set<string>::const_iterator iter = Option::PackageDirectories.begin();
  55. iter != Option::PackageDirectories.end(); ++iter) {
  56. addDirectory(*iter, force);
  57. }
  58. for (set<string>::const_iterator iter = Option::PackageFiles.begin();
  59. iter != Option::PackageFiles.end(); ++iter) {
  60. addSourceFile((*iter).c_str());
  61. }
  62. }
  63. }
  64. void Package::addSourceFile(const char *fileName) {
  65. ASSERT(fileName && *fileName);
  66. m_files.add(Util::canonicalize(fileName).c_str());
  67. }
  68. void Package::addListFiles(const char *listFileName) {
  69. ASSERT(listFileName && *listFileName);
  70. FILE *f = fopen(listFileName, "r");
  71. if (f == NULL) {
  72. throw Exception("Unable to open %s: %s", listFileName,
  73. Util::safe_strerror(errno).c_str());
  74. }
  75. char fileName[PATH_MAX];
  76. while (fgets(fileName, sizeof(fileName), f)) {
  77. int len = strlen(fileName);
  78. if (fileName[len - 1] == '\n') fileName[len-1] = '\0';
  79. addSourceFile(fileName);
  80. }
  81. fclose(f);
  82. }
  83. void Package::addStaticFile(const char *fileName) {
  84. ASSERT(fileName && *fileName);
  85. m_extraStaticFiles.insert(fileName);
  86. }
  87. void Package::addStaticDirectory(const std::string path) {
  88. m_staticDirectories.insert(path);
  89. }
  90. void Package::addDirectory(const std::string &path, bool force) {
  91. addDirectory(path.c_str(), force);
  92. }
  93. void Package::addDirectory(const char *path, bool force) {
  94. m_directories.insert(path);
  95. addDirectory(path, "*.php", force);
  96. #ifdef HAVE_PHPT
  97. addDirectory(path, "*.phpt", force);
  98. #endif
  99. addDirectory(path, "", force); // look for PHP files without postfix
  100. if (m_hookHandler) {
  101. m_hookHandler(this, path, onPackageAddDirectory);
  102. }
  103. }
  104. void Package::addDependencyParents(const char *path, const char *postfix,
  105. DependencyGraph::KindOf kindOf) {
  106. vector<string> files;
  107. findFiles(files, path, postfix);
  108. DependencyGraphPtr dep = m_ar->getDependencyGraph();
  109. int rootSize = m_root.size();
  110. for (unsigned int i = 0; i < files.size(); i++) {
  111. const string &file = files[i];
  112. ASSERT(file.substr(0, rootSize) == m_root);
  113. dep->addParent(kindOf, "", file.substr(rootSize), ConstructPtr());
  114. }
  115. }
  116. void Package::findFiles(std::vector<std::string> &out, const char *path,
  117. const char *postfix) {
  118. ASSERT(postfix && *postfix);
  119. if (!path) path = "";
  120. if (*path == '/') path++;
  121. string fullPath = m_root + path;
  122. const char *argv[] = {"", "-L", (char*)fullPath.c_str(),
  123. "-name", (char*)postfix, NULL};
  124. string files;
  125. Process::Exec("find", argv, NULL, files);
  126. Util::split('\n', files.c_str(), out, true);
  127. }
  128. void Package::findPHPFiles(std::vector<std::string> &out, const char *path) {
  129. if (!path) path = "";
  130. if (*path == '/') path++;
  131. string fullPath = m_root + path;
  132. const char *argv[] = {"", "-L", (char*)fullPath.c_str(),
  133. "-type", "f",
  134. "-regex", ".*/[A-Za-z0-9_\\-]+",
  135. "-not", "-regex", ".*/\\.svn/.*",
  136. "-not", "-regex", ".*/\\.git/.*",
  137. /* Do not use [A-Z] below. That seems to be
  138. broken outside of the C locale, and we dont
  139. know what locale find will run in */
  140. "-not", "-regex", ".*/[ABCDEFGHIJKLMNOPQRSTUVWXYZ]+",
  141. "-not", "-regex", ".*/tags",
  142. "-exec", "perl", "-n", "-e",
  143. "/php\\s*$/ && print $ARGV.\"\\n\"; exit",
  144. "{}", ";",
  145. NULL};
  146. string files;
  147. Process::Exec("find", argv, NULL, files);
  148. Util::split('\n', files.c_str(), out, true);
  149. }
  150. void Package::findNonPHPFiles(vector<string> &out, const char *path,
  151. bool exclude) {
  152. if (!path) path = "";
  153. if (*path == '/') path++;
  154. string fullPath = m_root + path;
  155. DIR *dir = opendir(fullPath.c_str());
  156. if (dir == NULL) {
  157. Logger::Error("findNonPHPFiles: unable to open directory %s",
  158. fullPath.c_str());
  159. return;
  160. }
  161. dirent *e;
  162. while (e = readdir(dir)) {
  163. char *ename = e->d_name;
  164. if (strcmp(ename, ".") == 0 || strcmp(ename, "..") == 0) {
  165. continue;
  166. }
  167. string fe = fullPath +
  168. (fullPath[fullPath.length() - 1] != '/' ? "/" : "") + ename;
  169. struct stat se;
  170. if (stat(fe.c_str(), &se) != 0) {
  171. // unable to stat, maybe a broken symbolic link
  172. // do not include it
  173. continue;
  174. }
  175. if ((se.st_mode & S_IFMT) == S_IFDIR) {
  176. if (strcmp(ename, ".svn") && strcmp(ename, ".git")) {
  177. string subdir = path;
  178. if (subdir[subdir.length() - 1] != '/') subdir += '/';
  179. subdir += ename;
  180. findNonPHPFiles(out, subdir.c_str(), exclude);
  181. }
  182. continue;
  183. }
  184. if (strcmp(ename, "tags") == 0) {
  185. continue;
  186. }
  187. size_t len = strlen(ename);
  188. if (len >= 4 && ename[len - 4] == '.' && ename[len - 3] == 'p' &&
  189. ename[len - 2] == 'h' && ename[len - 1] == 'p') {
  190. // ending with .php
  191. continue;
  192. }
  193. #ifdef HAVE_PHPT
  194. if (len >= 5 && ename[len - 5] == '.' && ename[len - 4] == 'p' &&
  195. ename[len - 3] == 'h' && ename[len - 2] == 'p' &&
  196. ename[len - 1] == 't') {
  197. // ending with .phpt
  198. continue;
  199. }
  200. #endif
  201. string fullname = string(path) + "/" + ename;
  202. if (!exclude ||
  203. Option::PackageExcludeStaticFiles.find(fullname) ==
  204. Option::PackageExcludeStaticFiles.end()) {
  205. out.push_back(fe);
  206. }
  207. }
  208. closedir(dir);
  209. }
  210. void Package::addDirectory(const char *path, const char *postfix, bool force) {
  211. ASSERT(path && *path);
  212. if (*path == '/') path++;
  213. vector<string> files;
  214. if (postfix && *postfix) {
  215. findFiles(files, path, postfix);
  216. } else {
  217. findPHPFiles(files, path);
  218. }
  219. for (unsigned int i = 0; i < files.size(); i++) {
  220. const string &file = files[i];
  221. bool excluded = false;
  222. if (!force) {
  223. for (set<string>::const_iterator iter =
  224. Option::PackageExcludeDirs.begin();
  225. iter != Option::PackageExcludeDirs.end(); ++iter) {
  226. if (file.find(*iter) == Option::RootDirectory.size()) {
  227. excluded = true;
  228. break;
  229. }
  230. }
  231. }
  232. if (!excluded) {
  233. ASSERT(file.substr(0, m_root.size()) == m_root);
  234. string name = file.substr(m_root.size());
  235. if (Option::PackageExcludeFiles.find(name) ==
  236. Option::PackageExcludeFiles.end()) {
  237. m_files.add(name.c_str());
  238. }
  239. }
  240. }
  241. }
  242. void Package::getFiles(std::vector<std::string> &files) const {
  243. files.clear();
  244. files.reserve(m_files.size());
  245. for (unsigned int i = 0; i < m_files.size(); i++) {
  246. const char *fileName = m_files.at(i);
  247. files.push_back(fileName);
  248. }
  249. }
  250. FileCachePtr Package::getFileCache() {
  251. for (set<string>::const_iterator iter = m_directories.begin();
  252. iter != m_directories.end(); ++iter) {
  253. vector<string> files;
  254. findNonPHPFiles(files, iter->c_str(), true);
  255. for (unsigned int i = 0; i < files.size(); i++) {
  256. string &file = files[i];
  257. string rpath = file.substr(m_root.size());
  258. if (!m_fileCache->fileExists(rpath.c_str())) {
  259. Logger::Verbose("saving %s", file.c_str());
  260. m_fileCache->write(rpath.c_str(), file.c_str());
  261. }
  262. }
  263. }
  264. for (set<string>::const_iterator iter = m_staticDirectories.begin();
  265. iter != m_staticDirectories.end(); ++iter) {
  266. vector<string> files;
  267. findNonPHPFiles(files, iter->c_str(), false);
  268. for (unsigned int i = 0; i < files.size(); i++) {
  269. string &file = files[i];
  270. string rpath = file.substr(m_root.size());
  271. if (!m_fileCache->fileExists(rpath.c_str())) {
  272. Logger::Verbose("saving %s", file.c_str());
  273. m_fileCache->write(rpath.c_str(), file.c_str());
  274. }
  275. }
  276. }
  277. for (set<string>::const_iterator iter = m_extraStaticFiles.begin();
  278. iter != m_extraStaticFiles.end(); ++iter) {
  279. const char *file = iter->c_str();
  280. if (!m_fileCache->fileExists(file)) {
  281. string fullpath = m_root + file;
  282. Logger::Verbose("saving %s", fullpath.c_str());
  283. m_fileCache->write(file, fullpath.c_str());
  284. }
  285. }
  286. return m_fileCache;
  287. }
  288. ///////////////////////////////////////////////////////////////////////////////
  289. bool Package::parse() {
  290. hphp_const_char_set files;
  291. for (unsigned int i = 0; i < m_files.size(); i++) {
  292. const char *fileName = m_files.at(i);
  293. if (files.find(fileName) == files.end()) {
  294. files.insert(fileName);
  295. if (!parseImpl(fileName)) return false;
  296. }
  297. }
  298. return true;
  299. }
  300. bool Package::parse(const char *fileName) {
  301. return parseImpl(m_files.add(fileName));
  302. }
  303. bool Package::parseImpl(const char *fileName) {
  304. ASSERT(fileName);
  305. if (fileName[0] == 0) return false;
  306. string fullPath;
  307. if (fileName[0] == '/') {
  308. fullPath = fileName;
  309. } else {
  310. fullPath = m_root + fileName;
  311. }
  312. struct stat sb;
  313. if (stat(fullPath.c_str(), &sb)) {
  314. Logger::Error("Unable to stat file %s", fullPath.c_str());
  315. return false;
  316. }
  317. try {
  318. ifstream f(fullPath.c_str());
  319. stringstream ss;
  320. istream *is = Option::EnableXHP ? preprocessXHP(f, ss, fullPath) : &f;
  321. Scanner scanner(new ylmm::basic_buffer(*is, false, true),
  322. m_bShortTags, m_bAspTags);
  323. Logger::Verbose("parsing %s ...", fullPath.c_str());
  324. ParserPtr parser(new Parser(scanner, fileName, sb.st_size, m_ar));
  325. if (parser->parse()) {
  326. throw Exception("Unable to parse file: %s\n%s", fullPath.c_str(),
  327. parser->getMessage().c_str());
  328. }
  329. m_lineCount += parser->line1();
  330. struct stat fst;
  331. stat(fullPath.c_str(), &fst);
  332. m_charCount += fst.st_size;
  333. } catch (std::runtime_error) {
  334. Logger::Error("Unable to open file %s", fullPath.c_str());
  335. return false;
  336. }
  337. if (!m_fileCache->fileExists(fileName) &&
  338. m_extraStaticFiles.find(fileName) == m_extraStaticFiles.end()) {
  339. if (Option::CachePHPFile) {
  340. m_fileCache->write(fileName, fullPath.c_str()); // name + content
  341. } else {
  342. m_fileCache->write(fileName); // just name, without content
  343. }
  344. }
  345. return true;
  346. }
  347. ///////////////////////////////////////////////////////////////////////////////
  348. void Package::saveStatsToFile(const char *filename, int totalSeconds) const {
  349. ofstream f(filename);
  350. if (f) {
  351. JSON::OutputStream o(f);
  352. JSON::MapStream ms(o);
  353. ms.add("FileCount", getFileCount())
  354. .add("LineCount", getLineCount())
  355. .add("CharCount", getCharCount())
  356. .add("FunctionCount", m_ar->getFunctionCount())
  357. .add("ClassCount", m_ar->getClassCount())
  358. .add("TotalTime", totalSeconds);
  359. if (getLineCount()) {
  360. ms.add("AvgCharPerLine", getCharCount() / getLineCount());
  361. }
  362. if (m_ar->getFunctionCount()) {
  363. ms.add("AvgLinePerFunc", getLineCount()/m_ar->getFunctionCount());
  364. }
  365. std::map<std::string, int> counts;
  366. SymbolTable::CountTypes(counts);
  367. m_ar->countReturnTypes(counts);
  368. ms.add("SymbolTypes");
  369. o << counts;
  370. ms.add("VariableTableFunctions");
  371. JSON::ListStream ls(o);
  372. BOOST_FOREACH(const std::string &f, m_ar->m_variableTableFunctions) {
  373. ls << f;
  374. }
  375. ls.done();
  376. ms.done();
  377. f.close();
  378. }
  379. }
  380. int Package::saveStatsToDB(ServerDataPtr server, int totalSeconds,
  381. const std::string &branch, int revision) const {
  382. std::map<std::string, int> counts;
  383. SymbolTable::CountTypes(counts);
  384. m_ar->countReturnTypes(counts);
  385. ostringstream sout;
  386. JSON::OutputStream o(sout);
  387. o << counts;
  388. DBConn conn;
  389. conn.open(server);
  390. const char *sql = "INSERT INTO hphp_run (branch, revision, file, line, "
  391. "byte, program, function, class, types, time)";
  392. DBQuery q(&conn, sql);
  393. q.insert("'%s', %d, %d, %d, %d, %d, %d, %d, '%s', %d",
  394. branch.c_str(), revision,
  395. getFileCount(), getLineCount(), getCharCount(),
  396. 1, m_ar->getFunctionCount(),
  397. m_ar->getClassCount(), sout.str().c_str(), totalSeconds);
  398. q.execute();
  399. return conn.getLastInsertId();
  400. }
  401. void Package::commitStats(ServerDataPtr server, int runId) const {
  402. DBConn conn;
  403. conn.open(server);
  404. {
  405. DBQuery q(&conn, "UPDATE hphp_dep");
  406. q.setField("parent_file = parent");
  407. q.filterBy("run = %d", runId);
  408. q.filterBy("kind IN ('PHPInclude', 'PHPTemplate')");
  409. q.execute();
  410. }
  411. {
  412. DBQuery q(&conn, "UPDATE hphp_run");
  413. q.setField("committed = 1");
  414. q.filterBy("id = %d", runId);
  415. q.execute();
  416. }
  417. }