Arquivos
hhvm/hphp/compiler/compiler.cpp
T
Rasmus Lerdorf 2b23084f39 Fix program args not having a space
When inputs.size() is 1 and we have programArgs,
we wind up squashing those two values together.

Be pessimistic and always add the space in.
2013-05-06 09:53:36 -07:00

985 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);
VM::initialize_repo();
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")
;
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 (const 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 ((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 (const 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);
}
}
///////////////////////////////////////////////////////////////////////////////
}