Merge "Handle missing namespace prefix in XML dumps more gracefully"
[lhc/web/wiklou.git] / maintenance / hhvm / makeRepo.php
1 <?php
2
3 require __DIR__ . '/../Maintenance.php';
4
5 class HHVMMakeRepo extends Maintenance {
6 function __construct() {
7 parent::__construct();
8 $this->addDescription( 'Compile PHP sources for this MediaWiki instance, ' .
9 'and generate an HHVM bytecode file to be used with HHVM\'s ' .
10 'RepoAuthoritative mode. The MediaWiki core installation path and ' .
11 'all registered extensions are automatically searched for the file ' .
12 'extensions *.php, *.inc, *.php5 and *.phtml.' );
13 $this->addOption( 'output', 'Output filename', true, true, 'o' );
14 $this->addOption( 'input-dir', 'Add an input directory. ' .
15 'This can be specified multiple times.', false, true, 'd', true );
16 $this->addOption( 'exclude-dir', 'Directory to exclude. ' .
17 'This can be specified multiple times.', false, true, false, true );
18 $this->addOption( 'extension', 'Extra file extension', false, true, false, true );
19 $this->addOption( 'hhvm', 'Location of HHVM binary', false, true );
20 $this->addOption( 'base-dir', 'The root of all source files. ' .
21 'This must match hhvm.server.source_root in the server\'s configuration file. ' .
22 'By default, the MW core install path will be used.',
23 false, true );
24 $this->addOption( 'verbose', 'Log level 0-3', false, true, 'v' );
25 }
26
27 private static function startsWith( $subject, $search ) {
28 return substr( $subject, 0, strlen( $search ) === $search );
29 }
30
31 function execute() {
32 global $wgExtensionCredits, $IP;
33
34 $dirs = [ $IP ];
35
36 foreach ( $wgExtensionCredits as $type => $extensions ) {
37 foreach ( $extensions as $extension ) {
38 if ( isset( $extension['path'] )
39 && !self::startsWith( $extension['path'], $IP )
40 ) {
41 $dirs[] = dirname( $extension['path'] );
42 }
43 }
44 }
45
46 $dirs = array_merge( $dirs, $this->getOption( 'input-dir', [] ) );
47 $fileExts =
48 [
49 'php' => true,
50 'inc' => true,
51 'php5' => true,
52 'phtml' => true
53 ] +
54 array_flip( $this->getOption( 'extension', [] ) );
55
56 $dirs = array_unique( $dirs );
57
58 $baseDir = $this->getOption( 'base-dir', $IP );
59 $excludeDirs = array_map( 'realpath', $this->getOption( 'exclude-dir', [] ) );
60
61 if ( $baseDir !== '' && substr( $baseDir, -1 ) !== '/' ) {
62 $baseDir .= '/';
63 }
64
65 $unfilteredFiles = [ "$IP/LocalSettings.php" ];
66 foreach ( $dirs as $dir ) {
67 $this->appendDir( $unfilteredFiles, $dir );
68 }
69
70 $files = [];
71 foreach ( $unfilteredFiles as $file ) {
72 $dotPos = strrpos( $file, '.' );
73 $slashPos = strrpos( $file, '/' );
74 if ( $dotPos === false || $slashPos === false || $dotPos < $slashPos ) {
75 continue;
76 }
77 $extension = substr( $file, $dotPos + 1 );
78 if ( !isset( $fileExts[$extension] ) ) {
79 continue;
80 }
81 $canonical = realpath( $file );
82 foreach ( $excludeDirs as $excluded ) {
83 if ( self::startsWith( $canonical, $excluded ) ) {
84 continue 2;
85 }
86 }
87 if ( self::startsWith( $file, $baseDir ) ) {
88 $file = substr( $file, strlen( $baseDir ) );
89 }
90 $files[] = $file;
91 }
92
93 $files = array_unique( $files );
94
95 print "Found " . count( $files ) . " files in " .
96 count( $dirs ) . " directories\n";
97
98 $tmpDir = wfTempDir() . '/mw-make-repo' . mt_rand( 0, 1<<31 );
99 if ( !mkdir( $tmpDir ) ) {
100 $this->error( 'Unable to create temporary directory', 1 );
101 }
102 file_put_contents( "$tmpDir/file-list", implode( "\n", $files ) );
103
104 $hhvm = $this->getOption( 'hhvm', 'hhvm' );
105 $verbose = $this->getOption( 'verbose', 3 );
106 $cmd = wfEscapeShellArg(
107 $hhvm,
108 '--hphp',
109 '--target', 'hhbc',
110 '--format', 'binary',
111 '--force', '1',
112 '--keep-tempdir', '1',
113 '--log', $verbose,
114 '-v', 'AllVolatile=true',
115 '--input-dir', $baseDir,
116 '--input-list', "$tmpDir/file-list",
117 '--output-dir', $tmpDir );
118 print "$cmd\n";
119 passthru( $cmd, $ret );
120 if ( $ret ) {
121 $this->cleanupTemp( $tmpDir );
122 $this->error( "Error: HHVM returned error code $ret", 1 );
123 }
124 if ( !rename( "$tmpDir/hhvm.hhbc", $this->getOption( 'output' ) ) ) {
125 $this->cleanupTemp( $tmpDir );
126 $this->error( "Error: unable to rename output file", 1 );
127 }
128 $this->cleanupTemp( $tmpDir );
129 return 0;
130 }
131
132 private function cleanupTemp( $tmpDir ) {
133 if ( file_exists( "$tmpDir/hhvm.hhbc" ) ) {
134 unlink( "$tmpDir/hhvm.hhbc" );
135 }
136 if ( file_exists( "$tmpDir/Stats.js" ) ) {
137 unlink( "$tmpDir/Stats.js" );
138 }
139
140 unlink( "$tmpDir/file-list" );
141 rmdir( $tmpDir );
142 }
143
144 private function appendDir( &$files, $dir ) {
145 $iter = new RecursiveIteratorIterator(
146 new RecursiveDirectoryIterator(
147 $dir,
148 FilesystemIterator::UNIX_PATHS
149 ),
150 RecursiveIteratorIterator::LEAVES_ONLY
151 );
152 foreach ( $iter as $file => $fileInfo ) {
153 if ( $fileInfo->isFile() ) {
154 $files[] = $file;
155 }
156 }
157 }
158 }
159
160 $maintClass = 'HHVMMakeRepo';
161 require RUN_MAINTENANCE_IF_MAIN;