Arquivos
hhvm/hphp/compiler/compiler.cpp
T
mwilliams 1d68430f6a Always setup Repo paths in hphp mode
We didnt do it for the analyze target, for example,
which resulted in hphp hanging on some machines due to
trying to create ~/.hhvm.hhbc
2013-03-21 16:13:21 -07:00

991 linhas
31 KiB
C++

/*
+----------------------------------------------------------------------+
| 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 <boost/program_options/options_description.hpp>
#include <boost/program_options/positional_options.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/parsers.hpp>
#include <compiler/package.h>
#include <compiler/analysis/analysis_result.h>
#include <compiler/analysis/alias_manager.h>
#include <compiler/analysis/code_error.h>
#include <compiler/analysis/emitter.h>
#include <compiler/analysis/type.h>
#include <util/json.h>
#include <util/logger.h>
#include <compiler/analysis/symbol_table.h>
#include <compiler/option.h>
#include <compiler/parser/parser.h>
#include <compiler/builtin_symbols.h>
#include <util/db_conn.h>
#include <util/exception.h>
#include <util/process.h>
#include <util/util.h>
#include <util/timer.h>
#include <util/hdf.h>
#include <util/async_func.h>
#include <runtime/base/program_functions.h>
#include <runtime/base/memory/smart_allocator.h>
#include <runtime/base/externals.h>
#include <runtime/base/thread_init_fini.h>
#include <runtime/vm/repo.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <dlfcn.h>
#include <system/lib/systemlib.h>
#include <compiler/compiler.h>
#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<string> config;
string configDir;
vector<string> confStrings;
string inputDir;
vector<string> inputs;
string inputList;
vector<string> includePaths;
vector<string> modules;
vector<string> excludeDirs;
vector<string> excludeFiles;
vector<string> excludePatterns;
vector<string> excludeStaticDirs;
vector<string> excludeStaticFiles;
vector<string> excludeStaticPatterns;
vector<string> fmodules;
vector<string> ffiles;
vector<string> cfiles;
vector<string> cmodules;
bool parseOnDemand;
vector<string> 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<AsyncFileCacheSaver> {
public:
AsyncFileCacheSaver(Package *package, const char *name)
: AsyncFunc<AsyncFileCacheSaver>(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 <options> <inputs>\n\n"
"Options");
desc.add_options()
("help", "display this message")
("version", "display version number")
("target,t", value<string>(&po.target)->default_value("run"),
"lint | "
"analyze | "
"php | "
"hhbc | "
"filecache | "
"run (default)")
("format,f", value<string>(&po.format),
"lint: (none); \n"
"analyze: (none); \n"
"php: trimmed (default) | inlined | pickled | typeinfo |"
" <any combination of them by any separator>; \n"
"hhbc: binary (default) | text; \n"
"run: cluster (default) | file")
("cluster-count", value<int>(&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<string>(&po.inputDir), "input directory")
("program", value<string>(&po.program)->default_value("program"),
"final program name to use")
("args", value<string>(&po.programArgs), "program arguments")
("inputs,i", value<vector<string> >(&po.inputs), "input file names")
("input-list", value<string>(&po.inputList),
"file containing list of file names, one per line")
("include-path",
value<vector<string> >(&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<vector<string> >(&po.modules)->composing(),
"directories containing all input files")
("exclude-dir", value<vector<string> >(&po.excludeDirs)->composing(),
"directories to exclude from the input")
("fmodule", value<vector<string> >(&po.fmodules)->composing(),
"same with module, except no exclusion checking is performed, so these "
"modules are forced to be included")
("ffile", value<vector<string> >(&po.ffiles)->composing(),
"extra PHP files forced to include without exclusion checking")
("exclude-file", value<vector<string> >(&po.excludeFiles)->composing(),
"files to exclude from the input, even if parse-on-demand finds it")
("exclude-pattern",
value<vector<string> >(&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<vector<string> >(&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<vector<string> >(&po.excludeStaticDirs)->composing(),
"directories to exclude from static content cache")
("exclude-static-file",
value<vector<string> >(&po.excludeStaticFiles)->composing(),
"files to exclude from static content cache")
("cfile", value<vector<string> >(&po.cfiles)->composing(),
"extra static files forced to include without exclusion checking")
("cmodule", value<vector<string> >(&po.cmodules)->composing(),
"extra directories for static files without exclusion checking")
("parse-on-demand", value<bool>(&po.parseOnDemand)->default_value(true),
"whether to parse files that are not specified from command line")
("branch", value<string>(&po.branch), "SVN branch")
("revision", value<int>(&po.revision), "SVN revision")
("output-dir,o", value<string>(&po.outputDir), "output directory")
("output-file", value<string>(&po.outputFile), "output file")
("sync-dir", value<string>(&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<int>(&po.optimizeLevel)->default_value(-1),
"optimization level")
("gen-stats", value<bool>(&po.genStats)->default_value(false),
"whether to generate code errors")
("keep-tempdir,k", value<bool>(&po.keepTempDir)->default_value(false),
"whether to keep the temporary directory")
("db-stats", value<string>(&po.dbStats),
"database connection string to save code errors: "
"<username>:<password>@<host>:<port>/<db>")
("no-type-inference",
value<bool>(&po.noTypeInference)->default_value(false),
"turn off type inference for C++ code generation")
("no-min-include",
value<bool>(&po.noMinInclude)->default_value(false),
"turn off minimium include analysis when target is \"analyze\"")
("no-meta-info",
value<bool>(&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<vector<string> >(&po.config)->composing(),
"config file name")
("config-dir", value<string>(&po.configDir),
"root directory configuration is based on (for example, "
"excluded directories may be relative path in configuration.")
("config-value,v", value<vector<string> >(&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<int>(&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<bool>(&po.force)->default_value(true),
"force to ignore code generation errors and continue compilations")
("file-cache",
value<string>(&po.filecache),
"if specified, generate a static file cache with this file name")
("dump",
value<bool>(&po.dump)->default_value(false),
"dump the program graph")
("docjson",
value<string>(&po.docjson)->default_value(""),
"Filename to generate a JSON file for PHP docs")
("coredump",
value<bool>(&po.coredump)->default_value(false),
"turn on coredump")
("nofork",
value<bool>(&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<bool>(&po.fl_annotate)->default_value(false),
"Annotate emitted source with compiler file-line info")
("opts",
value<string>(&po.optimizations)->default_value(""),
"Set optimizations to enable/disable")
("ppp",
value<string>(&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<string>::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<string> 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);
}
}
///////////////////////////////////////////////////////////////////////////////
}