Merge "(bug 25830) fix JS preview causing the page to "jump""
[lhc/web/wiklou.git] / maintenance / hiphop / make
index 60388ac..2fa70dc 100755 (executable)
@@ -1,18 +1,27 @@
 #!/usr/bin/hphpi -f 
 <?php
 
-require( dirname( __FILE__ ) . '/../Maintenance.php' );
+define( 'MW_CONFIG_CALLBACK', 'MakeHipHop::noConfigNeeded' );
+require( __DIR__ . '/../Maintenance.php' );
 
 class MakeHipHop extends Maintenance {
+       function noConfigNeeded() {}
 
        function execute() {
+               global $wgHipHopBuildDirectory;
+
                $startTime = time();
 
-               $sourceDir = realpath( dirname( __FILE__ ) );
-               $IP = realpath( "$sourceDir/../.." );
-               $buildDir = "$sourceDir/build";
+               $thisDir = realpath( __DIR__ );
+               $IP = realpath( "$thisDir/../.." );
+               if ( strval( $wgHipHopBuildDirectory ) !== '' ) {
+                       $buildDir = $wgHipHopBuildDirectory;
+               } else {
+                       $buildDir = "$thisDir/build";
+               }
+               $extensionsDir = realpath( MWInit::getExtensionsDirectory() );
                $outDir = "$buildDir/hiphop-output";
-               $persistentDir = "$buildDir/persistent" ;
+               $persistentDir = "$buildDir/persistent";
 
                if ( !is_dir( $buildDir ) ) {
                        mkdir( $buildDir, 0777, true );
@@ -21,6 +30,17 @@ class MakeHipHop extends Maintenance {
                        mkdir( $persistentDir, 0777, true );
                }
 
+               if ( realpath( "$IP/../phase3" ) !== $IP
+                       || realpath( "$IP/../extensions" ) !== $extensionsDir )
+               {
+                       # Set up a fake source directory with the correct layout
+                       $sourceBase = "$buildDir/source";
+                       $this->setupFakeSourceBase( $IP, $extensionsDir, $sourceBase );
+               } else {
+                       $sourceBase = realpath( "$IP/.." );
+                       unlink( "$buildDir/source" );
+               }
+
                # With the CentOS RPMs, you just get g++44, no g++, so we have to 
                # use the environment
                if ( isset( $_ENV['CXX'] ) ) {
@@ -40,13 +60,8 @@ class MakeHipHop extends Maintenance {
                        "}\n"
                );
 
-               # Generate the file list from the autoloader
-               global $wgAutoloadLocalClasses;
-               $files = array_merge(
-                       array_values( $wgAutoloadLocalClasses ),
-                       array_map( 'trim', file( "$sourceDir/extra-files" ) )
-               );
-               $files = array_unique( $files );
+               # Generate the file list
+               $files = $this->getFileList();
                file_put_contents(
                        "$buildDir/file-list",
                        implode( "\n", $files ) . "\n" );
@@ -56,20 +71,28 @@ class MakeHipHop extends Maintenance {
                        'hphp' .
                        ' --target=cpp' .
                        ' --format=file' .
-                       ' --input-dir=' . wfEscapeShellArg( $IP ) .
+                       ' --input-dir=' . wfEscapeShellArg( $sourceBase ) .
                        ' --input-list=' . wfEscapeShellArg( "$buildDir/file-list" ) .
                        ' --inputs=' . wfEscapeShellArg( "$buildDir/HipHopCompilerVersion.php" ) .
-                       ' -c ' . wfEscapeShellArg( "$sourceDir/compiler.conf" ) .
+                       ' -c ' . wfEscapeShellArg( "$thisDir/compiler.conf" ) .
                        ' --parse-on-demand=false' .
                        ' --program=mediawiki-hphp' .
                        ' --output-dir=' . wfEscapeShellArg( $outDir ) .
-                       ' --log=3' );
+                       ' --log=3', $ret );
+
+               if ( $ret ) {
+                       $this->error( "hphp hit an error. Stopping build.\n" );
+                       exit( 1 );
+               }
 
                # Sanity check, quickly make sure we've got an output directory
                if( !is_dir( $outDir ) ) {
                        $this->error( "No output directory", true );
                }
 
+               # Warn about volatile classes
+               $this->checkVolatileClasses( $outDir );
+
                # Copy the generated C++ files into the source directory for cmake
                $iter = new RecursiveIteratorIterator( 
                        new RecursiveDirectoryIterator( $outDir ),
@@ -140,7 +163,7 @@ class MakeHipHop extends Maintenance {
                        }
 
                        $cmd = 'cmake' .
-                               ' -D CMAKE_BUILD_TYPE:string=Debug' .
+                               " -D CMAKE_BUILD_TYPE:string=" . wfEscapeShellArg( $GLOBALS['wgHipHopBuildType'] ) .
                                ' -D PROGRAM_NAME:string=mediawiki-hphp';
                        
                        if ( file_exists( '/usr/bin/ccache' ) ) {
@@ -155,19 +178,7 @@ class MakeHipHop extends Maintenance {
 
                # Determine appropriate make concurrency
                # Compilation can take a lot of memory, let's assume that that is limiting.
-               $mem = false;
-               foreach ( file( '/proc/meminfo' ) as $line ) {
-                       if ( preg_match( '/^MemTotal:\s+(\d+)\s+kB/', $line, $m ) ) {
-                               $mem = intval( $m[1] );
-                               break;
-                       }
-               }
-               if ( $mem ) {
-                       $procs = floor( $mem / 1000000 );
-                       $procs = $procs >= 1 ? $procs : 1; // No less than 1
-               } else {
-                       $procs = 1;
-               }
+               $procs = $this->getNumProcs();
                
                # Run make. This is the slow step.
                passthru( 'make -j' . wfEscapeShellArg( $procs ) );
@@ -186,7 +197,113 @@ class MakeHipHop extends Maintenance {
                        $elapsed -= $minutes * 60;
                }
                echo $elapsed . "s\n";
-               echo "The MediaWiki executable is at build/persistent/mediawiki-hphp\n";
+               echo "The MediaWiki executable is at $buildDir/persistent/mediawiki-hphp\n";
+       }
+
+       function checkVolatileClasses( $dir ) {
+               $lines = file( "$dir/sys/dynamic_table_class.cpp" );
+               $classes = array();
+               foreach ( $lines as $line ) {
+                       if ( preg_match( '/^\s+\(const char \*\)"([^"]*)", \(const char \*\)-1/', $line, $m ) ) {
+                               $classes[] = $m[1];
+                       }
+               }
+               if ( !count( $classes ) ) {
+                       print "No volatile classes found\n";
+                       return;
+               }
+               sort( $classes );
+               $classes = array_unique( $classes );
+               print "WARNING: The following classes are volatile: " . implode( ', ', $classes ) . "\n";
+       }
+
+       function getNumProcs() {
+               global $wgHipHopCompilerProcs;
+               if ( $wgHipHopCompilerProcs !== 'detect' ) {
+                       return intval( $wgHipHopCompilerProcs );
+               }
+
+               if ( !file_exists( '/proc/meminfo' ) ) {
+                       return 1;
+               }
+               $mem = false;
+               foreach ( file( '/proc/meminfo' ) as $line ) {
+                       if ( preg_match( '/^MemTotal:\s+(\d+)\s+kB/', $line, $m ) ) {
+                               $mem = intval( $m[1] );
+                               break;
+                       }
+               }
+               if ( $mem ) {
+                       // At least one process
+                       return max( 1, floor( $mem / 1000000 ) );
+               } else {
+                       return 1;
+               }
+       }
+
+       function setupFakeSourceBase( $phase3, $extensions, $dest ) {
+               if ( !file_exists( $dest ) ) {
+                       mkdir( $dest, 0777, true );
+               }
+
+               $this->forceCreateLink( "$dest/phase3", $phase3 );
+               $this->forceCreateLink( "$dest/extensions", $extensions );
+       }
+
+       function forceCreateLink( $target, $link ) {
+               if ( file_exists( $target ) ) {
+                       if ( readlink( $target ) === $link ) {
+                               return;
+                       }
+                       unlink( $target );
+               }
+               symlink( $target, $link );
+       }
+
+       function getFileList() {
+               global $wgAutoloadClasses, $wgAutoloadLocalClasses, $wgCompiledFiles;
+               $inputFiles = array_merge(
+                       array_values( $wgAutoloadClasses ),
+                       array_values( $wgAutoloadLocalClasses ),
+                       $wgCompiledFiles
+               );
+               $processedFiles = array();
+               foreach ( $inputFiles as $file ) {
+                       if ( substr( $file, 0, 1 ) === '/' ) {
+                               $processedFiles[] = $this->absoluteToRelative( $file );
+                       } elseif ( preg_match( '/^extensions/', $file ) ) {
+                               $processedFiles[] = $file;
+                       } else {
+                               $processedFiles[] = "phase3/$file";
+                       }
+               }
+
+               $extraCoreFiles = array_map( 'trim', file( __DIR__ . '/extra-files' ) );
+               foreach ( $extraCoreFiles as $file ) {
+                       if ( $file === '' ) {
+                               continue;
+                       }
+                       $processedFiles[] = "phase3/$file";
+               }
+               return array_unique( $processedFiles );
+       }
+
+       function absoluteToRelative( $file ) {
+               global $IP;
+
+               $coreBase = realpath( $IP ) . '/';
+               $extBase = realpath( MWInit::getExtensionsDirectory() ) . '/';
+               $file = realpath( $file );
+
+               if ( substr( $file, 0, strlen( $extBase ) ) === $extBase ) {
+                       return 'extensions/' . substr( $file, strlen( $extBase ) );
+               } elseif ( substr( $file, 0, strlen( $coreBase ) ) === $coreBase ) {
+                       return 'phase3/' . substr( $file, strlen( $coreBase ) );
+               } else {
+                       $this->error( "The following file is registered for compilation but is not in \$IP or " .
+                               "\$wgExtensionsDirectory: $file \n" );
+                       exit( 1 );
+               }
        }
 }