#!/usr/bin/env php <?php /* * Copyright 2011 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ require_once dirname(__FILE__).'/__init_script__.php'; $liberate_mode = false; for ($ii = 0; $ii < $argc; $ii++) { if ($argv[$ii] == '--find-paths-for-liberate') { $liberate_mode = true; unset($argv[$ii]); } } $argv = array_values($argv); $argc = count($argv); if ($argc != 2) { $self = basename($argv[0]); echo "usage: {$self} <phutil_library_root>\n"; exit(1); } phutil_require_module('phutil', 'filesystem'); phutil_require_module('phutil', 'filesystem/filefinder'); phutil_require_module('phutil', 'future/exec'); $root = Filesystem::resolvePath($argv[1]); if (!@file_exists($root.'/__phutil_library_init__.php')) { throw new Exception("Provided path is not a phutil library."); } if ($liberate_mode) { ob_start(); } echo "Finding phutil modules...\n"; $files = id(new FileFinder($root)) ->withType('f') ->withSuffix('php') ->excludePath('*/.*') ->setGenerateChecksums(true) ->find(); // NOTE: Sorting by filename ensures that hash computation is stable; it is // important we sort by name instead of by hash because sorting by hash could // create a bad cache hit if the user swaps the contents of two files. ksort($files); $modules = array(); foreach ($files as $file => $hash) { if (dirname($file) == $root) { continue; } $modules[Filesystem::readablePath(dirname($file), $root)][] = $hash; } echo "Found ".count($files)." files in ".count($modules)." modules.\n"; $signatures = array(); foreach ($modules as $module => $hashes) { $hashes = implode(' ', $hashes); $signature = md5($hashes); $signatures[$module] = $signature; } try { $cache = Filesystem::readFile($root.'/.phutil_module_cache'); } catch (Exception $ex) { $cache = null; } $signature_cache = array(); if ($cache) { $signature_cache = json_decode($cache, true); if (!is_array($signature_cache)) { $signature_cache = array(); } } $specs = array(); $need_update = array(); foreach ($signatures as $module => $signature) { if (isset($signature_cache[$module]) && $signature_cache[$module]['signature'] == $signature) { $specs[$module] = $signature_cache[$module]; } else { $need_update[$module] = true; } } $futures = array(); foreach ($need_update as $module => $ignored) { $futures[$module] = new ExecFuture( '%s %s', dirname(__FILE__).'/phutil_analyzer.php', $root.'/'.$module); } if ($futures) { echo "Found ".count($specs)." modules in cache; ". "analyzing ".count($futures)." modified modules"; foreach (Futures($futures)->limit(8) as $module => $future) { echo "."; $specs[$module] = array( 'signature' => $signatures[$module], 'spec' => $future->resolveJSON(), ); } echo "\n"; } else { echo "All modules were found in cache.\n"; } $class_map = array(); $requires_class_map = array(); $requires_interface_map = array(); $function_map = array(); foreach ($specs as $module => $info) { $spec = $info['spec']; foreach (array('class', 'interface') as $type) { foreach ($spec['declares'][$type] as $class => $where) { if (!empty($class_map[$class])) { $prior = $class_map[$class]; echo "\n"; echo "Error: definition of {$type} '{$class}' in module '{$module}' ". "duplicates prior definition in module '{$prior}'."; echo "\n"; exit(1); } $class_map[$class] = $module; } } if (!empty($spec['chain']['class'])) { $requires_class_map += $spec['chain']['class']; } if (!empty($spec['chain']['interface'])) { $requires_interface_map += $spec['chain']['interface']; } foreach ($spec['declares']['function'] as $function => $where) { if (!empty($function_map[$function])) { $prior = $function_map[$function]; echo "\n"; echo "Error: definition of function '{$function}' in module '{$module}' ". "duplicates prior definition in module '{$prior}'."; echo "\n"; exit(1); } $function_map[$function] = $module; } } echo "\n"; ksort($class_map); ksort($requires_class_map); ksort($requires_interface_map); ksort($function_map); $library_map = array( 'class' => $class_map, 'function' => $function_map, 'requires_class' => $requires_class_map, 'requires_interface' => $requires_interface_map, ); $library_map = var_export($library_map, $return_string = true); $library_map = preg_replace('/\s+$/m', '', $library_map); $library_map = preg_replace('/array \(/', 'array(', $library_map); $at = '@'; $map_file = <<<EOPHP <?php /** * This file is automatically generated. Use 'phutil_mapper.php' to rebuild it. * {$at}generated */ phutil_register_library_map({$library_map}); EOPHP; echo "Writing library map file...\n"; Filesystem::writeFile($root.'/__phutil_library_map__.php', $map_file); if ($liberate_mode) { ob_get_clean(); echo json_encode(array_keys($need_update))."\n"; return; } echo "Writing module cache...\n"; Filesystem::writeFile( $root.'/.phutil_module_cache', json_encode($specs)); echo "Done.\n";