/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "util/repo_schema.h" #include "hhvm/process_init.h" using namespace boost::program_options; using std::cout; namespace HPHP { /////////////////////////////////////////////////////////////////////////////// struct CompilerOptions { string target; string format; string outputDir; string outputFile; string syncDir; vector config; string configDir; vector confStrings; string inputDir; vector inputs; string inputList; vector includePaths; vector modules; vector excludeDirs; vector excludeFiles; vector excludePatterns; vector excludeStaticDirs; vector excludeStaticFiles; vector excludeStaticPatterns; vector fmodules; vector ffiles; vector cfiles; vector cmodules; bool parseOnDemand; vector parseOnDemandDirs; // parse these directories on-demand // when parseOnDemand=false string program; string programArgs; string branch; int revision; bool genStats; bool keepTempDir; string dbStats; bool noTypeInference; bool noMinInclude; bool noMetaInfo; int logLevel; bool force; int clusterCount; int optimizeLevel; string filecache; string javaRoot; bool dump; string docjson; bool coredump; bool nofork; bool fl_annotate; string optimizations; string ppp; }; /////////////////////////////////////////////////////////////////////////////// class AsyncFileCacheSaver : public AsyncFunc { public: AsyncFileCacheSaver(Package *package, const char *name) : AsyncFunc(this, &AsyncFileCacheSaver::saveCache), m_package(package), m_name(name) { } void saveCache() { Timer timer(Timer::WallTime, "saving file cache..."); m_package->getFileCache()->save(m_name); struct stat sb; stat(m_name, &sb); Logger::Info("%dMB %s saved", (int64_t)sb.st_size/(1024*1024), m_name); } private: Package *m_package; const char *m_name; }; /////////////////////////////////////////////////////////////////////////////// // forward declarations int prepareOptions(CompilerOptions &po, int argc, char **argv); void createOutputDirectory(CompilerOptions &po); int process(const CompilerOptions &po); int lintTarget(const CompilerOptions &po); int analyzeTarget(const CompilerOptions &po, AnalysisResultPtr ar); int phpTarget(const CompilerOptions &po, AnalysisResultPtr ar); void hhbcTargetInit(const CompilerOptions &po, AnalysisResultPtr ar); int hhbcTarget(const CompilerOptions &po, AnalysisResultPtr ar, AsyncFileCacheSaver &fcThread); int runTargetCheck(const CompilerOptions &po, AnalysisResultPtr ar, AsyncFileCacheSaver &fcThread); int runTarget(const CompilerOptions &po); /////////////////////////////////////////////////////////////////////////////// extern "C" void compiler_hook_initialize(); int compiler_main(int argc, char **argv) { try { Hdf empty; RuntimeOption::Load(empty); CompilerOptions po; #ifdef FACEBOOK compiler_hook_initialize(); #endif int ret = prepareOptions(po, argc, argv); if (ret == 1) return 0; // --help if (ret == -1) return -1; // command line error Timer totalTimer(Timer::WallTime, "running hphp"); createOutputDirectory(po); if (ret == 0) { if (!po.nofork && !Process::IsUnderGDB()) { int pid = fork(); if (pid == 0) { ret = process(po); _exit(ret); } wait(&ret); ret = WIFEXITED(ret) ? WEXITSTATUS(ret) : -1; } else { ret = process(po); } } if (ret == 0) { if (po.target == "run") { ret = runTarget(po); } } if (ret) { Logger::Error("hphp failed"); } else { Logger::Info("all files saved in %s ...", po.outputDir.c_str()); } return ret; } catch (Exception &e) { Logger::Error("Exception: %s\n", e.getMessage().c_str()); } catch (const FailedAssertion& fa) { fa.print(); StackTraceNoHeap::AddExtraLogging("Assertion failure", fa.summary); abort(); } catch (std::exception &e) { Logger::Error("std::exception: %s\n", e.what()); } catch (...) { Logger::Error("(unknown exception was thrown)\n"); } return -1; } /////////////////////////////////////////////////////////////////////////////// int prepareOptions(CompilerOptions &po, int argc, char **argv) { options_description desc("HipHop Compiler for PHP Usage:\n\n" "\thphp \n\n" "Options"); desc.add_options() ("help", "display this message") ("version", "display version number") ("target,t", value(&po.target)->default_value("run"), "lint | " "analyze | " "php | " "hhbc | " "filecache | " "run (default)") ("format,f", value(&po.format), "lint: (none); \n" "analyze: (none); \n" "php: trimmed (default) | inlined | pickled | typeinfo |" " ; \n" "hhbc: binary (default) | text; \n" "run: cluster (default) | file") ("cluster-count", value(&po.clusterCount)->default_value(0), "Cluster by file sizes and output roughly these many number of files. " "Use 0 for no clustering.") ("input-dir", value(&po.inputDir), "input directory") ("program", value(&po.program)->default_value("program"), "final program name to use") ("args", value(&po.programArgs), "program arguments") ("inputs,i", value >(&po.inputs), "input file names") ("input-list", value(&po.inputList), "file containing list of file names, one per line") ("include-path", value >(&po.includePaths)->composing(), "a list of full paths to search for files being included in includes " "or requires but cannot be found assuming relative paths") ("module", value >(&po.modules)->composing(), "directories containing all input files") ("exclude-dir", value >(&po.excludeDirs)->composing(), "directories to exclude from the input") ("fmodule", value >(&po.fmodules)->composing(), "same with module, except no exclusion checking is performed, so these " "modules are forced to be included") ("ffile", value >(&po.ffiles)->composing(), "extra PHP files forced to include without exclusion checking") ("exclude-file", value >(&po.excludeFiles)->composing(), "files to exclude from the input, even if parse-on-demand finds it") ("exclude-pattern", value >(&po.excludePatterns)->composing(), "regex (in 'find' command's regex command line option format) of files " "or directories to exclude from the input, even if parse-on-demand finds " "it") ("exclude-static-pattern", value >(&po.excludeStaticPatterns)->composing(), "regex (in 'find' command's regex command line option format) of files " "or directories to exclude from static content cache") ("exclude-static-dir", value >(&po.excludeStaticDirs)->composing(), "directories to exclude from static content cache") ("exclude-static-file", value >(&po.excludeStaticFiles)->composing(), "files to exclude from static content cache") ("cfile", value >(&po.cfiles)->composing(), "extra static files forced to include without exclusion checking") ("cmodule", value >(&po.cmodules)->composing(), "extra directories for static files without exclusion checking") ("parse-on-demand", value(&po.parseOnDemand)->default_value(true), "whether to parse files that are not specified from command line") ("branch", value(&po.branch), "SVN branch") ("revision", value(&po.revision), "SVN revision") ("output-dir,o", value(&po.outputDir), "output directory") ("output-file", value(&po.outputFile), "output file") ("sync-dir", value(&po.syncDir), "Files will be created in this directory first, then sync with output " "directory without overwriting identical files. Great for incremental " "compilation and build.") ("optimize-level", value(&po.optimizeLevel)->default_value(-1), "optimization level") ("gen-stats", value(&po.genStats)->default_value(false), "whether to generate code errors") ("keep-tempdir,k", value(&po.keepTempDir)->default_value(false), "whether to keep the temporary directory") ("db-stats", value(&po.dbStats), "database connection string to save code errors: " ":@:/") ("no-type-inference", value(&po.noTypeInference)->default_value(false), "turn off type inference for C++ code generation") ("no-min-include", value(&po.noMinInclude)->default_value(false), "turn off minimium include analysis when target is \"analyze\"") ("no-meta-info", value(&po.noMetaInfo)->default_value(false), "do not generate class map, function jump table and macros " "when generating code; good for demo purposes") ("config,c", value >(&po.config)->composing(), "config file name") ("config-dir", value(&po.configDir), "root directory configuration is based on (for example, " "excluded directories may be relative path in configuration.") ("config-value,v", value >(&po.confStrings)->composing(), "individual configuration string in a format of name=value, where " "name can be any valid configuration for a config file") ("log,l", value(&po.logLevel)->default_value(-1), "-1: (default); 0: no logging; 1: errors only; 2: warnings and errors; " "3: informational as well; 4: really verbose.") ("force", value(&po.force)->default_value(true), "force to ignore code generation errors and continue compilations") ("file-cache", value(&po.filecache), "if specified, generate a static file cache with this file name") ("dump", value(&po.dump)->default_value(false), "dump the program graph") ("docjson", value(&po.docjson)->default_value(""), "Filename to generate a JSON file for PHP docs") ("coredump", value(&po.coredump)->default_value(false), "turn on coredump") ("nofork", value(&po.nofork)->default_value(false), "forking is needed for large compilation to release memory before g++" "compilation. turning off forking can help gdb debugging.") ("fl-annotate", value(&po.fl_annotate)->default_value(false), "Annotate emitted source with compiler file-line info") ("opts", value(&po.optimizations)->default_value(""), "Set optimizations to enable/disable") ("ppp", value(&po.ppp)->default_value(""), "Preprocessed partition configuration. To speed up distcc compilation, " "bin/ppp.php can pre-compute better partition between different .cpp " "files according to preprocessed file sizes, instead of original file " "sizes (default). Run bin/ppp.php to generate an HDF configuration file " "to specify here.") ("compiler-id", "display the git hash for the compiler id") ("repo-schema", "display the repo schema id used by this app") ("taint-status", "check if the compiler was built with taint enabled") ; positional_options_description p; p.add("inputs", -1); variables_map vm; try { store(command_line_parser(argc, argv).options(desc).positional(p).run(), vm); notify(vm); } catch (unknown_option e) { Logger::Error("Error in command line: %s\n\n", e.what()); cout << desc << "\n"; return -1; } if (argc <= 1 || vm.count("help")) { cout << desc << "\n"; return 1; } if (vm.count("version")) { #ifdef HPHP_VERSION #undef HPHP_VERSION #endif #ifdef HPHP_COMPILER_STR #undef HPHP_COMPILER_STR #endif #ifdef DEBUG #define HPHP_COMPILER_STR "HipHop Compiler (Debug Build) v" #else #define HPHP_COMPILER_STR "HipHop Compiler v" #endif #define HPHP_VERSION(v) cout << HPHP_COMPILER_STR #v << "\n"; #include "../version" cout << "Compiler: " << kCompilerId << "\n"; cout << "Repo schema: " << kRepoSchemaId << "\n"; return 1; } if (vm.count("compiler-id")) { cout << kCompilerId << "\n"; return 1; } if (vm.count("repo-schema")) { cout << kRepoSchemaId << "\n"; return 1; } if (vm.count("taint-status")) { #ifdef TAINTED cout << TAINTED << "\n"; #endif return 1; } if ((po.target == "hhbc" || po.target == "run") && po.format.find("exe") == string::npos) { if (po.program == "program") { po.program = "hhvm.hhbc"; } } // log level if (po.logLevel != -1) { Logger::LogLevel = (Logger::LogLevelType)po.logLevel; } else if (po.target == "run") { Logger::LogLevel = Logger::LogNone; } else { Logger::LogLevel = Logger::LogInfo; } Option::FlAnnotate = po.fl_annotate; Hdf config; for (vector::const_iterator it = po.config.begin(); it != po.config.end(); ++it) { config.append(*it); } for (unsigned int i = 0; i < po.confStrings.size(); i++) { config.fromString(po.confStrings[i].c_str()); } Option::Load(config); vector badnodes; config.lint(badnodes); for (unsigned int i = 0; i < badnodes.size(); i++) { Logger::Error("Possible bad config node: %s", badnodes[i].c_str()); } if (po.dump) Option::DumpAst = true; if (po.inputDir.empty()) { po.inputDir = '.'; } po.inputDir = Util::normalizeDir(po.inputDir); if (po.configDir.empty()) { po.configDir = po.inputDir; } po.configDir = Util::normalizeDir(po.configDir); Option::RootDirectory = po.configDir; Option::IncludeSearchPaths = po.includePaths; for (unsigned int i = 0; i < po.excludeDirs.size(); i++) { Option::PackageExcludeDirs.insert (Util::normalizeDir(po.excludeDirs[i])); } for (unsigned int i = 0; i < po.excludeFiles.size(); i++) { Option::PackageExcludeFiles.insert(po.excludeFiles[i]); } for (unsigned int i = 0; i < po.excludePatterns.size(); i++) { Option::PackageExcludePatterns.insert (Util::format_pattern(po.excludePatterns[i], true)); } for (unsigned int i = 0; i < po.excludeStaticDirs.size(); i++) { Option::PackageExcludeStaticDirs.insert (Util::normalizeDir(po.excludeStaticDirs[i])); } for (unsigned int i = 0; i < po.excludeStaticFiles.size(); i++) { Option::PackageExcludeStaticFiles.insert(po.excludeStaticFiles[i]); } for (unsigned int i = 0; i < po.excludeStaticPatterns.size(); i++) { Option::PackageExcludeStaticPatterns.insert (Util::format_pattern(po.excludeStaticPatterns[i], true)); } if (po.target == "hhbc" || po.target == "run") { Option::AnalyzePerfectVirtuals = false; } Option::ProgramName = po.program; Option::PreprocessedPartitionConfig = po.ppp; if (po.format.empty()) { if (po.target == "php") { po.format = "trimmed"; } else if (po.target == "run") { po.format = "binary"; } else if (po.target == "hhbc") { po.format = "binary"; } } if (!po.docjson.empty()) { if (po.target != "run" && po.target != "hhbc" && po.target != "analyze") { Logger::Error( "Cannot generate doc JSON file unless target is " "'hhbc', 'run', or 'analyze'"); } else { Option::DocJson = po.docjson; } } if (po.optimizeLevel == -1) { po.optimizeLevel = 1; } // we always do pre/post opt no matter the opt level Option::PreOptimization = true; Option::PostOptimization = true; if (po.optimizeLevel == 0) { // --optimize-level=0 is equivalent to --opts=none po.optimizations = "none"; Option::ParseTimeOpts = false; } return 0; } /////////////////////////////////////////////////////////////////////////////// int process(const CompilerOptions &po) { if (po.coredump) { #if defined(__APPLE__) || defined(__FreeBSD__) struct rlimit rl; getrlimit(RLIMIT_CORE, &rl); rl.rlim_cur = 80000000LL; if (rl.rlim_max < rl.rlim_cur) { rl.rlim_max = rl.rlim_cur; } setrlimit(RLIMIT_CORE, &rl); #else struct rlimit64 rl; getrlimit64(RLIMIT_CORE, &rl); rl.rlim_cur = 8000000000LL; if (rl.rlim_max < rl.rlim_cur) { rl.rlim_max = rl.rlim_cur; } setrlimit64(RLIMIT_CORE, &rl); #endif } // lint doesn't need analysis if (po.target == "lint") { return lintTarget(po); } register_process_init(); init_thread_locals(); Timer timer(Timer::WallTime); AnalysisResultPtr ar; // prepare a package Package package(po.inputDir.c_str()); ar = package.getAnalysisResult(); hhbcTargetInit(po, ar); std::string errs; if (!AliasManager::parseOptimizations(po.optimizations, errs)) { Logger::Error("%s\n", errs.c_str()); return false; } // one time initialization Type::InitTypeHintMap(); BuiltinSymbols::LoadSuperGlobals(); ClassInfo::Load(); bool isPickledPHP = (po.target == "php" && po.format == "pickled"); if (!isPickledPHP) { if (!BuiltinSymbols::Load(ar, po.target == "hhbc" && !Option::WholeProgram)) { return false; } if (po.target == "hhbc" && !Option::WholeProgram) { BuiltinSymbols::NoSuperGlobals = false; } else { ar->loadBuiltins(); } hphp_process_init(); } { Timer timer(Timer::WallTime, "parsing inputs"); if (!po.inputs.empty() && isPickledPHP) { for (unsigned int i = 0; i < po.inputs.size(); i++) { package.addSourceFile(po.inputs[i].c_str()); } } else { ar->setPackage(&package); ar->setParseOnDemand(po.parseOnDemand); if (!po.parseOnDemand) { ar->setParseOnDemandDirs(Option::ParseOnDemandDirs); } if (po.modules.empty() && po.fmodules.empty() && po.ffiles.empty() && po.inputs.empty() && po.inputList.empty()) { package.addAllFiles(false); } else { for (unsigned int i = 0; i < po.modules.size(); i++) { package.addDirectory(po.modules[i], false); } for (unsigned int i = 0; i < po.fmodules.size(); i++) { package.addDirectory(po.fmodules[i], true); } for (unsigned int i = 0; i < po.ffiles.size(); i++) { package.addSourceFile(po.ffiles[i].c_str()); } for (unsigned int i = 0; i < po.cmodules.size(); i++) { package.addStaticDirectory(po.cmodules[i].c_str()); } for (unsigned int i = 0; i < po.cfiles.size(); i++) { package.addStaticFile(po.cfiles[i].c_str()); } for (unsigned int i = 0; i < po.inputs.size(); i++) { package.addSourceFile(po.inputs[i].c_str()); } if (!po.inputList.empty()) { package.addInputList(po.inputList.c_str()); } } } if (po.target != "filecache") { if (!package.parse(!po.force)) { return 1; } if (Option::WholeProgram || po.target == "analyze") { ar->analyzeProgram(); } } } // saving file cache AsyncFileCacheSaver fileCacheThread(&package, po.filecache.c_str()); if (po.target != "analyze" && !po.filecache.empty()) { fileCacheThread.start(); } if (Option::DumpAst) { ar->dump(); } int ret = 0; if (po.target == "analyze") { ret = analyzeTarget(po, ar); } else if (po.target == "php") { ret = phpTarget(po, ar); } else if (po.target == "hhbc") { ret = hhbcTarget(po, ar, fileCacheThread); } else if (po.target == "run") { ret = runTargetCheck(po, ar, fileCacheThread); } else if (po.target == "filecache") { // do nothing } else { Logger::Error("Unknown target: %s", po.target.c_str()); return 1; } if (Option::DumpAst) { ar->dump(); } if (!Option::DocJson.empty()) { Timer timer(Timer::WallTime, "Saving doc JSON file"); ar->docJson(Option::DocJson); } // saving stats if (po.target == "analyze" || po.genStats || !po.dbStats.empty()) { int seconds = timer.getMicroSeconds() / 1000000; Logger::Info("saving code errors and stats..."); Timer timer(Timer::WallTime, "saving stats"); if (!po.dbStats.empty()) { try { ServerDataPtr server = ServerData::Create(po.dbStats); int runId = package.saveStatsToDB(server, seconds, po.branch, po.revision); package.commitStats(server, runId); } catch (DatabaseException e) { Logger::Error("%s", e.what()); } } else { Compiler::SaveErrors(ar, (po.outputDir + "/CodeError.js").c_str()); package.saveStatsToFile((po.outputDir + "/Stats.js").c_str(), seconds); } } else if (Compiler::HasError()) { Logger::Info("saving code errors..."); Compiler::SaveErrors(ar, (po.outputDir + "/CodeError.js").c_str()); } if (!po.filecache.empty()) { fileCacheThread.waitForEnd(); } return ret; } /////////////////////////////////////////////////////////////////////////////// int lintTarget(const CompilerOptions &po) { int ret = 0; for (unsigned int i = 0; i < po.inputs.size(); i++) { string filename = po.inputDir + "/" + po.inputs[i]; try { Scanner scanner(filename.c_str(), Option::ScannerType); Compiler::Parser parser(scanner, filename.c_str(), AnalysisResultPtr(new AnalysisResult())); if (!parser.parse()) { Logger::Error("Unable to parse file %s: %s", filename.c_str(), parser.getMessage().c_str()); ret = 1; } else { Logger::Info("%s parsed successfully...", filename.c_str()); } } catch (FileOpenException &e) { Logger::Error("%s", e.getMessage().c_str()); ret = 1; } } return ret; } /////////////////////////////////////////////////////////////////////////////// int analyzeTarget(const CompilerOptions &po, AnalysisResultPtr ar) { int ret = 0; if (!po.noTypeInference) { Option::GenerateInferredTypes = true; } if (Option::PreOptimization) { Timer timer(Timer::WallTime, "pre-optimizing"); ar->preOptimize(); } if (!Option::AllVolatile) { Timer timer(Timer::WallTime, "analyze includes"); ar->analyzeIncludes(); } if (Option::GenerateInferredTypes) { Timer timer(Timer::WallTime, "inferring types"); ar->inferTypes(); } if (Option::PostOptimization) { Timer timer(Timer::WallTime, "post-optimizing"); ar->postOptimize(); } ar->analyzeProgramFinal(); return ret; } /////////////////////////////////////////////////////////////////////////////// int phpTarget(const CompilerOptions &po, AnalysisResultPtr ar) { int ret = 0; // format int formatCount = 0; if (po.format.find("pickled") != string::npos) { Option::GeneratePickledPHP = true; formatCount++; } if (po.format.find("inlined") != string::npos) { Option::GenerateInlinedPHP = true; formatCount++; } if (po.format.find("trimmed") != string::npos) { Option::GenerateTrimmedPHP = true; formatCount++; } if (po.format.find("typeinfo") != string::npos) { Option::GenerateInferredTypes = true; } if (formatCount == 0) { Logger::Error("Unknown format for PHP target: %s", po.format.c_str()); return 1; } // analyze if (Option::GenerateInferredTypes || Option::ConvertSuperGlobals) { Logger::Info("inferring types..."); ar->inferTypes(); } // generate ar->setOutputPath(po.outputDir); if (Option::GeneratePickledPHP) { Logger::Info("creating pickled PHP files..."); string outputDir = po.outputDir; if (formatCount > 1) outputDir += "/pickled"; mkdir(outputDir.c_str(), 0777); ar->outputAllPHP(CodeGenerator::PickledPHP); } if (Option::GenerateInlinedPHP) { Logger::Info("creating inlined PHP files..."); string outputDir = po.outputDir; if (formatCount > 1) outputDir += "/inlined"; mkdir(outputDir.c_str(), 0777); if (!ar->outputAllPHP(CodeGenerator::InlinedPHP)) { ret = -1; } } if (Option::GenerateTrimmedPHP) { Logger::Info("creating trimmed PHP files..."); string outputDir = po.outputDir; if (formatCount > 1) outputDir += "/trimmed"; mkdir(outputDir.c_str(), 0777); if (!ar->outputAllPHP(CodeGenerator::TrimmedPHP)) { ret = -1; } } return ret; } /////////////////////////////////////////////////////////////////////////////// void hhbcTargetInit(const CompilerOptions &po, AnalysisResultPtr ar) { if (po.syncDir.empty()) { ar->setOutputPath(po.outputDir); } else { ar->setOutputPath(po.syncDir); } // Propagate relevant compiler-specific options to the runtime. RuntimeOption::RepoCentralPath = ar->getOutputPath() + '/' + po.program; if (po.format.find("exe") != string::npos) { RuntimeOption::RepoCentralPath += ".hhbc"; } RuntimeOption::RepoLocalMode = "--"; RuntimeOption::RepoDebugInfo = Option::RepoDebugInfo; RuntimeOption::RepoJournal = "memory"; RuntimeOption::EnableHipHopSyntax = Option::EnableHipHopSyntax; RuntimeOption::EvalJitEnableRenameFunction = Option::JitEnableRenameFunction; } int hhbcTarget(const CompilerOptions &po, AnalysisResultPtr ar, AsyncFileCacheSaver &fcThread) { int ret = 0; int formatCount = 0; const char *type = 0; if (po.format.find("text") != string::npos) { Option::GenerateTextHHBC = true; type = "creating text HHBC files"; formatCount++; } if (po.format.find("binary") != string::npos) { Option::GenerateBinaryHHBC = true; type = "creating binary HHBC files"; formatCount++; } if (po.format.find("exe") != string::npos) { Option::GenerateBinaryHHBC = true; type = "creating binary HHBC files"; formatCount++; } if (formatCount == 0) { Logger::Error("Unknown format for HHBC target: %s", po.format.c_str()); return 1; } /* without this, emitClass allows classes with interfaces to be hoistable */ SystemLib::s_inited = true; Option::AutoInline = -1; if (po.optimizeLevel > 0) { ret = analyzeTarget(po, ar); } Timer timer(Timer::WallTime, type); Compiler::emitAllHHBC(ar); if (!po.syncDir.empty()) { if (!po.filecache.empty()) { fcThread.waitForEnd(); } Util::syncdir(po.outputDir, po.syncDir); boost::filesystem::remove_all(po.syncDir); } if (!ret && po.format.find("exe") != string::npos) { /* * We need to create an executable with the repo * embedded in it. * Copy ourself, and embed the repo as a section * named "repo". */ string exe = po.outputDir + '/' + po.program; string repo = "repo=" + exe + ".hhbc"; char buf[PATH_MAX]; if (!realpath("/proc/self/exe", buf)) return -1; const char *argv[] = { "objcopy", "--add-section", repo.c_str(), buf, exe.c_str(), 0 }; string out; ret = Process::Exec(argv[0], argv, nullptr, out, nullptr) ? 0 : 1; } return ret; } /////////////////////////////////////////////////////////////////////////////// int runTargetCheck(const CompilerOptions &po, AnalysisResultPtr ar, AsyncFileCacheSaver &fcThread) { // generate code if (hhbcTarget(po, ar, fcThread)) { return 1; } // check error if (Compiler::HasError() && !po.force) { Compiler::DumpErrors(ar); return 1; } return 0; } int runTarget(const CompilerOptions &po) { int ret = 0; // If there are more than one input files, we need one extra arg to run. // If it's missing, we will stop right here, with compiled code. if ((po.inputs.size() != 1 && po.programArgs.empty()) || !po.inputList.empty()) { return 0; } // run the executable string cmd; if (po.format.find("exe") == string::npos) { char buf[PATH_MAX]; if (!realpath("/proc/self/exe", buf)) return -1; cmd += buf; cmd += " -vRepo.Authoritative=true"; cmd += " -vRepo.Local.Mode=r- -vRepo.Local.Path="; } cmd += po.outputDir + '/' + po.program; cmd += string(" --file ") + (po.inputs.size() == 1 ? po.inputs[0] : "") + po.programArgs; Logger::Info("running executable %s...", cmd.c_str()); ret = Util::ssystem(cmd.c_str()); if (ret && ret != -1) ret = 1; // delete the temporary directory if not needed if (!po.keepTempDir) { Logger::Info("deleting temporary directory %s...", po.outputDir.c_str()); boost::filesystem::remove_all(po.outputDir); } return ret; } void createOutputDirectory(CompilerOptions &po) { if (po.outputDir.empty()) { const char *t = getenv("TEMP"); if (!t) { t = "/tmp"; } string temp = t; temp += "/hphp_XXXXXX"; char path[PATH_MAX + 1]; strncpy(path, temp.c_str(), PATH_MAX); path[PATH_MAX] = '\0'; po.outputDir = mkdtemp(path); Logger::Info("creating temporary directory %s ...", po.outputDir.c_str()); } mkdir(po.outputDir.c_str(), 0777); if (!po.syncDir.empty()) { Logger::Info("re-creating sync directory %s ...", po.syncDir.c_str()); boost::filesystem::remove_all(po.syncDir); mkdir(po.syncDir.c_str(), 0777); } } /////////////////////////////////////////////////////////////////////////////// }