2 +----------------------------------------------------------------------+
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>
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>
66 #include <folly/portability/SysStat.h>
68 using namespace boost::program_options
;
72 ///////////////////////////////////////////////////////////////////////////////
74 struct CompilerOptions
{
77 std::string outputDir
;
79 std::vector
<std::string
> config
;
80 std::string configDir
;
81 std::vector
<std::string
> confStrings
;
82 std::vector
<std::string
> iniStrings
;
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
;
100 std::string programArgs
;
108 std::string filecache
;
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
) {
123 Timer
timer(Timer::WallTime
, "saving file cache...");
124 m_package
->getFileCache()->save(m_name
);
128 Logger::Info("%" PRId64
" MB %s saved",
129 (int64_t)sb
.st_size
/(1024*1024), 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
);
153 ///////////////////////////////////////////////////////////////////////////////
155 int compiler_main(int argc
, char **argv
) {
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
);
167 if (!po
.nofork
&& !Process::IsUnderGDB()) {
174 ret
= WIFEXITED(ret
) ? WEXITSTATUS(ret
) : -1;
182 if (po
.target
== "run") {
187 Logger::Error("hphp failed");
189 Logger::Info("all files saved in %s ...", po
.outputDir
.c_str());
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());
197 Logger::Error("(unknown exception was thrown)\n");
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"
209 ("help", "display this message")
210 ("version", "display version number")
211 ("target,t", value
<std::string
>(&po
.target
)->default_value("run"),
217 ("format,f", value
<std::string
>(&po
.format
),
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
),
229 ("input-list", value
<std::string
>(&po
.inputList
),
230 "file containing list of file names, one per line")
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")
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")
246 value
<std::vector
<std::string
>>(&po
.excludeFiles
)->composing(),
247 "files to exclude from the input, even if parse-on-demand finds it")
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 "
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(),
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.")
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 "
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.")
299 value
<bool>(&po
.force
)->default_value(true),
300 "force to ignore code generation errors and continue compilations")
302 value
<std::string
>(&po
.filecache
),
303 "if specified, generate a static file cache with this file name")
305 value
<bool>(&po
.dump
)->default_value(false),
306 "dump the program graph")
308 value
<bool>(&po
.coredump
)->default_value(false),
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
;
322 auto opts
= command_line_parser(argc
, argv
).options(desc
)
323 .positional(p
).run();
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";
339 } catch (const error
& e
) {
340 Logger::Error("Error in command line: %s", e
.what());
341 cout
<< desc
<< "\n";
344 } catch (const unknown_option
& e
) {
345 Logger::Error("Error in command line: %s", e
.what());
346 cout
<< desc
<< "\n";
348 } catch (const error
& e
) {
349 Logger::Error("Error in command line: %s", e
.what());
350 cout
<< desc
<< "\n";
353 Logger::Error("Error in command line parsing.");
354 cout
<< desc
<< "\n";
357 if (argc
<= 1 || vm
.count("help")) {
358 cout
<< desc
<< "\n";
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";
370 if (vm
.count("compiler-id")) {
371 cout
<< compilerId() << "\n";
375 if (vm
.count("repo-schema")) {
376 cout
<< repoSchemaId() << "\n";
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.",
387 // desc[ription] is the --help output
388 cout
<< desc
<< "\n";
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";
400 if (po
.logLevel
!= -1) {
401 Logger::LogLevel
= (Logger::LogLevelType
)po
.logLevel
;
402 } else if (po
.target
== "run") {
403 Logger::LogLevel
= Logger::LogNone
;
405 Logger::LogLevel
= Logger::LogInfo
;
408 MemoryManager::TlsWrapper::getCheck();
409 IniSetting::Map ini
= IniSetting::Map::object
;
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;
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
438 if (po
.dump
) Option::DumpAst
= true;
440 if (po
.inputDir
.empty()) {
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();
502 ///////////////////////////////////////////////////////////////////////////////
504 int process(const CompilerOptions
&po
) {
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__)
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
);
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
);
530 // lint doesn't need analysis
531 if (po
.target
== "lint") {
532 return lintTarget(po
);
535 register_process_init();
537 Timer
timer(Timer::WallTime
);
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;
549 if (processInitRan
) {
554 bool isPickledPHP
= (po
.target
== "php" && po
.format
== "pickled");
556 bool wp
= Option::WholeProgram
;
557 Option::WholeProgram
= false;
558 BuiltinSymbols::s_systemAr
= ar
;
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.
567 if (!BuiltinSymbols::Load(ar
)) {
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());
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);
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
)) {
619 if (Option::WholeProgram
) {
620 Timer
timer3(Timer::WallTime
, "analyzeProgram");
621 ar
->analyzeProgram();
627 AsyncFileCacheSaver
fileCacheThread(&package
, po
.filecache
.c_str());
628 if (!po
.filecache
.empty()) {
629 fileCacheThread
.start();
632 if (Option::DumpAst
) {
636 ar
->setFinish([&po
,&timer
,&package
](AnalysisResultPtr res
) {
637 if (Option::DumpAst
) {
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
);
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") {
662 Logger::Error("Unknown target: %s", po
.target
.c_str());
666 if (!po
.filecache
.empty()) {
667 fileCacheThread
.waitForEnd();
672 ///////////////////////////////////////////////////////////////////////////////
674 int lintTarget(const CompilerOptions
&po
) {
676 for (unsigned int i
= 0; i
< po
.inputs
.size(); i
++) {
677 std::string filename
= po
.inputDir
+ "/" + po
.inputs
[i
];
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());
687 Logger::Info("%s parsed successfully...", filename
.c_str());
689 } catch (FileOpenException
&e
) {
690 Logger::Error("%s", e
.getMessage().c_str());
697 ///////////////////////////////////////////////////////////////////////////////
699 static void wholeProgramPasses(const CompilerOptions
& po
,
700 AnalysisResultPtr ar
) {
701 if (Option::PreOptimization
) {
702 Timer
timer(Timer::WallTime
, "pre-optimizing");
707 ///////////////////////////////////////////////////////////////////////////////
709 int phpTarget(const CompilerOptions
&po
, AnalysisResultPtr ar
) {
714 if (po
.format
.find("pickled") != std::string::npos
) {
715 Option::GeneratePickledPHP
= true;
718 if (po
.format
.find("inlined") != std::string::npos
) {
719 Option::GenerateInlinedPHP
= true;
722 if (po
.format
.find("trimmed") != std::string::npos
) {
723 Option::GenerateTrimmedPHP
= true;
726 if (formatCount
== 0) {
727 Logger::Error("Unknown format for PHP target: %s", po
.format
.c_str());
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
)) {
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
)) {
762 ///////////////////////////////////////////////////////////////////////////////
764 void hhbcTargetInit(const CompilerOptions
&po
, AnalysisResultPtr ar
) {
765 if (po
.syncDir
.empty()) {
766 ar
->setOutputPath(po
.outputDir
);
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
) {
791 const char *type
= 0;
792 if (po
.format
.find("text") != std::string::npos
) {
793 Option::GenerateTextHHBC
= true;
794 type
= "creating text HHBC files";
797 if (po
.format
.find("binary") != std::string::npos
) {
798 Option::GenerateBinaryHHBC
= true;
799 type
= "creating binary HHBC files";
802 if (po
.format
.find("exe") != std::string::npos
) {
803 Option::GenerateBinaryHHBC
= true;
804 type
= "creating binary HHBC files";
808 if (formatCount
== 0) {
809 Logger::Error("Unknown format for HHBC target: %s", po
.format
.c_str());
813 /* without this, emitClass allows classes with interfaces to be
815 SystemLib::s_inited
= true;
816 RuntimeOption::RepoCommit
= true;
818 if (po
.optimizeLevel
> 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
840 * Copy ourself, and embed the repo as a section
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 };
851 ret
= proc::exec(argv
[0], argv
, nullptr, out
, nullptr) ? 0 : 1;
857 ///////////////////////////////////////////////////////////////////////////////
859 int runTargetCheck(const CompilerOptions
&po
, AnalysisResultPtr
&& ar
,
860 AsyncFileCacheSaver
&fcThread
) {
862 if (hhbcTarget(po
, std::move(ar
), fcThread
)) {
867 if (Compiler::HasError() && !po
.force
) {
868 Compiler::DumpErrors(ar
);
875 int runTarget(const CompilerOptions
&po
) {
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()) {
885 // run the executable
887 if (po
.format
.find("exe") == std::string::npos
) {
888 std::string buf
= current_executable_path();
889 if (buf
.empty()) return -1;
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
);
913 void createOutputDirectory(CompilerOptions
&po
) {
914 if (po
.outputDir
.empty()) {
915 const char *t
= getenv("TEMP");
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 ///////////////////////////////////////////////////////////////////////////////