mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-21 22:32:41 +01:00
Remove all libphutil v1 support
Summary: Delete all code related to v1 libraries in arcanist. When users liberate a v1 library, prompt them to upgrade. Test Plan: Reverted phabricator/ to a couple of months ago and liberated it. Got prompted to upgrade. Upgraded. Reviewers: vrana, btrahan Reviewed By: vrana CC: aran Maniphest Tasks: T1103 Differential Revision: https://secure.phabricator.com/D2861
This commit is contained in:
parent
5b1a00eab1
commit
67956306cb
6 changed files with 3 additions and 1393 deletions
|
@ -1,409 +0,0 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
$builtin_classes = get_declared_classes();
|
||||
$builtin_interfaces = get_declared_interfaces();
|
||||
$builtin_functions = get_defined_functions();
|
||||
$builtin_functions = $builtin_functions['internal'];
|
||||
|
||||
$builtin = array(
|
||||
'class' => array_fill_keys($builtin_classes, true) + array(
|
||||
'PhutilBootloader' => true,
|
||||
),
|
||||
'function' => array_filter(
|
||||
array(
|
||||
'empty' => true,
|
||||
'isset' => true,
|
||||
'echo' => true,
|
||||
'print' => true,
|
||||
'exit' => true,
|
||||
'die' => true,
|
||||
|
||||
// These are provided by libphutil but not visible in the map.
|
||||
|
||||
'phutil_is_windows' => true,
|
||||
'phutil_load_library' => true,
|
||||
'phutil_is_hiphop_runtime' => true,
|
||||
|
||||
// HPHP/i defines these functions as 'internal', but they are NOT
|
||||
// builtins and do not exist in vanilla PHP. Make sure we don't mark them
|
||||
// as builtin since we need to add dependencies for them.
|
||||
'idx' => false,
|
||||
'id' => false,
|
||||
) + array_fill_keys($builtin_functions, true)),
|
||||
'interface' => array_fill_keys($builtin_interfaces, true),
|
||||
);
|
||||
|
||||
require_once dirname(__FILE__).'/__init_script__.php';
|
||||
|
||||
if ($argc != 2) {
|
||||
$self = basename($argv[0]);
|
||||
echo "usage: {$self} <module>\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
phutil_require_module('phutil', 'filesystem');
|
||||
$dir = Filesystem::resolvePath($argv[1]);
|
||||
|
||||
|
||||
$data = array();
|
||||
$futures = array();
|
||||
foreach (Filesystem::listDirectory($dir, $hidden_files = false) as $file) {
|
||||
if (!preg_match('/.php$/', $file)) {
|
||||
continue;
|
||||
}
|
||||
$data[$file] = Filesystem::readFile($dir.'/'.$file);
|
||||
$futures[$file] = xhpast_get_parser_future($data[$file]);
|
||||
}
|
||||
|
||||
|
||||
$requirements = new PhutilModuleRequirements();
|
||||
$requirements->addBuiltins($builtin);
|
||||
|
||||
$doc_parser = new PhutilDocblockParser();
|
||||
|
||||
$has_init = false;
|
||||
$has_files = false;
|
||||
foreach (Futures($futures) as $file => $future) {
|
||||
|
||||
try {
|
||||
$tree = XHPASTTree::newFromDataAndResolvedExecFuture(
|
||||
$data[$file],
|
||||
$future->resolve());
|
||||
} catch (XHPASTSyntaxErrorException $ex) {
|
||||
echo "Syntax Error! In '{$file}': ".$ex->getMessage()."\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$root = $tree->getRootNode();
|
||||
$requirements->setCurrentFile($file);
|
||||
|
||||
if ($file == '__init__.php') {
|
||||
$has_init = true;
|
||||
$calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
|
||||
foreach ($calls as $call) {
|
||||
$name = $call->getChildByIndex(0);
|
||||
$call_name = $name->getConcreteString();
|
||||
if ($call_name == 'phutil_require_source') {
|
||||
$params = $call->getChildByIndex(1)->getChildren();
|
||||
if (count($params) !== 1) {
|
||||
$requirements->addLint(
|
||||
$call,
|
||||
$call->getConcreteString(),
|
||||
ArcanistPhutilModuleLinter::LINT_ANALYZER_SIGNATURE,
|
||||
"Call to phutil_require_source() must have exactly one argument.");
|
||||
continue;
|
||||
}
|
||||
$param = reset($params);
|
||||
$value = $param->getStringLiteralValue();
|
||||
if ($value === null) {
|
||||
$requirements->addLint(
|
||||
$param,
|
||||
$param->getConcreteString(),
|
||||
ArcanistPhutilModuleLinter::LINT_ANALYZER_SIGNATURE,
|
||||
"phutil_require_source() parameter must be a string literal.");
|
||||
continue;
|
||||
}
|
||||
$requirements->addSourceDependency($name, $value);
|
||||
} else if ($call_name == 'phutil_require_module') {
|
||||
analyze_phutil_require_module($call, $requirements, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$has_files = true;
|
||||
|
||||
$requirements->addSourceDeclaration(basename($file));
|
||||
|
||||
// Find symbols declared as "@phutil-external-symbol function example",
|
||||
// and ignore these in building dependency lists.
|
||||
|
||||
$externals = array();
|
||||
foreach ($root->getTokens() as $token) {
|
||||
if ($token->getTypeName() == 'T_DOC_COMMENT') {
|
||||
list($block, $special) = $doc_parser->parse($token->getValue());
|
||||
|
||||
$ext_list = idx($special, 'phutil-external-symbol');
|
||||
$ext_list = explode("\n", $ext_list);
|
||||
$ext_list = array_filter($ext_list);
|
||||
|
||||
foreach ($ext_list as $ext_ref) {
|
||||
$matches = null;
|
||||
if (preg_match('/^\s*(\S+)\s+(\S+)/', $ext_ref, $matches)) {
|
||||
$externals[$matches[1]][$matches[2]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function uses:
|
||||
// - Explicit call
|
||||
// TODO?: String literal in ReflectionFunction().
|
||||
|
||||
$calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
|
||||
foreach ($calls as $call) {
|
||||
$name = $call->getChildByIndex(0);
|
||||
if ($name->getTypeName() == 'n_VARIABLE' ||
|
||||
$name->getTypeName() == 'n_VARIABLE_VARIABLE') {
|
||||
$requirements->addLint(
|
||||
$name,
|
||||
$name->getConcreteString(),
|
||||
ArcanistPhutilModuleLinter::LINT_ANALYZER_DYNAMIC,
|
||||
"Use of variable function calls prevents dependencies from being ".
|
||||
"checked statically. This module may have undetectable errors.");
|
||||
continue;
|
||||
}
|
||||
if ($name->getTypeName() == 'n_CLASS_STATIC_ACCESS') {
|
||||
// We'll pick this up later.
|
||||
continue;
|
||||
}
|
||||
|
||||
$call_name = $name->getConcreteString();
|
||||
if ($call_name == 'phutil_require_module') {
|
||||
analyze_phutil_require_module($call, $requirements, false);
|
||||
} else if ($call_name == 'call_user_func' ||
|
||||
$call_name == 'call_user_func_array') {
|
||||
$params = $call->getChildByIndex(1)->getChildren();
|
||||
if (count($params) == 0) {
|
||||
$requirements->addLint(
|
||||
$call,
|
||||
$call->getConcreteString(),
|
||||
ArcanistPhutilModuleLinter::LINT_ANALYZER_SIGNATURE,
|
||||
"Call to {$call_name}() must have at least one argument.");
|
||||
}
|
||||
$symbol = array_shift($params);
|
||||
$symbol_value = $symbol->getStringLiteralValue();
|
||||
if ($symbol_value) {
|
||||
$requirements->addFunctionDependency(
|
||||
$symbol,
|
||||
$symbol_value);
|
||||
} else {
|
||||
$requirements->addLint(
|
||||
$symbol,
|
||||
$symbol->getConcreteString(),
|
||||
ArcanistPhutilModuleLinter::LINT_ANALYZER_DYNAMIC,
|
||||
"Use of variable arguments to {$call_name} prevents dependencies ".
|
||||
"from being checked statically. This module may have undetectable ".
|
||||
"errors.");
|
||||
}
|
||||
} else if (empty($externals['function'][$name->getConcreteString()])) {
|
||||
$requirements->addFunctionDependency(
|
||||
$name,
|
||||
$name->getConcreteString());
|
||||
}
|
||||
}
|
||||
|
||||
$functions = $root->selectDescendantsOfType('n_FUNCTION_DECLARATION');
|
||||
foreach ($functions as $function) {
|
||||
$name = $function->getChildByIndex(2);
|
||||
if ($name->getTypeName() == 'n_EMPTY') {
|
||||
// This is an anonymous function; don't record it into the symbol
|
||||
// index.
|
||||
} else {
|
||||
$requirements->addFunctionDeclaration(
|
||||
$name,
|
||||
$name->getConcreteString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Class uses:
|
||||
// - new
|
||||
// - extends (in class declaration)
|
||||
// - Static method call
|
||||
// - Static property access
|
||||
// - Constant use
|
||||
// TODO?: String literal in ReflectionClass().
|
||||
// TODO?: String literal in array literal in call_user_func /
|
||||
// call_user_func_array().
|
||||
|
||||
// TODO: Raise a soft warning for use of an unknown class in:
|
||||
// - Typehints
|
||||
// - instanceof
|
||||
// - catch
|
||||
|
||||
$classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
|
||||
foreach ($classes as $class) {
|
||||
$class_name = $class->getChildByIndex(1);
|
||||
$requirements->addClassDeclaration(
|
||||
$class_name,
|
||||
$class_name->getConcreteString());
|
||||
$extends = $class->getChildByIndex(2);
|
||||
foreach ($extends->selectDescendantsOfType('n_CLASS_NAME') as $parent) {
|
||||
if (empty($externals['class'][$parent->getConcreteString()])) {
|
||||
$requirements->addClassDependency(
|
||||
$class_name->getConcreteString(),
|
||||
$parent,
|
||||
$parent->getConcreteString());
|
||||
}
|
||||
}
|
||||
$implements = $class->getChildByIndex(3);
|
||||
$interfaces = $implements->selectDescendantsOfType('n_CLASS_NAME');
|
||||
foreach ($interfaces as $interface) {
|
||||
if (empty($externals['interface'][$interface->getConcreteString()])) {
|
||||
$requirements->addInterfaceDependency(
|
||||
$class_name->getConcreteString(),
|
||||
$interface,
|
||||
$interface->getConcreteString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($classes) > 1) {
|
||||
foreach ($classes as $class) {
|
||||
$class_name = $class->getChildByIndex(1);
|
||||
$class_string = $class_name->getConcreteString();
|
||||
$requirements->addLint(
|
||||
$class_name,
|
||||
$class_string,
|
||||
ArcanistPhutilModuleLinter::LINT_ANALYZER_MULTIPLE_CLASSES,
|
||||
"This file declares more than one class. Declare only one class per ".
|
||||
"file.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$uses_of_new = $root->selectDescendantsOfType('n_NEW');
|
||||
foreach ($uses_of_new as $new_operator) {
|
||||
$name = $new_operator->getChildByIndex(0);
|
||||
if ($name->getTypeName() == 'n_VARIABLE' ||
|
||||
$name->getTypeName() == 'n_VARIABLE_VARIABLE') {
|
||||
$requirements->addLint(
|
||||
$name,
|
||||
$name->getConcreteString(),
|
||||
ArcanistPhutilModuleLinter::LINT_ANALYZER_DYNAMIC,
|
||||
"Use of variable class instantiation prevents dependencies from ".
|
||||
"being checked statically. This module may have undetectable ".
|
||||
"errors.");
|
||||
continue;
|
||||
}
|
||||
if (empty($externals['class'][$name->getConcreteString()])) {
|
||||
$requirements->addClassDependency(
|
||||
null,
|
||||
$name,
|
||||
$name->getConcreteString());
|
||||
}
|
||||
}
|
||||
|
||||
$static_uses = $root->selectDescendantsOfType('n_CLASS_STATIC_ACCESS');
|
||||
foreach ($static_uses as $static_use) {
|
||||
$name = $static_use->getChildByIndex(0);
|
||||
if ($name->getTypeName() != 'n_CLASS_NAME') {
|
||||
echo "WARNING UNLINTABLE\n";
|
||||
continue;
|
||||
}
|
||||
$name_concrete = $name->getConcreteString();
|
||||
$magic_names = array(
|
||||
'static' => true,
|
||||
'parent' => true,
|
||||
'self' => true,
|
||||
);
|
||||
if (isset($magic_names[$name_concrete])) {
|
||||
continue;
|
||||
}
|
||||
if (empty($externals['class'][$name_concrete])) {
|
||||
$requirements->addClassDependency(
|
||||
null,
|
||||
$name,
|
||||
$name_concrete);
|
||||
}
|
||||
}
|
||||
|
||||
// Interface uses:
|
||||
// - implements
|
||||
// - extends (in interface declaration)
|
||||
|
||||
$interfaces = $root->selectDescendantsOfType('n_INTERFACE_DECLARATION');
|
||||
foreach ($interfaces as $interface) {
|
||||
$interface_name = $interface->getChildByIndex(1);
|
||||
$requirements->addInterfaceDeclaration(
|
||||
$interface_name,
|
||||
$interface_name->getConcreteString());
|
||||
$extends = $interface->getChildByIndex(2);
|
||||
foreach ($extends->selectDescendantsOfType('n_CLASS_NAME') as $parent) {
|
||||
if (empty($externals['interface'][$parent->getConcreteString()])) {
|
||||
$requirements->addInterfaceDependency(
|
||||
$interface_name->getConcreteString(),
|
||||
$parent,
|
||||
$parent->getConcreteString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!$has_init && $has_files) {
|
||||
$requirements->addRawLint(
|
||||
ArcanistPhutilModuleLinter::LINT_ANALYZER_NO_INIT,
|
||||
"Create an __init__.php file in this module.");
|
||||
}
|
||||
|
||||
echo json_encode($requirements->toDictionary());
|
||||
|
||||
/**
|
||||
* Parses meaning from calls to phutil_require_module() in __init__.php files.
|
||||
*
|
||||
* @group module
|
||||
*/
|
||||
function analyze_phutil_require_module(
|
||||
XHPASTNode $call,
|
||||
PhutilModuleRequirements $requirements,
|
||||
$create_dependency) {
|
||||
|
||||
$name = $call->getChildByIndex(0);
|
||||
$params = $call->getChildByIndex(1)->getChildren();
|
||||
if (count($params) !== 2) {
|
||||
$requirements->addLint(
|
||||
$call,
|
||||
$call->getConcreteString(),
|
||||
ArcanistPhutilModuleLinter::LINT_ANALYZER_SIGNATURE,
|
||||
"Call to phutil_require_module() must have exactly two arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
$module_param = array_pop($params);
|
||||
$library_param = array_pop($params);
|
||||
|
||||
$library_value = $library_param->getStringLiteralValue();
|
||||
if ($library_value === null) {
|
||||
$requirements->addLint(
|
||||
$library_param,
|
||||
$library_param->getConcreteString(),
|
||||
ArcanistPhutilModuleLinter::LINT_ANALYZER_SIGNATURE,
|
||||
"phutil_require_module() parameters must be string literals.");
|
||||
return;
|
||||
}
|
||||
|
||||
$module_value = $module_param->getStringLiteralValue();
|
||||
if ($module_value === null) {
|
||||
$requirements->addLint(
|
||||
$module_param,
|
||||
$module_param->getConcreteString(),
|
||||
ArcanistPhutilModuleLinter::LINT_ANALYZER_SIGNATURE,
|
||||
"phutil_require_module() parameters must be string literals.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($create_dependency) {
|
||||
$requirements->addModuleDependency(
|
||||
$name,
|
||||
$library_value.':'.$module_value);
|
||||
}
|
||||
}
|
|
@ -1,210 +0,0 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 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);
|
||||
}
|
||||
|
||||
$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";
|
|
@ -63,7 +63,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistInstallCertificateWorkflow' => 'workflow/ArcanistInstallCertificateWorkflow.php',
|
||||
'ArcanistJSHintLinter' => 'lint/linter/ArcanistJSHintLinter.php',
|
||||
'ArcanistLandWorkflow' => 'workflow/ArcanistLandWorkflow.php',
|
||||
'ArcanistLiberateLintEngine' => 'lint/engine/ArcanistLiberateLintEngine.php',
|
||||
'ArcanistLiberateWorkflow' => 'workflow/ArcanistLiberateWorkflow.php',
|
||||
'ArcanistLicenseLinter' => 'lint/linter/ArcanistLicenseLinter.php',
|
||||
'ArcanistLintConsoleRenderer' => 'lint/renderer/ArcanistLintConsoleRenderer.php',
|
||||
|
@ -93,7 +92,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistPatchWorkflow' => 'workflow/ArcanistPatchWorkflow.php',
|
||||
'ArcanistPhpcsLinter' => 'lint/linter/ArcanistPhpcsLinter.php',
|
||||
'ArcanistPhutilLibraryLinter' => 'lint/linter/ArcanistPhutilLibraryLinter.php',
|
||||
'ArcanistPhutilModuleLinter' => 'lint/linter/ArcanistPhutilModuleLinter.php',
|
||||
'ArcanistPhutilTestCase' => 'unit/engine/phutil/ArcanistPhutilTestCase.php',
|
||||
'ArcanistPhutilTestCaseTestCase' => 'unit/engine/phutil/testcase/ArcanistPhutilTestCaseTestCase.php',
|
||||
'ArcanistPhutilTestSkippedException' => 'unit/engine/phutil/testcase/ArcanistPhutilTestSkippedException.php',
|
||||
|
@ -178,7 +176,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistInstallCertificateWorkflow' => 'ArcanistBaseWorkflow',
|
||||
'ArcanistJSHintLinter' => 'ArcanistLinter',
|
||||
'ArcanistLandWorkflow' => 'ArcanistBaseWorkflow',
|
||||
'ArcanistLiberateLintEngine' => 'ArcanistLintEngine',
|
||||
'ArcanistLiberateWorkflow' => 'ArcanistBaseWorkflow',
|
||||
'ArcanistLicenseLinter' => 'ArcanistLinter',
|
||||
'ArcanistLintConsoleRenderer' => 'ArcanistLintRenderer',
|
||||
|
@ -200,7 +197,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistPatchWorkflow' => 'ArcanistBaseWorkflow',
|
||||
'ArcanistPhpcsLinter' => 'ArcanistLinter',
|
||||
'ArcanistPhutilLibraryLinter' => 'ArcanistLinter',
|
||||
'ArcanistPhutilModuleLinter' => 'ArcanistLinter',
|
||||
'ArcanistPhutilTestCaseTestCase' => 'ArcanistPhutilTestCase',
|
||||
'ArcanistPhutilTestSkippedException' => 'Exception',
|
||||
'ArcanistPhutilTestTerminatedException' => 'Exception',
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Lint engine which powers 'arc liberate'.
|
||||
*
|
||||
* @group linter
|
||||
*/
|
||||
final class ArcanistLiberateLintEngine extends ArcanistLintEngine {
|
||||
|
||||
public function buildLinters() {
|
||||
// We just run the module linter, 'arc liberate' is only interested in
|
||||
// building __init__.php files.
|
||||
|
||||
$module_linter = new ArcanistPhutilModuleLinter();
|
||||
foreach ($this->getPaths() as $path) {
|
||||
$module_linter->addPath($path);
|
||||
}
|
||||
|
||||
return array($module_linter);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,536 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Applies rules for modules in Phutil libraries.
|
||||
*
|
||||
* @group linter
|
||||
*/
|
||||
final class ArcanistPhutilModuleLinter extends ArcanistLinter {
|
||||
|
||||
const LINT_UNDECLARED_CLASS = 1;
|
||||
const LINT_UNDECLARED_FUNCTION = 2;
|
||||
const LINT_UNDECLARED_INTERFACE = 3;
|
||||
const LINT_UNDECLARED_SOURCE = 4;
|
||||
const LINT_UNUSED_MODULE = 5;
|
||||
const LINT_UNUSED_SOURCE = 6;
|
||||
const LINT_INIT_REBUILD = 7;
|
||||
const LINT_UNKNOWN_CLASS = 8;
|
||||
const LINT_UNKNOWN_FUNCTION = 9;
|
||||
|
||||
const LINT_ANALYZER_SIGNATURE = 100;
|
||||
const LINT_ANALYZER_DYNAMIC = 101;
|
||||
const LINT_ANALYZER_NO_INIT = 102;
|
||||
const LINT_ANALYZER_MULTIPLE_CLASSES = 103;
|
||||
|
||||
public function getLintNameMap() {
|
||||
return array(
|
||||
self::LINT_UNDECLARED_CLASS => 'Use of Undeclared Class',
|
||||
self::LINT_UNDECLARED_FUNCTION => 'Use of Undeclared Function',
|
||||
self::LINT_UNDECLARED_INTERFACE => 'Use of Undeclared Interface',
|
||||
self::LINT_UNDECLARED_SOURCE => 'Use of Nonexistent File',
|
||||
self::LINT_UNUSED_SOURCE => 'Unused Source',
|
||||
self::LINT_UNUSED_MODULE => 'Unused Module',
|
||||
self::LINT_INIT_REBUILD => 'Rebuilt __init__.php File',
|
||||
self::LINT_UNKNOWN_CLASS => 'Unknown Class',
|
||||
self::LINT_UNKNOWN_FUNCTION => 'Unknown Function',
|
||||
self::LINT_ANALYZER_SIGNATURE => 'Analyzer: Bad Call Signature',
|
||||
self::LINT_ANALYZER_DYNAMIC => 'Analyzer: Dynamic Dependency',
|
||||
self::LINT_ANALYZER_NO_INIT => 'Analyzer: No __init__.php File',
|
||||
self::LINT_ANALYZER_MULTIPLE_CLASSES
|
||||
=> 'Analyzer: File Declares Multiple Classes',
|
||||
);
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'PHU';
|
||||
}
|
||||
|
||||
public function getLintSeverityMap() {
|
||||
return array(
|
||||
self::LINT_ANALYZER_DYNAMIC => ArcanistLintSeverity::SEVERITY_WARNING,
|
||||
);
|
||||
}
|
||||
|
||||
private $moduleInfo = array();
|
||||
private $unknownClasses = array();
|
||||
private $unknownFunctions = array();
|
||||
|
||||
private function setModuleInfo($key, array $info) {
|
||||
$this->moduleInfo[$key] = $info;
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function getModulePathOnDisk($key) {
|
||||
$info = $this->moduleInfo[$key];
|
||||
return $info['root'].'/'.$info['module'];
|
||||
}
|
||||
|
||||
private function getModuleDisplayName($key) {
|
||||
$info = $this->moduleInfo[$key];
|
||||
return $info['module'];
|
||||
}
|
||||
|
||||
private function isPhutilLibraryMetadata($path) {
|
||||
$file = basename($path);
|
||||
return !strncmp('__phutil_library_', $file, strlen('__phutil_library_'));
|
||||
}
|
||||
|
||||
public function willLintPaths(array $paths) {
|
||||
|
||||
if ($paths) {
|
||||
if (!xhpast_is_available()) {
|
||||
throw new Exception(xhpast_get_build_instructions());
|
||||
}
|
||||
}
|
||||
|
||||
$modules = array();
|
||||
$moduleinfo = array();
|
||||
|
||||
$project_root = $this->getEngine()->getWorkingCopy()->getProjectRoot();
|
||||
$bootloader = PhutilBootloader::getInstance();
|
||||
|
||||
foreach ($paths as $path) {
|
||||
$absolute_path = $project_root.'/'.$path;
|
||||
$library_root = phutil_get_library_root_for_path($absolute_path);
|
||||
if (!$library_root) {
|
||||
continue;
|
||||
}
|
||||
if ($this->isPhutilLibraryMetadata($path)) {
|
||||
continue;
|
||||
}
|
||||
$library_name = phutil_get_library_name_for_root($library_root);
|
||||
|
||||
$version = $bootloader->getLibraryFormatVersion($library_name);
|
||||
if ($version != 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_dir($path)) {
|
||||
$path = dirname($path);
|
||||
}
|
||||
$path = Filesystem::resolvePath(
|
||||
$path,
|
||||
$project_root);
|
||||
if ($path == $library_root) {
|
||||
continue;
|
||||
}
|
||||
$module_name = Filesystem::readablePath($path, $library_root);
|
||||
$module_key = $library_name.':'.$module_name;
|
||||
if (empty($modules[$module_key])) {
|
||||
$modules[$module_key] = $module_key;
|
||||
$this->setModuleInfo($module_key, array(
|
||||
'library' => $library_name,
|
||||
'root' => $library_root,
|
||||
'module' => $module_name,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$modules) {
|
||||
return;
|
||||
}
|
||||
|
||||
$modules = array_keys($modules);
|
||||
|
||||
$arc_root = phutil_get_library_root('arcanist');
|
||||
$bin = dirname($arc_root).'/scripts/phutil_analyzer.php';
|
||||
|
||||
$futures = array();
|
||||
foreach ($modules as $mkey => $key) {
|
||||
$disk_path = $this->getModulePathOnDisk($key);
|
||||
if (Filesystem::pathExists($disk_path)) {
|
||||
$futures[$key] = new ExecFuture(
|
||||
'%s %s',
|
||||
$bin,
|
||||
$disk_path);
|
||||
} else {
|
||||
// This can occur in git when you add a module in HEAD and then remove
|
||||
// it in unstaged changes in the working copy. Just ignore it.
|
||||
unset($modules[$mkey]);
|
||||
}
|
||||
}
|
||||
|
||||
$requirements = array();
|
||||
foreach (Futures($futures)->limit(16) as $key => $future) {
|
||||
$requirements[$key] = $future->resolveJSON();
|
||||
}
|
||||
|
||||
$dependencies = array();
|
||||
$futures = array();
|
||||
foreach ($requirements as $key => $requirement) {
|
||||
foreach ($requirement['messages'] as $message) {
|
||||
list($where, $text, $code, $description) = $message;
|
||||
if ($where) {
|
||||
$where = array($where);
|
||||
}
|
||||
$this->raiseLintInModule(
|
||||
$key,
|
||||
$code,
|
||||
$description,
|
||||
$where,
|
||||
$text);
|
||||
}
|
||||
|
||||
foreach ($requirement['requires']['module'] as $req_module => $where) {
|
||||
if (isset($requirements[$req_module])) {
|
||||
$dependencies[$req_module] = $requirements[$req_module];
|
||||
} else {
|
||||
list($library_name, $module_name) = explode(':', $req_module);
|
||||
$library_root = phutil_get_library_root($library_name);
|
||||
$this->setModuleInfo($req_module, array(
|
||||
'library' => $library_name,
|
||||
'root' => $library_root,
|
||||
'module' => $module_name,
|
||||
));
|
||||
$disk_path = $this->getModulePathOnDisk($req_module);
|
||||
if (Filesystem::pathExists($disk_path)) {
|
||||
$futures[$req_module] = new ExecFuture(
|
||||
'%s %s',
|
||||
$bin,
|
||||
$disk_path);
|
||||
} else {
|
||||
$dependencies[$req_module] = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Futures($futures)->limit(16) as $key => $future) {
|
||||
$dependencies[$key] = $future->resolveJSON();
|
||||
}
|
||||
|
||||
foreach ($requirements as $key => $spec) {
|
||||
$deps = array_intersect_key(
|
||||
$dependencies,
|
||||
$spec['requires']['module']);
|
||||
$this->lintModule($key, $spec, $deps);
|
||||
}
|
||||
}
|
||||
|
||||
private function lintModule($key, $spec, $deps) {
|
||||
$resolvable = array();
|
||||
$need_classes = array();
|
||||
$need_functions = array();
|
||||
$drop_modules = array();
|
||||
|
||||
$used = array();
|
||||
static $types = array(
|
||||
'class' => self::LINT_UNDECLARED_CLASS,
|
||||
'interface' => self::LINT_UNDECLARED_INTERFACE,
|
||||
'function' => self::LINT_UNDECLARED_FUNCTION,
|
||||
);
|
||||
foreach ($types as $type => $lint_code) {
|
||||
foreach ($spec['requires'][$type] as $name => $places) {
|
||||
$declared = $this->checkDependency(
|
||||
$type,
|
||||
$name,
|
||||
$deps);
|
||||
if (!$declared) {
|
||||
$module = $this->getModuleDisplayName($key);
|
||||
$message = $this->raiseLintInModule(
|
||||
$key,
|
||||
$lint_code,
|
||||
"Module '{$module}' uses {$type} '{$name}' but does not include ".
|
||||
"any module which declares it.",
|
||||
$places);
|
||||
|
||||
if ($type == 'class' || $type == 'interface') {
|
||||
$loader = new PhutilSymbolLoader();
|
||||
$loader->setType($type);
|
||||
$loader->setName($name);
|
||||
$symbols = $loader->selectSymbolsWithoutLoading();
|
||||
if ($symbols) {
|
||||
$class_spec = reset($symbols);
|
||||
try {
|
||||
$loader->selectAndLoadSymbols();
|
||||
$loaded = true;
|
||||
} catch (PhutilMissingSymbolException $ex) {
|
||||
$loaded = false;
|
||||
} catch (PhutilBootloaderException $ex) {
|
||||
$loaded = false;
|
||||
}
|
||||
if ($loaded) {
|
||||
$resolvable[] = $message;
|
||||
$need_classes[$name] = $class_spec;
|
||||
} else {
|
||||
if (empty($this->unknownClasses[$name])) {
|
||||
$this->unknownClasses[$name] = true;
|
||||
$library = $class_spec['library'];
|
||||
$this->raiseLintInModule(
|
||||
$key,
|
||||
self::LINT_UNKNOWN_CLASS,
|
||||
"Class '{$name}' exists in the library map for library ".
|
||||
"'{$library}', but could not be loaded. You may need to ".
|
||||
"rebuild the library map.",
|
||||
$places);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (empty($this->unknownClasses[$name])) {
|
||||
$this->unknownClasses[$name] = true;
|
||||
$this->raiseLintInModule(
|
||||
$key,
|
||||
self::LINT_UNKNOWN_CLASS,
|
||||
"Class '{$name}' could not be found in any known library. ".
|
||||
"You may need to rebuild the map for the library which ".
|
||||
"contains it.",
|
||||
$places);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$loader = new PhutilSymbolLoader();
|
||||
$loader->setType($type);
|
||||
$loader->setName($name);
|
||||
$symbols = $loader->selectSymbolsWithoutLoading();
|
||||
if ($symbols) {
|
||||
$func_spec = reset($symbols);
|
||||
try {
|
||||
$loader->selectAndLoadSymbols();
|
||||
$loaded = true;
|
||||
} catch (PhutilMissingSymbolException $ex) {
|
||||
$loaded = false;
|
||||
} catch (PhutilBootloaderException $ex) {
|
||||
$loaded = false;
|
||||
}
|
||||
if ($loaded) {
|
||||
$resolvable[] = $message;
|
||||
$need_functions[$name] = $func_spec;
|
||||
} else {
|
||||
if (empty($this->unknownFunctions[$name])) {
|
||||
$this->unknownFunctions[$name] = true;
|
||||
$library = $func_spec['library'];
|
||||
$this->raiseLintInModule(
|
||||
$key,
|
||||
self::LINT_UNKNOWN_FUNCTION,
|
||||
"Function '{$name}' exists in the library map for library ".
|
||||
"'{$library}', but could not be loaded. You may need to ".
|
||||
"rebuild the library map.",
|
||||
$places);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (empty($this->unknownFunctions[$name])) {
|
||||
$this->unknownFunctions[$name] = true;
|
||||
$this->raiseLintInModule(
|
||||
$key,
|
||||
self::LINT_UNKNOWN_FUNCTION,
|
||||
"Function '{$name}' could not be found in any known ".
|
||||
"library. You may need to rebuild the map for the library ".
|
||||
"which contains it.",
|
||||
$places);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$used[$declared] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$unused = array_diff_key($deps, $used);
|
||||
foreach ($unused as $unused_module_key => $ignored) {
|
||||
$module = $this->getModuleDisplayName($key);
|
||||
$unused_module = $this->getModuleDisplayName($unused_module_key);
|
||||
$resolvable[] = $this->raiseLintInModule(
|
||||
$key,
|
||||
self::LINT_UNUSED_MODULE,
|
||||
"Module '{$module}' requires module '{$unused_module}' but does not ".
|
||||
"use anything it declares.",
|
||||
$spec['requires']['module'][$unused_module_key]);
|
||||
$drop_modules[] = $unused_module_key;
|
||||
}
|
||||
|
||||
foreach ($spec['requires']['source'] as $file => $where) {
|
||||
if (empty($spec['declares']['source'][$file])) {
|
||||
$module = $this->getModuleDisplayName($key);
|
||||
$resolvable[] = $this->raiseLintInModule(
|
||||
$key,
|
||||
self::LINT_UNDECLARED_SOURCE,
|
||||
"Module '{$module}' requires source '{$file}', but it does not ".
|
||||
"exist.",
|
||||
$where);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($spec['declares']['source'] as $file => $ignored) {
|
||||
if (empty($spec['requires']['source'][$file])) {
|
||||
$module = $this->getModuleDisplayName($key);
|
||||
$resolvable[] = $this->raiseLintInModule(
|
||||
$key,
|
||||
self::LINT_UNUSED_SOURCE,
|
||||
"Module '{$module}' does not include source file '{$file}'.",
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
if ($resolvable) {
|
||||
$new_file = $this->buildNewModuleInit(
|
||||
$key,
|
||||
$spec,
|
||||
$need_classes,
|
||||
$need_functions,
|
||||
$drop_modules);
|
||||
$init_path = $this->getModulePathOnDisk($key).'/__init__.php';
|
||||
|
||||
$root = $this->getEngine()->getWorkingCopy()->getProjectRoot();
|
||||
$try_path = Filesystem::readablePath($init_path, $root);
|
||||
$full_path = Filesystem::resolvePath($try_path, $root);
|
||||
if (Filesystem::pathExists($full_path)) {
|
||||
$init_path = $try_path;
|
||||
$old_file = Filesystem::readFile($full_path);
|
||||
} else {
|
||||
$old_file = '';
|
||||
}
|
||||
|
||||
$this->willLintPath($init_path);
|
||||
$message = $this->raiseLintAtOffset(
|
||||
null,
|
||||
self::LINT_INIT_REBUILD,
|
||||
"This generated phutil '__init__.php' file is suggested to address ".
|
||||
"lint problems with static dependencies in the module.",
|
||||
$old_file,
|
||||
$new_file);
|
||||
$message->setDependentMessages($resolvable);
|
||||
foreach ($resolvable as $resolvable_message) {
|
||||
$resolvable_message->setObsolete(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function buildNewModuleInit(
|
||||
$key,
|
||||
$spec,
|
||||
$need_classes,
|
||||
$need_functions,
|
||||
$drop_modules) {
|
||||
|
||||
$init = array();
|
||||
$init[] = '<?php';
|
||||
|
||||
$at = '@';
|
||||
$init[] = <<<EOHEADER
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* {$at}generated
|
||||
*/
|
||||
|
||||
EOHEADER;
|
||||
$init[] = null;
|
||||
|
||||
$modules = $spec['requires']['module'];
|
||||
|
||||
foreach ($drop_modules as $drop) {
|
||||
unset($modules[$drop]);
|
||||
}
|
||||
|
||||
foreach ($need_classes as $need => $class_spec) {
|
||||
$modules[$class_spec['library'].':'.$class_spec['module']] = true;
|
||||
}
|
||||
|
||||
foreach ($need_functions as $need => $func_spec) {
|
||||
$modules[$func_spec['library'].':'.$func_spec['module']] = true;
|
||||
}
|
||||
|
||||
ksort($modules);
|
||||
|
||||
$last = null;
|
||||
foreach ($modules as $module_key => $ignored) {
|
||||
|
||||
if (is_array($ignored)) {
|
||||
$in_init = false;
|
||||
$in_file = false;
|
||||
foreach ($ignored as $where) {
|
||||
list($file, $line) = explode(':', $where);
|
||||
if ($file == '__init__.php') {
|
||||
$in_init = true;
|
||||
} else {
|
||||
$in_file = true;
|
||||
}
|
||||
}
|
||||
if ($in_file && !$in_init) {
|
||||
// If this is a runtime include, don't try to put it in the
|
||||
// __init__ file.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
list($library, $module_name) = explode(':', $module_key);
|
||||
if ($last != $library) {
|
||||
$last = $library;
|
||||
if ($last != null) {
|
||||
$init[] = null;
|
||||
}
|
||||
}
|
||||
|
||||
$library = "'".addcslashes($library, "'\\")."'";
|
||||
$module_name = "'".addcslashes($module_name, "'\\")."'";
|
||||
|
||||
$init[] = "phutil_require_module({$library}, {$module_name});";
|
||||
}
|
||||
|
||||
$init[] = null;
|
||||
$init[] = null;
|
||||
|
||||
$files = array_keys($spec['declares']['source']);
|
||||
sort($files);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$file = "'".addcslashes($file, "'\\")."'";
|
||||
$init[] = "phutil_require_source({$file});";
|
||||
}
|
||||
$init[] = null;
|
||||
|
||||
return implode("\n", $init);
|
||||
}
|
||||
|
||||
private function checkDependency($type, $name, $deps) {
|
||||
foreach ($deps as $key => $dep) {
|
||||
if (isset($dep['declares'][$type][$name])) {
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function raiseLintInModule($key, $code, $desc, $places, $text = null) {
|
||||
if ($places) {
|
||||
foreach ($places as $place) {
|
||||
list($file, $offset) = explode(':', $place);
|
||||
$this->willLintPath(
|
||||
Filesystem::readablePath(
|
||||
$this->getModulePathOnDisk($key).'/'.$file,
|
||||
$this->getEngine()->getWorkingCopy()->getProjectRoot()));
|
||||
return $this->raiseLintAtOffset(
|
||||
$offset,
|
||||
$code,
|
||||
$desc,
|
||||
$text);
|
||||
}
|
||||
} else {
|
||||
$this->willLintPath($this->getModuleDisplayName($key));
|
||||
return $this->raiseLintAtPath(
|
||||
$code,
|
||||
$desc);
|
||||
}
|
||||
}
|
||||
|
||||
public function lintPath($path) {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
|
@ -131,7 +131,9 @@ EOTEXT
|
|||
if ($this->getArgument('upgrade')) {
|
||||
return $this->upgradeLibrary($path);
|
||||
}
|
||||
return $this->liberateVersion1($path);
|
||||
throw new ArcanistUsageException(
|
||||
"This library is using libphutil v1, which is no longer supported. ".
|
||||
"Run 'arc liberate --upgrade' to upgrade to v2.");
|
||||
case 2:
|
||||
if ($this->getArgument('upgrade')) {
|
||||
throw new ArcanistUsageException(
|
||||
|
@ -186,139 +188,6 @@ EOTEXT
|
|||
$this->liberateVersion2($path);
|
||||
}
|
||||
|
||||
private function liberateVersion1($path) {
|
||||
|
||||
if ($this->getArgument('remap')) {
|
||||
return $this->liberateRunRemap($path);
|
||||
}
|
||||
|
||||
if ($this->getArgument('verify')) {
|
||||
return $this->liberateRunVerify($path);
|
||||
}
|
||||
|
||||
$readable = Filesystem::readablePath($path);
|
||||
echo "Using library root at '{$readable}'...\n";
|
||||
|
||||
$this->checkForLooseFiles($path);
|
||||
|
||||
if ($this->getArgument('all')) {
|
||||
echo "Dropping module cache...\n";
|
||||
Filesystem::remove($path.'/.phutil_module_cache');
|
||||
}
|
||||
|
||||
echo "Mapping library...\n";
|
||||
|
||||
// Force a rebuild of the library map before running lint. The remap
|
||||
// operation will load the map before regenerating it, so if a class has
|
||||
// been renamed (say, from OldClass to NewClass) this rebuild will
|
||||
// cause the initial remap to see NewClass and correctly remove includes
|
||||
// caused by use of OldClass.
|
||||
$this->liberateGetChangedPaths($path);
|
||||
|
||||
$arc_bin = $this->getScriptPath('bin/arc');
|
||||
|
||||
do {
|
||||
$future = new ExecFuture(
|
||||
'%s liberate --remap -- %s',
|
||||
$arc_bin,
|
||||
$path);
|
||||
$wrote = $future->resolveJSON();
|
||||
foreach ($wrote as $wrote_path) {
|
||||
echo "Updated '{$wrote_path}'...\n";
|
||||
}
|
||||
} while ($wrote);
|
||||
|
||||
echo "Verifying library...\n";
|
||||
|
||||
$err = phutil_passthru('%s liberate --verify -- %s', $arc_bin, $path);
|
||||
|
||||
$do_update = (!$err || $this->getArgument('force-update'));
|
||||
|
||||
if ($do_update) {
|
||||
echo "Finalizing library map...\n";
|
||||
execx('%s %s', $this->getPhutilMapperLocation(), $path);
|
||||
}
|
||||
|
||||
if ($err) {
|
||||
if ($do_update) {
|
||||
echo phutil_console_format(
|
||||
"<bg:yellow>** WARNING **</bg> Library update forced, but lint ".
|
||||
"failures remain.\n");
|
||||
} else {
|
||||
echo phutil_console_format(
|
||||
"<bg:red>** UNRESOLVED LINT ERRORS **</bg> This library has ".
|
||||
"unresolved lint failures. The library map was not updated. Use ".
|
||||
"--force-update to force an update.\n");
|
||||
}
|
||||
} else {
|
||||
echo phutil_console_format(
|
||||
"<bg:green>** OKAY **</bg> Library updated.\n");
|
||||
}
|
||||
|
||||
return $err;
|
||||
}
|
||||
|
||||
private function liberateLintModules($path, array $changed) {
|
||||
$engine = $this->liberateBuildLintEngine($path, $changed);
|
||||
if ($engine) {
|
||||
return $engine->run();
|
||||
} else {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
private function liberateWritePatches(array $results) {
|
||||
assert_instances_of($results, 'ArcanistLintResult');
|
||||
$wrote = array();
|
||||
|
||||
foreach ($results as $result) {
|
||||
if ($result->isPatchable()) {
|
||||
$patcher = ArcanistLintPatcher::newFromArcanistLintResult($result);
|
||||
$patcher->writePatchToDisk();
|
||||
$wrote[] = $result->getPath();
|
||||
}
|
||||
}
|
||||
|
||||
return $wrote;
|
||||
}
|
||||
|
||||
private function liberateBuildLintEngine($path, array $changed) {
|
||||
$lint_map = array();
|
||||
foreach ($changed as $module) {
|
||||
$module_path = $path.'/'.$module;
|
||||
$files = Filesystem::listDirectory($module_path);
|
||||
$lint_map[$module] = $files;
|
||||
}
|
||||
|
||||
$working_copy = ArcanistWorkingCopyIdentity::newFromRootAndConfigFile(
|
||||
$path,
|
||||
json_encode(
|
||||
array(
|
||||
'project_id' => '__arcliberate__',
|
||||
)),
|
||||
'arc liberate');
|
||||
|
||||
$engine = new ArcanistLiberateLintEngine();
|
||||
$engine->setWorkingCopy($working_copy);
|
||||
|
||||
$lint_paths = array();
|
||||
foreach ($lint_map as $module => $files) {
|
||||
foreach ($files as $file) {
|
||||
$lint_paths[] = $module.'/'.$file;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$lint_paths) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$engine->setPaths($lint_paths);
|
||||
$engine->setMinimumSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
|
||||
|
||||
return $engine;
|
||||
}
|
||||
|
||||
|
||||
private function liberateCreateDirectory($path) {
|
||||
if (Filesystem::pathExists($path)) {
|
||||
if (!is_dir($path)) {
|
||||
|
@ -361,72 +230,10 @@ EOTEXT
|
|||
Filesystem::writeFile($init_path, $template);
|
||||
}
|
||||
|
||||
private function liberateGetChangedPaths($path) {
|
||||
$mapper = $this->getPhutilMapperLocation();
|
||||
$future = new ExecFuture('%s %s --find-paths-for-liberate', $mapper, $path);
|
||||
return $future->resolveJSON();
|
||||
}
|
||||
|
||||
private function getScriptPath($script) {
|
||||
$root = dirname(phutil_get_library_root('arcanist'));
|
||||
return $root.'/'.$script;
|
||||
}
|
||||
|
||||
private function getPhutilMapperLocation() {
|
||||
return $this->getScriptPath('scripts/phutil_mapper.php');
|
||||
}
|
||||
|
||||
private function liberateRunRemap($path) {
|
||||
phutil_load_library($path);
|
||||
|
||||
$paths = $this->liberateGetChangedPaths($path);
|
||||
$results = $this->liberateLintModules($path, $paths);
|
||||
$wrote = $this->liberateWritePatches($results);
|
||||
|
||||
echo json_encode($wrote, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function liberateRunVerify($path) {
|
||||
phutil_load_library($path);
|
||||
|
||||
$paths = $this->liberateGetChangedPaths($path);
|
||||
$results = $this->liberateLintModules($path, $paths);
|
||||
|
||||
$renderer = new ArcanistLintConsoleRenderer();
|
||||
|
||||
$unresolved = false;
|
||||
foreach ($results as $result) {
|
||||
foreach ($result->getMessages() as $message) {
|
||||
echo $renderer->renderLintResult($result);
|
||||
$unresolved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)$unresolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanity check to catch people putting class files in the root of a libphutil
|
||||
* library.
|
||||
*/
|
||||
private function checkForLooseFiles($path) {
|
||||
foreach (Filesystem::listDirectory($path) as $item) {
|
||||
if (!preg_match('/\.php$/', $item)) {
|
||||
// Not a ".php" file.
|
||||
continue;
|
||||
}
|
||||
if (preg_match('/^__/', $item)) {
|
||||
// Has magic '__' prefix.
|
||||
continue;
|
||||
}
|
||||
|
||||
echo phutil_console_wrap(
|
||||
"WARNING: File '{$item}' is not in a module and won't be loaded. ".
|
||||
"Put source files in subdirectories, not the top level directory.\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue