Split try-fault into two instructions, like try-catch
[hiphop-php.git] / hphp / compiler / compiler.cpp
blob6b9a6ddc8d1b8de695995e06209a349cf37f3e40
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 +----------------------------------------------------------------------+
17 #include "hphp/compiler/compiler.h"
19 #include "hphp/compiler/analysis/analysis_result.h"
20 #include "hphp/compiler/analysis/code_error.h"
21 #include "hphp/compiler/analysis/emitter.h"
22 #include "hphp/compiler/analysis/symbol_table.h"
23 #include "hphp/compiler/builtin_symbols.h"
24 #include "hphp/compiler/json.h"
25 #include "hphp/compiler/option.h"
26 #include "hphp/compiler/package.h"
27 #include "hphp/compiler/parser/parser.h"
29 #include "hphp/hhbbc/hhbbc.h"
30 #include "hphp/runtime/base/config.h"
31 #include "hphp/runtime/base/externals.h"
32 #include "hphp/runtime/base/file-util.h"
33 #include "hphp/runtime/base/ini-setting.h"
34 #include "hphp/runtime/base/program-functions.h"
35 #include "hphp/runtime/vm/repo.h"
36 #include "hphp/system/systemlib.h"
38 #include "hphp/util/async-func.h"
39 #include "hphp/util/build-info.h"
40 #include "hphp/util/current-executable.h"
41 #include "hphp/util/exception.h"
42 #include "hphp/util/hdf.h"
43 #include "hphp/util/logger.h"
44 #include "hphp/util/process.h"
45 #include "hphp/util/process-exec.h"
46 #include "hphp/util/text-util.h"
47 #include "hphp/util/timer.h"
49 #include "hphp/hhvm/process-init.h"
51 #include <sys/types.h>
52 #ifndef _MSC_VER
53 #include <sys/wait.h>
54 #include <dlfcn.h>
55 #endif
57 #include <boost/algorithm/string/replace.hpp>
58 #include <boost/program_options/options_description.hpp>
59 #include <boost/program_options/positional_options.hpp>
60 #include <boost/program_options/variables_map.hpp>
61 #include <boost/program_options/parsers.hpp>
62 #include <boost/filesystem.hpp>
64 #include <exception>
66 #include <folly/portability/SysStat.h>
68 using namespace boost::program_options;
69 using std::cout;
71 namespace HPHP {
72 ///////////////////////////////////////////////////////////////////////////////
74 struct CompilerOptions {
75 std::string target;
76 std::string format;
77 std::string outputDir;
78 std::string syncDir;
79 std::vector<std::string> config;
80 std::string configDir;
81 std::vector<std::string> confStrings;
82 std::vector<std::string> iniStrings;
83 std::string inputDir;
84 std::vector<std::string> inputs;
85 std::string inputList;
86 std::vector<std::string> includePaths;
87 std::vector<std::string> modules;
88 std::vector<std::string> excludeDirs;
89 std::vector<std::string> excludeFiles;
90 std::vector<std::string> excludePatterns;
91 std::vector<std::string> excludeStaticDirs;
92 std::vector<std::string> excludeStaticFiles;
93 std::vector<std::string> excludeStaticPatterns;
94 std::vector<std::string> fmodules;
95 std::vector<std::string> ffiles;
96 std::vector<std::string> cfiles;
97 std::vector<std::string> cmodules;
98 bool parseOnDemand;
99 std::string program;
100 std::string programArgs;
101 std::string branch;
102 int revision;
103 bool genStats;
104 bool keepTempDir;
105 int logLevel;
106 bool force;
107 int optimizeLevel;
108 std::string filecache;
109 bool dump;
110 bool coredump;
111 bool nofork;
114 ///////////////////////////////////////////////////////////////////////////////
116 struct AsyncFileCacheSaver : AsyncFunc<AsyncFileCacheSaver> {
117 AsyncFileCacheSaver(Package *package, const char *name)
118 : AsyncFunc<AsyncFileCacheSaver>(this, &AsyncFileCacheSaver::saveCache),
119 m_package(package), m_name(name) {
122 void saveCache() {
123 Timer timer(Timer::WallTime, "saving file cache...");
124 m_package->getFileCache()->save(m_name);
126 struct stat sb;
127 stat(m_name, &sb);
128 Logger::Info("%" PRId64" MB %s saved",
129 (int64_t)sb.st_size/(1024*1024), m_name);
132 private:
133 Package *m_package;
134 const char *m_name;
137 ///////////////////////////////////////////////////////////////////////////////
138 // forward declarations
140 int prepareOptions(CompilerOptions &po, int argc, char **argv);
141 void createOutputDirectory(CompilerOptions &po);
142 int process(const CompilerOptions &po);
143 int lintTarget(const CompilerOptions &po);
144 int phpTarget(const CompilerOptions &po, AnalysisResultPtr ar);
145 void hhbcTargetInit(const CompilerOptions &po, AnalysisResultPtr ar);
146 int hhbcTarget(const CompilerOptions &po, AnalysisResultPtr&& ar,
147 AsyncFileCacheSaver &fcThread);
148 int runTargetCheck(const CompilerOptions &po, AnalysisResultPtr&& ar,
149 AsyncFileCacheSaver &fcThread);
150 int runTarget(const CompilerOptions &po);
151 void pcre_init();
153 ///////////////////////////////////////////////////////////////////////////////
155 int compiler_main(int argc, char **argv) {
156 try {
157 CompilerOptions po;
159 int ret = prepareOptions(po, argc, argv);
160 if (ret == 1) return 0; // --help
161 if (ret == -1) return -1; // command line error
163 Timer totalTimer(Timer::WallTime, "running hphp");
164 createOutputDirectory(po);
165 if (ret == 0) {
166 #ifndef _MSC_VER
167 if (!po.nofork && !Process::IsUnderGDB()) {
168 int pid = fork();
169 if (pid == 0) {
170 ret = process(po);
171 _exit(ret);
173 wait(&ret);
174 ret = WIFEXITED(ret) ? WEXITSTATUS(ret) : -1;
175 } else
176 #endif
178 ret = process(po);
181 if (ret == 0) {
182 if (po.target == "run") {
183 ret = runTarget(po);
186 if (ret) {
187 Logger::Error("hphp failed");
188 } else {
189 Logger::Info("all files saved in %s ...", po.outputDir.c_str());
191 return ret;
192 } catch (Exception &e) {
193 Logger::Error("Exception: %s\n", e.getMessage().c_str());
194 } catch (std::exception &e) {
195 Logger::Error("std::exception: %s\n", e.what());
196 } catch (...) {
197 Logger::Error("(unknown exception was thrown)\n");
199 return -1;
202 ///////////////////////////////////////////////////////////////////////////////
204 int prepareOptions(CompilerOptions &po, int argc, char **argv) {
205 options_description desc("HipHop Compiler for PHP Usage:\n\n"
206 "\thphp <options> <inputs>\n\n"
207 "Options");
208 desc.add_options()
209 ("help", "display this message")
210 ("version", "display version number")
211 ("target,t", value<std::string>(&po.target)->default_value("run"),
212 "lint | "
213 "php | "
214 "hhbc | "
215 "filecache | "
216 "run (default)")
217 ("format,f", value<std::string>(&po.format),
218 "lint: (none); \n"
219 "php: trimmed (default) | inlined | pickled |"
220 " <any combination of them by any separator>; \n"
221 "hhbc: binary (default) | text; \n"
222 "run: cluster (default) | file")
223 ("input-dir", value<std::string>(&po.inputDir), "input directory")
224 ("program", value<std::string>(&po.program)->default_value("program"),
225 "final program name to use")
226 ("args", value<std::string>(&po.programArgs), "program arguments")
227 ("inputs,i", value<std::vector<std::string>>(&po.inputs),
228 "input file names")
229 ("input-list", value<std::string>(&po.inputList),
230 "file containing list of file names, one per line")
231 ("include-path",
232 value<std::vector<std::string>>(&po.includePaths)->composing(),
233 "a list of full paths to search for files being included in includes "
234 "or requires but cannot be found assuming relative paths")
235 ("module", value<std::vector<std::string>>(&po.modules)->composing(),
236 "directories containing all input files")
237 ("exclude-dir",
238 value<std::vector<std::string>>(&po.excludeDirs)->composing(),
239 "directories to exclude from the input")
240 ("fmodule", value<std::vector<std::string>>(&po.fmodules)->composing(),
241 "same with module, except no exclusion checking is performed, so these "
242 "modules are forced to be included")
243 ("ffile", value<std::vector<std::string>>(&po.ffiles)->composing(),
244 "extra PHP files forced to include without exclusion checking")
245 ("exclude-file",
246 value<std::vector<std::string>>(&po.excludeFiles)->composing(),
247 "files to exclude from the input, even if parse-on-demand finds it")
248 ("exclude-pattern",
249 value<std::vector<std::string>>(&po.excludePatterns)->composing(),
250 "regex (in 'find' command's regex command line option format) of files "
251 "or directories to exclude from the input, even if parse-on-demand finds "
252 "it")
253 ("exclude-static-pattern",
254 value<std::vector<std::string>>(&po.excludeStaticPatterns)->composing(),
255 "regex (in 'find' command's regex command line option format) of files "
256 "or directories to exclude from static content cache")
257 ("exclude-static-dir",
258 value<std::vector<std::string>>(&po.excludeStaticDirs)->composing(),
259 "directories to exclude from static content cache")
260 ("exclude-static-file",
261 value<std::vector<std::string>>(&po.excludeStaticFiles)->composing(),
262 "files to exclude from static content cache")
263 ("cfile", value<std::vector<std::string>>(&po.cfiles)->composing(),
264 "extra static files forced to include without exclusion checking")
265 ("cmodule", value<std::vector<std::string>>(&po.cmodules)->composing(),
266 "extra directories for static files without exclusion checking")
267 ("parse-on-demand", value<bool>(&po.parseOnDemand)->default_value(true),
268 "whether to parse files that are not specified from command line")
269 ("branch", value<std::string>(&po.branch), "SVN branch")
270 ("revision", value<int>(&po.revision), "SVN revision")
271 ("output-dir,o", value<std::string>(&po.outputDir), "output directory")
272 ("sync-dir", value<std::string>(&po.syncDir),
273 "Files will be created in this directory first, then sync with output "
274 "directory without overwriting identical files. Great for incremental "
275 "compilation and build.")
276 ("optimize-level", value<int>(&po.optimizeLevel)->default_value(-1),
277 "optimization level")
278 ("gen-stats", value<bool>(&po.genStats)->default_value(false),
279 "whether to generate code errors")
280 ("keep-tempdir,k", value<bool>(&po.keepTempDir)->default_value(false),
281 "whether to keep the temporary directory")
282 ("config,c", value<std::vector<std::string>>(&po.config)->composing(),
283 "config file name")
284 ("config-dir", value<std::string>(&po.configDir),
285 "root directory configuration is based on (for example, "
286 "excluded directories may be relative path in configuration.")
287 ("config-value,v",
288 value<std::vector<std::string>>(&po.confStrings)->composing(),
289 "individual configuration string in a format of name=value, where "
290 "name can be any valid configuration for a config file")
291 ("define,d", value<std::vector<std::string>>(&po.iniStrings)->composing(),
292 "define an ini setting in the same format ( foo[=bar] ) as provided in a "
293 ".ini file")
294 ("log,l",
295 value<int>(&po.logLevel)->default_value(-1),
296 "-1: (default); 0: no logging; 1: errors only; 2: warnings and errors; "
297 "3: informational as well; 4: really verbose.")
298 ("force",
299 value<bool>(&po.force)->default_value(true),
300 "force to ignore code generation errors and continue compilations")
301 ("file-cache",
302 value<std::string>(&po.filecache),
303 "if specified, generate a static file cache with this file name")
304 ("dump",
305 value<bool>(&po.dump)->default_value(false),
306 "dump the program graph")
307 ("coredump",
308 value<bool>(&po.coredump)->default_value(false),
309 "turn on coredump")
310 ("nofork",
311 value<bool>(&po.nofork)->default_value(false),
312 "forking is needed for large compilation to release memory before g++"
313 "compilation. turning off forking can help gdb debugging.")
314 ("compiler-id", "display the git hash for the compiler id")
315 ("repo-schema", "display the repo schema id used by this app")
318 positional_options_description p;
319 p.add("inputs", -1);
320 variables_map vm;
321 try {
322 auto opts = command_line_parser(argc, argv).options(desc)
323 .positional(p).run();
324 try {
325 store(opts, vm);
326 notify(vm);
327 #if defined(BOOST_VERSION) && BOOST_VERSION >= 105000 && BOOST_VERSION <= 105400
328 } catch (const error_with_option_name &e) {
329 std::string wrong_name = e.get_option_name();
330 std::string right_name = get_right_option_name(opts, wrong_name);
331 std::string message = e.what();
332 if (right_name != "") {
333 boost::replace_all(message, wrong_name, right_name);
335 Logger::Error("Error in command line: %s", message.c_str());
336 cout << desc << "\n";
337 return -1;
338 #endif
339 } catch (const error& e) {
340 Logger::Error("Error in command line: %s", e.what());
341 cout << desc << "\n";
342 return -1;
344 } catch (const unknown_option& e) {
345 Logger::Error("Error in command line: %s", e.what());
346 cout << desc << "\n";
347 return -1;
348 } catch (const error& e) {
349 Logger::Error("Error in command line: %s", e.what());
350 cout << desc << "\n";
351 return -1;
352 } catch (...) {
353 Logger::Error("Error in command line parsing.");
354 cout << desc << "\n";
355 return -1;
357 if (argc <= 1 || vm.count("help")) {
358 cout << desc << "\n";
359 return 1;
361 if (vm.count("version")) {
362 cout << "HipHop Repo Compiler";
363 cout << " " << HHVM_VERSION;
364 cout << " (" << (debug ? "dbg" : "rel") << ")\n";
365 cout << "Compiler: " << compilerId() << "\n";
366 cout << "Repo schema: " << repoSchemaId() << "\n";
367 return 1;
370 if (vm.count("compiler-id")) {
371 cout << compilerId() << "\n";
372 return 1;
375 if (vm.count("repo-schema")) {
376 cout << repoSchemaId() << "\n";
377 return 1;
380 if (po.target != "run"
381 && po.target != "lint"
382 && po.target != "php"
383 && po.target != "hhbc"
384 && po.target != "filecache") {
385 Logger::Error("Error in command line: target '%s' is not supported.",
386 po.target.c_str());
387 // desc[ription] is the --help output
388 cout << desc << "\n";
389 return -1;
392 if ((po.target == "hhbc" || po.target == "run") &&
393 po.format.find("exe") == std::string::npos) {
394 if (po.program == "program") {
395 po.program = "hhvm.hhbc";
399 // log level
400 if (po.logLevel != -1) {
401 Logger::LogLevel = (Logger::LogLevelType)po.logLevel;
402 } else if (po.target == "run") {
403 Logger::LogLevel = Logger::LogNone;
404 } else {
405 Logger::LogLevel = Logger::LogInfo;
408 MemoryManager::TlsWrapper::getCheck();
409 IniSetting::Map ini = IniSetting::Map::object;
410 Hdf config;
411 for (auto& file : po.config) {
412 Config::ParseConfigFile(file, ini, config);
414 for (unsigned int i = 0; i < po.iniStrings.size(); i++) {
415 Config::ParseIniString(po.iniStrings[i].c_str(), ini);
417 for (unsigned int i = 0; i < po.confStrings.size(); i++) {
418 Config::ParseHdfString(po.confStrings[i].c_str(), config);
420 Hdf runtime = config["Runtime"];
421 // The configuration command line strings were already processed above
422 // Don't process them again.
423 RuntimeOption::Load(ini, runtime);
424 Option::Load(ini, config);
425 RuntimeOption::EvalJit = false;
427 initialize_repo();
429 std::vector<std::string> badnodes;
430 config.lint(badnodes);
431 for (unsigned int i = 0; i < badnodes.size(); i++) {
432 Logger::Error("Possible bad config node: %s", badnodes[i].c_str());
435 // we need to initialize pcre cache table very early
436 pcre_init();
438 if (po.dump) Option::DumpAst = true;
440 if (po.inputDir.empty()) {
441 po.inputDir = '.';
443 po.inputDir = FileUtil::normalizeDir(po.inputDir);
444 if (po.configDir.empty()) {
445 po.configDir = po.inputDir;
447 po.configDir = FileUtil::normalizeDir(po.configDir);
448 Option::RootDirectory = po.configDir;
449 Option::IncludeSearchPaths = po.includePaths;
451 for (unsigned int i = 0; i < po.excludeDirs.size(); i++) {
452 Option::PackageExcludeDirs.insert
453 (FileUtil::normalizeDir(po.excludeDirs[i]));
455 for (unsigned int i = 0; i < po.excludeFiles.size(); i++) {
456 Option::PackageExcludeFiles.insert(po.excludeFiles[i]);
458 for (unsigned int i = 0; i < po.excludePatterns.size(); i++) {
459 Option::PackageExcludePatterns.insert
460 (format_pattern(po.excludePatterns[i], true));
462 for (unsigned int i = 0; i < po.excludeStaticDirs.size(); i++) {
463 Option::PackageExcludeStaticDirs.insert
464 (FileUtil::normalizeDir(po.excludeStaticDirs[i]));
466 for (unsigned int i = 0; i < po.excludeStaticFiles.size(); i++) {
467 Option::PackageExcludeStaticFiles.insert(po.excludeStaticFiles[i]);
469 for (unsigned int i = 0; i < po.excludeStaticPatterns.size(); i++) {
470 Option::PackageExcludeStaticPatterns.insert
471 (format_pattern(po.excludeStaticPatterns[i], true));
474 Option::ProgramName = po.program;
476 if (po.format.empty()) {
477 if (po.target == "php") {
478 po.format = "trimmed";
479 } else if (po.target == "run") {
480 po.format = "binary";
481 } else if (po.target == "hhbc") {
482 po.format = "binary";
486 if (po.optimizeLevel == -1) {
487 po.optimizeLevel = 1;
490 // we always do pre/post opt no matter the opt level
491 Option::PreOptimization = true;
492 if (po.optimizeLevel == 0) {
493 // --optimize-level=0 is equivalent to --opts=none
494 Option::ParseTimeOpts = false;
497 initialize_hhbbc_options();
499 return 0;
502 ///////////////////////////////////////////////////////////////////////////////
504 int process(const CompilerOptions &po) {
505 if (po.coredump) {
506 #ifdef _MSC_VER
508 * Windows actually does core dump size and control at a system, not an app
509 * level. So we do nothing here and are at the mercy of Dr. Watson.
511 #elif defined(__APPLE__) || defined(__FreeBSD__)
512 struct rlimit rl;
513 getrlimit(RLIMIT_CORE, &rl);
514 rl.rlim_cur = 80000000LL;
515 if (rl.rlim_max < rl.rlim_cur) {
516 rl.rlim_max = rl.rlim_cur;
518 setrlimit(RLIMIT_CORE, &rl);
519 #else
520 struct rlimit64 rl;
521 getrlimit64(RLIMIT_CORE, &rl);
522 rl.rlim_cur = 8000000000LL;
523 if (rl.rlim_max < rl.rlim_cur) {
524 rl.rlim_max = rl.rlim_cur;
526 setrlimit64(RLIMIT_CORE, &rl);
527 #endif
530 // lint doesn't need analysis
531 if (po.target == "lint") {
532 return lintTarget(po);
535 register_process_init();
537 Timer timer(Timer::WallTime);
538 // prepare a package
539 Package package(po.inputDir.c_str());
540 AnalysisResultPtr ar = package.getAnalysisResult();
542 hhbcTargetInit(po, ar);
544 // one time initialization
545 BuiltinSymbols::LoadSuperGlobals();
547 bool processInitRan = false;
548 SCOPE_EXIT {
549 if (processInitRan) {
550 hphp_process_exit();
554 bool isPickledPHP = (po.target == "php" && po.format == "pickled");
555 if (!isPickledPHP) {
556 bool wp = Option::WholeProgram;
557 Option::WholeProgram = false;
558 BuiltinSymbols::s_systemAr = ar;
559 hphp_process_init();
560 processInitRan = true;
561 BuiltinSymbols::s_systemAr.reset();
562 Option::WholeProgram = wp;
563 if (po.target == "hhbc" && !Option::WholeProgram) {
564 // We're trying to produce the same bytecode as runtime parsing.
565 // There's nothing to do.
566 } else {
567 if (!BuiltinSymbols::Load(ar)) {
568 return false;
571 } else {
572 hphp_process_init();
573 processInitRan = true;
577 Timer timer2(Timer::WallTime, "parsing inputs");
578 if (!po.inputs.empty() && isPickledPHP) {
579 for (unsigned int i = 0; i < po.inputs.size(); i++) {
580 package.addSourceFile(po.inputs[i].c_str());
582 } else {
583 ar->setPackage(&package);
584 ar->setParseOnDemand(po.parseOnDemand);
585 if (!po.parseOnDemand) {
586 ar->setParseOnDemandDirs(Option::ParseOnDemandDirs);
588 if (po.modules.empty() && po.fmodules.empty() &&
589 po.ffiles.empty() && po.inputs.empty() && po.inputList.empty()) {
590 package.addAllFiles(false);
591 } else {
592 for (unsigned int i = 0; i < po.modules.size(); i++) {
593 package.addDirectory(po.modules[i], false);
595 for (unsigned int i = 0; i < po.fmodules.size(); i++) {
596 package.addDirectory(po.fmodules[i], true);
598 for (unsigned int i = 0; i < po.ffiles.size(); i++) {
599 package.addSourceFile(po.ffiles[i].c_str());
601 for (unsigned int i = 0; i < po.cmodules.size(); i++) {
602 package.addStaticDirectory(po.cmodules[i].c_str());
604 for (unsigned int i = 0; i < po.cfiles.size(); i++) {
605 package.addStaticFile(po.cfiles[i].c_str());
607 for (unsigned int i = 0; i < po.inputs.size(); i++) {
608 package.addSourceFile(po.inputs[i].c_str());
610 if (!po.inputList.empty()) {
611 package.addInputList(po.inputList.c_str());
615 if (po.target != "filecache") {
616 if (!package.parse(!po.force)) {
617 return 1;
619 if (Option::WholeProgram) {
620 Timer timer3(Timer::WallTime, "analyzeProgram");
621 ar->analyzeProgram();
626 // saving file cache
627 AsyncFileCacheSaver fileCacheThread(&package, po.filecache.c_str());
628 if (!po.filecache.empty()) {
629 fileCacheThread.start();
632 if (Option::DumpAst) {
633 ar->dump();
636 ar->setFinish([&po,&timer,&package](AnalysisResultPtr res) {
637 if (Option::DumpAst) {
638 res->dump();
641 // saving stats
642 if (po.genStats) {
643 int seconds = timer.getMicroSeconds() / 1000000;
645 Logger::Info("saving code errors and stats...");
646 Timer timer(Timer::WallTime, "saving stats");
647 package.saveStatsToFile((po.outputDir + "/Stats.js").c_str(), seconds);
649 package.resetAr();
652 int ret = 0;
653 if (po.target == "php") {
654 ret = phpTarget(po, ar);
655 } else if (po.target == "hhbc") {
656 ret = hhbcTarget(po, std::move(ar), fileCacheThread);
657 } else if (po.target == "run") {
658 ret = runTargetCheck(po, std::move(ar), fileCacheThread);
659 } else if (po.target == "filecache") {
660 // do nothing
661 } else {
662 Logger::Error("Unknown target: %s", po.target.c_str());
663 return 1;
666 if (!po.filecache.empty()) {
667 fileCacheThread.waitForEnd();
669 return ret;
672 ///////////////////////////////////////////////////////////////////////////////
674 int lintTarget(const CompilerOptions &po) {
675 int ret = 0;
676 for (unsigned int i = 0; i < po.inputs.size(); i++) {
677 std::string filename = po.inputDir + "/" + po.inputs[i];
678 try {
679 Scanner scanner(filename, Option::GetScannerType());
680 Compiler::Parser parser(scanner, filename.c_str(),
681 std::make_shared<AnalysisResult>());
682 if (!parser.parse()) {
683 Logger::Error("Unable to parse file %s: %s", filename.c_str(),
684 parser.getMessage().c_str());
685 ret = 1;
686 } else {
687 Logger::Info("%s parsed successfully...", filename.c_str());
689 } catch (FileOpenException &e) {
690 Logger::Error("%s", e.getMessage().c_str());
691 ret = 1;
694 return ret;
697 ///////////////////////////////////////////////////////////////////////////////
699 static void wholeProgramPasses(const CompilerOptions& po,
700 AnalysisResultPtr ar) {
701 if (Option::PreOptimization) {
702 Timer timer(Timer::WallTime, "pre-optimizing");
703 ar->preOptimize();
707 ///////////////////////////////////////////////////////////////////////////////
709 int phpTarget(const CompilerOptions &po, AnalysisResultPtr ar) {
710 int ret = 0;
712 // format
713 int formatCount = 0;
714 if (po.format.find("pickled") != std::string::npos) {
715 Option::GeneratePickledPHP = true;
716 formatCount++;
718 if (po.format.find("inlined") != std::string::npos) {
719 Option::GenerateInlinedPHP = true;
720 formatCount++;
722 if (po.format.find("trimmed") != std::string::npos) {
723 Option::GenerateTrimmedPHP = true;
724 formatCount++;
726 if (formatCount == 0) {
727 Logger::Error("Unknown format for PHP target: %s", po.format.c_str());
728 return 1;
731 // generate
732 ar->setOutputPath(po.outputDir);
733 if (Option::GeneratePickledPHP) {
734 Logger::Info("creating pickled PHP files...");
735 std::string outputDir = po.outputDir;
736 if (formatCount > 1) outputDir += "/pickled";
737 mkdir(outputDir.c_str(), 0777);
738 ar->outputAllPHP(CodeGenerator::PickledPHP);
740 if (Option::GenerateInlinedPHP) {
741 Logger::Info("creating inlined PHP files...");
742 std::string outputDir = po.outputDir;
743 if (formatCount > 1) outputDir += "/inlined";
744 mkdir(outputDir.c_str(), 0777);
745 if (!ar->outputAllPHP(CodeGenerator::InlinedPHP)) {
746 ret = -1;
749 if (Option::GenerateTrimmedPHP) {
750 Logger::Info("creating trimmed PHP files...");
751 std::string outputDir = po.outputDir;
752 if (formatCount > 1) outputDir += "/trimmed";
753 mkdir(outputDir.c_str(), 0777);
754 if (!ar->outputAllPHP(CodeGenerator::TrimmedPHP)) {
755 ret = -1;
759 return ret;
762 ///////////////////////////////////////////////////////////////////////////////
764 void hhbcTargetInit(const CompilerOptions &po, AnalysisResultPtr ar) {
765 if (po.syncDir.empty()) {
766 ar->setOutputPath(po.outputDir);
767 } else {
768 ar->setOutputPath(po.syncDir);
770 // Propagate relevant compiler-specific options to the runtime.
771 RuntimeOption::RepoCentralPath = ar->getOutputPath() + '/' + po.program;
772 if (po.format.find("exe") != std::string::npos) {
773 RuntimeOption::RepoCentralPath += ".hhbc";
775 unlink(RuntimeOption::RepoCentralPath.c_str());
776 RuntimeOption::RepoLocalMode = "--";
777 RuntimeOption::RepoDebugInfo = Option::RepoDebugInfo;
778 RuntimeOption::RepoJournal = "memory";
779 if (HHBBC::options.HardReturnTypeHints) {
780 RuntimeOption::EvalCheckReturnTypeHints = 3;
783 // Turn off commits, because we don't want systemlib to get included
784 RuntimeOption::RepoCommit = false;
787 int hhbcTarget(const CompilerOptions &po, AnalysisResultPtr&& ar,
788 AsyncFileCacheSaver &fcThread) {
789 int ret = 0;
790 int formatCount = 0;
791 const char *type = 0;
792 if (po.format.find("text") != std::string::npos) {
793 Option::GenerateTextHHBC = true;
794 type = "creating text HHBC files";
795 formatCount++;
797 if (po.format.find("binary") != std::string::npos) {
798 Option::GenerateBinaryHHBC = true;
799 type = "creating binary HHBC files";
800 formatCount++;
802 if (po.format.find("exe") != std::string::npos) {
803 Option::GenerateBinaryHHBC = true;
804 type = "creating binary HHBC files";
805 formatCount++;
808 if (formatCount == 0) {
809 Logger::Error("Unknown format for HHBC target: %s", po.format.c_str());
810 return 1;
813 /* without this, emitClass allows classes with interfaces to be
814 hoistable */
815 SystemLib::s_inited = true;
816 RuntimeOption::RepoCommit = true;
818 if (po.optimizeLevel > 0) {
819 ret = 0;
820 wholeProgramPasses(po, ar);
821 ar->analyzeProgramFinal();
824 Timer timer(Timer::WallTime, type);
825 // NOTE: Repo errors are ignored!
826 Compiler::emitAllHHBC(std::move(ar));
828 if (!po.syncDir.empty()) {
829 if (!po.filecache.empty()) {
830 fcThread.waitForEnd();
832 FileUtil::syncdir(po.outputDir, po.syncDir);
833 boost::filesystem::remove_all(po.syncDir);
836 if (!ret && po.format.find("exe") != std::string::npos) {
838 * We need to create an executable with the repo
839 * embedded in it.
840 * Copy ourself, and embed the repo as a section
841 * named "repo".
843 std::string exe = po.outputDir + '/' + po.program;
844 std::string repo = "repo=" + exe + ".hhbc";
845 std::string buf = current_executable_path();
846 if (buf.empty()) return -1;
848 const char *argv[] = { "objcopy", "--add-section", repo.c_str(),
849 buf.c_str(), exe.c_str(), 0 };
850 std::string out;
851 ret = proc::exec(argv[0], argv, nullptr, out, nullptr) ? 0 : 1;
854 return ret;
857 ///////////////////////////////////////////////////////////////////////////////
859 int runTargetCheck(const CompilerOptions &po, AnalysisResultPtr&& ar,
860 AsyncFileCacheSaver &fcThread) {
861 // generate code
862 if (hhbcTarget(po, std::move(ar), fcThread)) {
863 return 1;
866 // check error
867 if (Compiler::HasError() && !po.force) {
868 Compiler::DumpErrors(ar);
869 return 1;
872 return 0;
875 int runTarget(const CompilerOptions &po) {
876 int ret = 0;
878 // If there are more than one input files, we need one extra arg to run.
879 // If it's missing, we will stop right here, with compiled code.
880 if ((po.inputs.size() != 1 && po.programArgs.empty()) ||
881 !po.inputList.empty()) {
882 return 0;
885 // run the executable
886 std::string cmd;
887 if (po.format.find("exe") == std::string::npos) {
888 std::string buf = current_executable_path();
889 if (buf.empty()) return -1;
891 cmd += buf;
892 cmd += " -vRepo.Authoritative=true";
893 if (getenv("HPHP_DUMP_BYTECODE")) cmd += " -vEval.DumpBytecode=1";
894 if (getenv("HPHP_INTERP")) cmd += " -vEval.Jit=0";
895 cmd += " -vRepo.Local.Mode=r- -vRepo.Local.Path=";
897 cmd += po.outputDir + '/' + po.program;
898 cmd += std::string(" --file ") +
899 (po.inputs.size() == 1 ? po.inputs[0] : "") +
900 " " + po.programArgs;
901 Logger::Info("running executable: %s", cmd.c_str());
902 ret = FileUtil::ssystem(cmd.c_str());
903 if (ret && ret != -1) ret = 1;
905 // delete the temporary directory if not needed
906 if (!po.keepTempDir) {
907 Logger::Info("deleting temporary directory %s...", po.outputDir.c_str());
908 boost::filesystem::remove_all(po.outputDir);
910 return ret;
913 void createOutputDirectory(CompilerOptions &po) {
914 if (po.outputDir.empty()) {
915 const char *t = getenv("TEMP");
916 if (!t) {
917 t = "/tmp";
919 std::string temp = t;
920 temp += "/hphp_XXXXXX";
921 std::vector<char> path(begin(temp), end(temp));
922 path.push_back('\0');
923 po.outputDir = mkdtemp(&path[0]);
924 Logger::Info("creating temporary directory %s ...", po.outputDir.c_str());
926 mkdir(po.outputDir.c_str(), 0777);
928 if (!po.syncDir.empty()) {
929 Logger::Info("re-creating sync directory %s ...", po.syncDir.c_str());
930 boost::filesystem::remove_all(po.syncDir);
931 mkdir(po.syncDir.c_str(), 0777);
935 ///////////////////////////////////////////////////////////////////////////////