39 require_once __DIR__ .
'/Maintenance.php';
48 parent::__construct();
50 $this->
addOption(
'dry-run',
'Determine what languages need to be rebuilt without changing anything' );
51 $this->
addOption(
'force',
'Rebuild all files, even ones not out of date' );
52 $this->
addOption(
'threads',
'Fork more than one thread',
false,
true );
53 $this->
addOption(
'outdir',
'Override the output directory (normally $wgCacheDirectory)',
55 $this->
addOption(
'lang',
'Only rebuild these languages, comma separated.',
59 'Override the LC store class (normally $wgLocalisationCacheConf[\'storeClass\'])',
65 'EXPERIMENTAL: Disable the database backend. Setting this option will result in an error ' .
66 'if you have extensions or use site configuration that need the database. This is an ' .
67 'experimental feature to allow offline building of the localisation cache. Known limitations:' .
69 '* Incompatible with LCStoreDB, which always requires a database. ' .
"\n" .
70 '* The message purge may require a database. See --skip-message-purge.'
76 'Skip purging of MessageBlobStore. The purge operation may require a database, depending ' .
77 'on the configuration and extensions on this wiki. If skipping the purge now, you need to ' .
78 'run purgeMessageBlobStore.php shortly after deployment.'
82 "Don't print a message for each rebuilt language file. Use this instead of " .
83 "--quiet to get a brief summary of the operation."
88 # This script needs to be run to build the initial l10n cache. But if
89 # LanguageCode is not 'en', it won't be able to run because there is
90 # no l10n cache. Break the cycle by forcing the LanguageCode setting to 'en'.
91 $settingsBuilder->putConfigValue( MainConfigNames::LanguageCode,
'en' );
92 parent::finalSetup( $settingsBuilder );
97 $threads = $this->
getOption(
'threads', 1 );
98 if ( $threads < 1 || $threads != intval( $threads ) ) {
99 $this->
output(
"Invalid thread count specified; running single-threaded.\n" );
103 $this->
output(
"Threaded rebuild is not supported on Windows; running single-threaded.\n" );
106 if ( $threads > 1 && ( !extension_loaded(
'sockets' ) || !function_exists(
'pcntl_fork' ) ) ) {
107 $this->
output(
"Threaded rebuild requires ext-pcntl and ext-sockets; running single-threaded.\n" );
111 $conf = $this->
getConfig()->get( MainConfigNames::LocalisationCacheConf );
113 $conf[
'manualRecache'] =
false;
114 $conf[
'forceRecache'] = $force || !empty( $conf[
'forceRecache'] );
116 $conf[
'storeDirectory'] = $this->
getOption(
'outdir' );
119 if ( $this->
hasOption(
'store-class' ) ) {
120 $conf[
'storeClass'] = $this->
getOption(
'store-class' );
129 $services->getMainConfig()
132 LoggerFactory::getInstance(
'localisation' ),
133 $this->hasOption(
'skip-message-purge' ) ? [] :
134 [
static function () use ( $services ) {
135 MessageBlobStore::clearGlobalCacheEntry( $services->getMainWANObjectCache() );
137 $services->getLanguageNameUtils(),
138 $services->getHookContainer()
141 $allCodes = array_keys( $services
142 ->getLanguageNameUtils()
143 ->getLanguageNames( LanguageNameUtils::AUTONYMS, LanguageNameUtils::SUPPORTED ) );
145 # Validate requested languages
146 $codes = array_intersect( $allCodes,
147 explode(
',', $this->
getOption(
'lang' ) ) );
148 # Bailed out if nothing is left
149 if ( count( $codes ) == 0 ) {
150 $this->
fatalError(
'None of the languages specified exists.' );
153 # By default get all languages
159 $total = count( $codes );
162 if ( $threads <= 1 ) {
164 $numRebuilt += $this->doRebuild( $codes, $lc, $force );
167 $chunks = array_chunk( $codes, ceil( count( $codes ) / $threads ) );
171 foreach ( $chunks as $codes ) {
175 if ( !socket_create_pair( AF_UNIX, SOCK_STREAM, 0, $socketpair ) ) {
176 $this->
fatalError(
'socket_create_pair failed' );
183 } elseif ( $pid === 0 ) {
186 mt_srand( getmypid() );
188 $numRebuilt = $this->doRebuild( $codes, $lc, $force );
190 $msg =
"$numRebuilt\n";
191 socket_write( $socketpair[1], $msg, strlen( $msg ) );
196 $sockets[$pid] = $socketpair[0];
201 foreach ( $sockets as $pid => $socket ) {
203 pcntl_waitpid( $pid, $status );
205 if ( pcntl_wifexited( $status ) ) {
206 $code = pcntl_wexitstatus( $status );
208 $this->
output(
"Pid $pid exited with status $code !!\n" );
211 $res = socket_read( $socket, 512, PHP_NORMAL_READ );
212 if ( $res ===
false ) {
213 $this->
output(
"socket_read failed in parent\n" );
215 $numRebuilt += intval( $res );
220 $parentStatus |= $code;
221 } elseif ( pcntl_wifsignaled( $status ) ) {
222 $signum = pcntl_wtermsig( $status );
223 $this->
output(
"Pid $pid terminated by signal $signum !!\n" );
229 $this->
output(
"$numRebuilt languages rebuilt out of $total\n" );
230 if ( $numRebuilt === 0 ) {
231 $this->
output(
"Use --force to rebuild the caches which are still fresh.\n" );
233 if ( $parentStatus ) {
234 $this->
fatalError(
'Failed.', $parentStatus );
246 private function doRebuild( $codes, $lc, $force ) {
248 $operation = $this->
hasOption(
'dry-run' ) ?
"Would rebuild" :
"Rebuilding";
250 foreach ( $codes as $code ) {
251 if ( $force || $lc->isExpired( $code ) ) {
252 if ( !$this->
hasOption(
'no-progress' ) ) {
253 $this->
output(
"$operation $code...\n" );
256 $lc->recache( $code );
267 if ( $this->
hasOption(
'no-database' ) ) {
271 return parent::getDbType();
280 $this->mOptions[
'force'] = $forced;
285 require_once RUN_MAINTENANCE_IF_MAIN;
wfIsWindows()
Check if the operating system is Windows.
LocalisationCache optimised for loading many languages at once.
const CONSTRUCTOR_OPTIONS
static getStoreFromConf(array $conf, $fallbackCacheDir)
Return a suitable LCStore as specified by the given configuration.
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
const DB_NONE
Constants for DB access type.
output( $out, $channel=null)
Throw some output to the user.
hasOption( $name)
Checks to see if a particular option was set.
getServiceContainer()
Returns the main service container.
addDescription( $text)
Set the description text.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
getOption( $name, $default=null)
Get an option, or return the default.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
A class containing constants representing the names of configuration variables.
Maintenance script to rebuild the localisation cache.
execute()
Do the actual work.
__construct()
Default constructor.
setForce( $forced=true)
Sets whether a run of this maintenance script has the force parameter set.
finalSetup(SettingsBuilder $settingsBuilder=null)
Handle some last-minute setup here.