Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F18735403
D19677.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
34 KB
Referenced Files
None
Subscribers
None
D19677.diff
View Options
diff --git a/bin/arc b/bin/arc
--- a/bin/arc
+++ b/bin/arc
@@ -1,21 +1,14 @@
-#!/usr/bin/env bash
+#!/usr/bin/env php
+<?php
-# NOTE: This file is a wrapper script instead of a symlink so it will work in
-# the Git Bash environment in Windows.
+if (function_exists('pcntl_async_signals')) {
+ pcntl_async_signals(true);
+} else {
+ declare(ticks = 1);
+}
-# Do bash magic to resolve the real location of this script through aliases,
-# symlinks, etc.
-SOURCE="${BASH_SOURCE[0]}";
-while [ -h "$SOURCE" ]; do
- LINK="$(readlink "$SOURCE")";
- if [ "${LINK:0:1}" == "/" ]; then
- # absolute symlink
- SOURCE="$LINK"
- else
- # relative symlink
- SOURCE="$(cd -P "$(dirname "$SOURCE")" && pwd)/$LINK"
- fi
-done;
-DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
+require_once dirname(dirname(__FILE__)).'/scripts/init/init-arcanist.php';
+
+$runtime = new ArcanistRuntime();
+return $runtime->execute($argv);
-exec "$DIR/../scripts/arcanist.php" "$@"
diff --git a/bin/arc.bat b/bin/arc.bat
--- a/bin/arc.bat
+++ b/bin/arc.bat
@@ -1,2 +1,2 @@
@echo off
-php -f "%~dp0..\scripts\arcanist.php" -- %*
+php -f "%~dp0..\bin\arc" -- %*
diff --git a/bin/phage b/bin/phage
old mode 120000
new mode 100755
--- /dev/null
+++ b/bin/phage
@@ -0,0 +1,14 @@
+#!/usr/bin/env php
+<?php
+
+if (function_exists('pcntl_async_signals')) {
+ pcntl_async_signals(true);
+} else {
+ declare(ticks = 1);
+}
+
+require_once dirname(dirname(__FILE__)).'/scripts/init/init-arcanist.php';
+
+$runtime = new ArcanistRuntime();
+return $runtime->execute($argv);
+
diff --git a/scripts/arcanist.php b/scripts/arcanist.php
--- a/scripts/arcanist.php
+++ b/scripts/arcanist.php
@@ -1,197 +1,32 @@
#!/usr/bin/env php
<?php
-sanity_check_environment();
-
-require_once dirname(__FILE__).'/__init_script__.php';
-
-ini_set('memory_limit', -1);
-
-$original_argv = $argv;
-$base_args = new PhutilArgumentParser($argv);
-$base_args->parseStandardArguments();
-$base_args->parsePartial(
- array(
- array(
- 'name' => 'load-phutil-library',
- 'param' => 'path',
- 'help' => pht('Load a libphutil library.'),
- 'repeat' => true,
- ),
- array(
- 'name' => 'skip-arcconfig',
- ),
- array(
- 'name' => 'arcrc-file',
- 'param' => 'filename',
- ),
- array(
- 'name' => 'conduit-uri',
- 'param' => 'uri',
- 'help' => pht('Connect to Phabricator install specified by __uri__.'),
- ),
- array(
- 'name' => 'conduit-token',
- 'param' => 'token',
- 'help' => pht('Use a specific authentication token.'),
- ),
- array(
- 'name' => 'anonymous',
- 'help' => pht('Run workflow as a public user, without authenticating.'),
- ),
- array(
- 'name' => 'conduit-version',
- 'param' => 'version',
- 'help' => pht(
- '(Developers) Mock client version in protocol handshake.'),
- ),
- array(
- 'name' => 'conduit-timeout',
- 'param' => 'timeout',
- 'help' => pht('Set Conduit timeout (in seconds).'),
- ),
- array(
- 'name' => 'config',
- 'param' => 'key=value',
- 'repeat' => true,
- 'help' => pht(
- 'Specify a runtime configuration value. This will take precedence '.
- 'over static values, and only affect the current arcanist invocation.'),
- ),
-));
+if (function_exists('pcntl_async_signals')) {
+ pcntl_async_signals(true);
+} else {
+ declare(ticks = 1);
+}
+
+require_once dirname(dirname(__FILE__)).'/scripts/init/init-arcanist.php';
+
+$runtime = new ArcanistRuntime();
+return $runtime->execute($argv);
+
$config_trace_mode = $base_args->getArg('trace');
$force_conduit = $base_args->getArg('conduit-uri');
$force_token = $base_args->getArg('conduit-token');
-$force_conduit_version = $base_args->getArg('conduit-version');
-$conduit_timeout = $base_args->getArg('conduit-timeout');
-$skip_arcconfig = $base_args->getArg('skip-arcconfig');
-$custom_arcrc = $base_args->getArg('arcrc-file');
$is_anonymous = $base_args->getArg('anonymous');
$load = $base_args->getArg('load-phutil-library');
$help = $base_args->getArg('help');
$args = array_values($base_args->getUnconsumedArgumentVector());
-$working_directory = getcwd();
$console = PhutilConsole::getConsole();
$config = null;
$workflow = null;
try {
- if ($config_trace_mode) {
- echo tsprintf(
- "**<bg:magenta> %s </bg>** %s\n",
- pht('ARGV'),
- csprintf('%Ls', $original_argv));
-
- $libraries = array(
- 'phutil',
- 'arcanist',
- );
-
- foreach ($libraries as $library_name) {
- echo tsprintf(
- "**<bg:magenta> %s </bg>** %s\n",
- pht('LOAD'),
- pht(
- 'Loaded "%s" from "%s".',
- $library_name,
- phutil_get_library_root($library_name)));
- }
- }
-
- if (!$args) {
- if ($help) {
- $args = array('help');
- } else {
- throw new ArcanistUsageException(
- pht('No command provided. Try `%s`.', 'arc help'));
- }
- } else if ($help) {
- array_unshift($args, 'help');
- }
-
- $configuration_manager = new ArcanistConfigurationManager();
- if ($custom_arcrc) {
- $configuration_manager->setUserConfigurationFileLocation($custom_arcrc);
- }
-
- $global_config = $configuration_manager->readUserArcConfig();
- $system_config = $configuration_manager->readSystemArcConfig();
- $runtime_config = $configuration_manager->applyRuntimeArcConfig($base_args);
-
- if ($skip_arcconfig) {
- $working_copy = ArcanistWorkingCopyIdentity::newDummyWorkingCopy();
- } else {
- $working_copy =
- ArcanistWorkingCopyIdentity::newFromPath($working_directory);
- }
- $configuration_manager->setWorkingCopyIdentity($working_copy);
-
- // Load additional libraries, which can provide new classes like configuration
- // overrides, linters and lint engines, unit test engines, etc.
-
- // If the user specified "--load-phutil-library" one or more times from
- // the command line, we load those libraries **instead** of whatever else
- // is configured. This is basically a debugging feature to let you force
- // specific libraries to load regardless of the state of the world.
- if ($load) {
- $console->writeLog(
- "%s\n",
- pht(
- 'Using `%s` flag, configuration will be ignored and configured '.
- 'libraries will not be loaded.',
- '--load-phutil-library'));
- // Load the flag libraries. These must load, since the user specified them
- // explicitly.
- arcanist_load_libraries(
- $load,
- $must_load = true,
- $lib_source = pht('a "%s" flag', '--load-phutil-library'),
- $working_copy);
- } else {
- // Load libraries in system 'load' config. In contrast to global config, we
- // fail hard here because this file is edited manually, so if 'arc' breaks
- // that doesn't make it any more difficult to correct.
- arcanist_load_libraries(
- idx($system_config, 'load', array()),
- $must_load = true,
- $lib_source = pht('the "%s" setting in system config', 'load'),
- $working_copy);
-
- // Load libraries in global 'load' config, as per "arc set-config load". We
- // need to fail softly if these break because errors would prevent the user
- // from running "arc set-config" to correct them.
- arcanist_load_libraries(
- idx($global_config, 'load', array()),
- $must_load = false,
- $lib_source = pht('the "%s" setting in global config', 'load'),
- $working_copy);
-
- // Load libraries in ".arcconfig". Libraries here must load.
- arcanist_load_libraries(
- $working_copy->getProjectConfig('load'),
- $must_load = true,
- $lib_source = pht('the "%s" setting in "%s"', 'load', '.arcconfig'),
- $working_copy);
-
- // Load libraries in ".arcconfig". Libraries here must load.
- arcanist_load_libraries(
- idx($runtime_config, 'load', array()),
- $must_load = true,
- $lib_source = pht('the %s argument', '--config "load=[...]"'),
- $working_copy);
- }
-
- $user_config = $configuration_manager->readUserConfigurationFile();
-
- $config_class = $working_copy->getProjectConfig('arcanist_configuration');
- if ($config_class) {
- $config = new $config_class();
- } else {
- $config = new ArcanistConfiguration();
- }
$command = strtolower($args[0]);
$args = array_slice($args, 1);
@@ -211,13 +46,6 @@
// their behaviors.
putenv('ARCANIST='.$command);
- if ($force_conduit_version) {
- $workflow->forceConduitVersion($force_conduit_version);
- }
- if ($conduit_timeout) {
- $workflow->setConduitTimeout($conduit_timeout);
- }
-
$need_working_copy = $workflow->requiresWorkingCopy();
$supported_vcs_types = $workflow->getSupportedRevisionControlSystems();
@@ -353,10 +181,6 @@
->setBasicAuthUser($basic_user)
->setBasicAuthPass($basic_pass);
- if ($conduit_timeout) {
- $engine->setConduitTimeout($conduit_timeout);
- }
-
$workflow->setConduitEngine($engine);
if ($need_auth) {
@@ -470,230 +294,6 @@
}
-/**
- * Perform some sanity checks against the possible diversity of PHP builds in
- * the wild, like very old versions and builds that were compiled with flags
- * that exclude core functionality.
- */
-function sanity_check_environment() {
- // NOTE: We don't have phutil_is_windows() yet here.
- $is_windows = (DIRECTORY_SEPARATOR != '/');
-
- // We use stream_socket_pair() which is not available on Windows earlier.
- $min_version = ($is_windows ? '5.3.0' : '5.2.3');
- $cur_version = phpversion();
- if (version_compare($cur_version, $min_version, '<')) {
- die_with_bad_php(
- "You are running PHP version '{$cur_version}', which is older than ".
- "the minimum version, '{$min_version}'. Update to at least ".
- "'{$min_version}'.");
- }
-
- if ($is_windows) {
- $need_functions = array(
- 'curl_init' => array('builtin-dll', 'php_curl.dll'),
- );
- } else {
- $need_functions = array(
- 'curl_init' => array(
- 'text',
- "You need to install the cURL PHP extension, maybe with ".
- "'apt-get install php5-curl' or 'yum install php53-curl' or ".
- "something similar.",
- ),
- 'json_decode' => array('flag', '--without-json'),
- );
- }
-
- $problems = array();
-
- $config = null;
- $show_config = false;
- foreach ($need_functions as $fname => $resolution) {
- if (function_exists($fname)) {
- continue;
- }
-
- static $info;
- if ($info === null) {
- ob_start();
- phpinfo(INFO_GENERAL);
- $info = ob_get_clean();
- $matches = null;
- if (preg_match('/^Configure Command =>\s*(.*?)$/m', $info, $matches)) {
- $config = $matches[1];
- }
- }
-
- $generic = true;
- list($what, $which) = $resolution;
-
- if ($what == 'flag' && strpos($config, $which) !== false) {
- $show_config = true;
- $generic = false;
- $problems[] =
- "This build of PHP was compiled with the configure flag '{$which}', ".
- "which means it does not have the function '{$fname}()'. This ".
- "function is required for arc to run. Rebuild PHP without this flag. ".
- "You may also be able to build or install the relevant extension ".
- "separately.";
- }
-
- if ($what == 'builtin-dll') {
- $generic = false;
- $problems[] =
- "Your install of PHP does not have the '{$which}' extension enabled. ".
- "Edit your php.ini file and uncomment the line which reads ".
- "'extension={$which}'.";
- }
-
- if ($what == 'text') {
- $generic = false;
- $problems[] = $which;
- }
-
- if ($generic) {
- $problems[] =
- "This build of PHP is missing the required function '{$fname}()'. ".
- "Rebuild PHP or install the extension which provides '{$fname}()'.";
- }
- }
-
- if ($problems) {
- if ($show_config) {
- $problems[] = "PHP was built with this configure command:\n\n{$config}";
- }
- die_with_bad_php(implode("\n\n", $problems));
- }
-}
-
-function die_with_bad_php($message) {
- // NOTE: We're bailing because PHP is broken. We can't call any library
- // functions because they won't be loaded yet.
-
- echo "\n";
- echo 'PHP CONFIGURATION ERRORS';
- echo "\n\n";
- echo $message;
- echo "\n\n";
- exit(1);
-}
-
-function arcanist_load_libraries(
- $load,
- $must_load,
- $lib_source,
- ArcanistWorkingCopyIdentity $working_copy) {
-
- if (!$load) {
- return;
- }
- if (!is_array($load)) {
- $error = pht(
- 'Libraries specified by %s are invalid; expected a list. '.
- 'Check your configuration.',
- $lib_source);
- $console = PhutilConsole::getConsole();
- $console->writeErr("%s: %s\n", pht('WARNING'), $error);
- return;
- }
-
- foreach ($load as $location) {
-
- // Try to resolve the library location. We look in several places, in
- // order:
- //
- // 1. Inside the working copy. This is for phutil libraries within the
- // project. For instance "library/src" will resolve to
- // "./library/src" if it exists.
- // 2. In the same directory as the working copy. This allows you to
- // check out a library alongside a working copy and reference it.
- // If we haven't resolved yet, "library/src" will try to resolve to
- // "../library/src" if it exists.
- // 3. Using normal libphutil resolution rules. Generally, this means
- // that it checks for libraries next to libphutil, then libraries
- // in the PHP include_path.
- //
- // Note that absolute paths will just resolve absolutely through rule (1).
-
- $resolved = false;
-
- // Check inside the working copy. This also checks absolute paths, since
- // they'll resolve absolute and just ignore the project root.
- $resolved_location = Filesystem::resolvePath(
- $location,
- $working_copy->getProjectRoot());
- if (Filesystem::pathExists($resolved_location)) {
- $location = $resolved_location;
- $resolved = true;
- }
- // If we didn't find anything, check alongside the working copy.
- if (!$resolved) {
- $resolved_location = Filesystem::resolvePath(
- $location,
- dirname($working_copy->getProjectRoot()));
- if (Filesystem::pathExists($resolved_location)) {
- $location = $resolved_location;
- $resolved = true;
- }
- }
- $console = PhutilConsole::getConsole();
- $console->writeLog(
- "%s\n",
- pht("Loading phutil library from '%s'...", $location));
-
- $error = null;
- try {
- phutil_load_library($location);
- } catch (PhutilBootloaderException $ex) {
- $error = pht(
- "Failed to load phutil library at location '%s'. This library ".
- "is specified by %s. Check that the setting is correct and the ".
- "library is located in the right place.",
- $location,
- $lib_source);
- if ($must_load) {
- throw new ArcanistUsageException($error);
- } else {
- fwrite(STDERR, phutil_console_wrap(
- phutil_console_format("%s: %s\n",
- pht('WARNING'),
- $error)));
- }
- } catch (PhutilLibraryConflictException $ex) {
- if ($ex->getLibrary() != 'arcanist') {
- throw $ex;
- }
-
- // NOTE: If you are running `arc` against itself, we ignore the library
- // conflict created by loading the local `arc` library (in the current
- // working directory) and continue without loading it.
-
- // This means we only execute code in the `arcanist/` directory which is
- // associated with the binary you are running, whereas we would normally
- // execute local code.
-
- // This can make `arc` development slightly confusing if your setup is
- // especially bizarre, but it allows `arc` to be used in automation
- // workflows more easily. For some context, see PHI13.
-
- $executing_directory = dirname(dirname(__FILE__));
- $working_directory = dirname($location);
-
- fwrite(
- STDERR,
- tsprintf(
- "**<bg:yellow> %s </bg>** %s\n",
- pht('VERY META'),
- pht(
- 'You are running one copy of Arcanist (at path "%s") against '.
- 'another copy of Arcanist (at path "%s"). Code in the current '.
- 'working directory will not be loaded or executed.',
- $executing_directory,
- $working_directory)));
- }
- }
-}
diff --git a/scripts/init/init-arcanist.php b/scripts/init/init-arcanist.php
new file mode 100644
--- /dev/null
+++ b/scripts/init/init-arcanist.php
@@ -0,0 +1,578 @@
+<?php
+
+final class ArcanistRuntime {
+
+ public function execute(array $argv) {
+ $err = $this->checkEnvironment();
+ if ($err) {
+ return $err;
+ }
+
+ $err = $this->includeCoreLibraries();
+ if ($err) {
+ return $err;
+ }
+
+ PhutilTranslator::getInstance()
+ ->setLocale(PhutilLocale::loadLocale('en_US'))
+ ->setTranslations(PhutilTranslation::getTranslationMapForLocale('en_US'));
+
+ ini_set('memory_limit', -1);
+
+ try {
+ return $this->executeCore($argv);
+ } catch (PhutilArgumentUsageException $ex) {
+ fwrite(
+ STDERR,
+ tsprintf(
+ "**%s:** %s\n",
+ pht('Usage Exception'),
+ $ex->getMessage()));
+ return 77;
+ }
+ }
+
+ private function executeCore(array $argv) {
+ $config_args = array(
+ array(
+ 'name' => 'load-phutil-library',
+ 'param' => 'path',
+ 'help' => pht('Load a libphutil library.'),
+ 'repeat' => true,
+ ),
+ array(
+ 'name' => 'config',
+ 'param' => 'key=value',
+ 'repeat' => true,
+ 'help' => pht('Specify a runtime configuration value.'),
+ ),
+ );
+
+ $args = id(new PhutilArgumentParser($argv))
+ ->parseStandardArguments();
+
+ $is_trace = $args->getArg('trace');
+ if ($is_trace) {
+ $this->logTrace(pht('ARGV'), csprintf('%Ls', $argv));
+ }
+
+ $args->parsePartial($config_args, true);
+
+ $config = $this->loadConfiguration($args);
+
+ $this->loadLibraries($args, $config);
+
+ $toolset = $this->newToolset($argv);
+
+ $args->parsePartial($toolset->getToolsetArguments());
+
+ $workflows = $this->newWorkflows($toolset);
+
+ $phutil_workflows = array();
+ foreach ($workflows as $key => $workflow) {
+ $phutil_workflows[$key] = $workflow->newPhutilWorkflow();
+ }
+
+ $unconsumed_argv = $args->getUnconsumedArgumentVector();
+
+ $result = $this->resolveAliases($workflows, $unconsumed_argv, $config);
+ if (is_int($result)) {
+ return $result;
+ }
+
+ $args->setUnconsumedArgumentVector($result);
+
+ return $args->parseWorkflows($phutil_workflows);
+ }
+
+
+ /**
+ * Perform some sanity checks against the possible diversity of PHP builds in
+ * the wild, like very old versions and builds that were compiled with flags
+ * that exclude core functionality.
+ */
+ private function checkEnvironment() {
+ // NOTE: We don't have phutil_is_windows() yet here.
+ $is_windows = (DIRECTORY_SEPARATOR != '/');
+
+ // We use stream_socket_pair() which is not available on Windows earlier.
+ $min_version = ($is_windows ? '5.3.0' : '5.2.3');
+ $cur_version = phpversion();
+ if (version_compare($cur_version, $min_version, '<')) {
+ $message = sprintf(
+ 'You are running a version of PHP ("%s"), which is older than the '.
+ 'minimum supported version ("%s"). Update PHP to continue.',
+ $cur_version,
+ $min_version);
+ return $this->fatalError($message);
+ }
+
+ if ($is_windows) {
+ $need_functions = array(
+ 'curl_init' => array('builtin-dll', 'php_curl.dll'),
+ );
+ } else {
+ $need_functions = array(
+ 'curl_init' => array(
+ 'text',
+ "You need to install the cURL PHP extension, maybe with ".
+ "'apt-get install php5-curl' or 'yum install php53-curl' or ".
+ "something similar.",
+ ),
+ 'json_decode' => array('flag', '--without-json'),
+ );
+ }
+
+ $problems = array();
+
+ $config = null;
+ $show_config = false;
+ foreach ($need_functions as $fname => $resolution) {
+ if (function_exists($fname)) {
+ continue;
+ }
+
+ static $info;
+ if ($info === null) {
+ ob_start();
+ phpinfo(INFO_GENERAL);
+ $info = ob_get_clean();
+ $matches = null;
+ if (preg_match('/^Configure Command =>\s*(.*?)$/m', $info, $matches)) {
+ $config = $matches[1];
+ }
+ }
+
+ list($what, $which) = $resolution;
+
+ if ($what == 'flag' && strpos($config, $which) !== false) {
+ $show_config = true;
+ $problems[] = sprintf(
+ 'The build of PHP you are running was compiled with the configure '.
+ 'flag "%s", which means it does not support the function "%s()". '.
+ 'This function is required for Arcanist to run. Install a standard '.
+ 'build of PHP or rebuild it without this flag. You may also be '.
+ 'able to build or install the relevant extension separately.',
+ $which,
+ $fname);
+ continue;
+ }
+
+ if ($what == 'builtin-dll') {
+ $problems[] = sprintf(
+ 'The build of PHP you are running does not have the "%s" extension '.
+ 'enabled. Edit your php.ini file and uncomment the line which '.
+ 'reads "extension=%s".',
+ $which,
+ $which);
+ continue;
+ }
+
+ if ($what == 'text') {
+ $problems[] = $which;
+ continue;
+ }
+
+ $problems[] = sprintf(
+ 'The build of PHP you are running is missing the required function '.
+ '"%s()". Rebuild PHP or install the extension which provides "%s()".',
+ $fname,
+ $fname);
+ }
+
+ if ($problems) {
+ if ($show_config) {
+ $problems[] = "PHP was built with this configure command:\n\n{$config}";
+ }
+ $problems = implode("\n\n", $problems);
+ return $this->fatalError($problems);
+ }
+
+ return 0;
+ }
+
+ private function fatalError($message) {
+ echo "CONFIGURATION ERROR\n\n";
+ echo $message;
+ echo "\n\n";
+ return 1;
+ }
+
+ private function includeCoreLibraries() {
+ // Adjust 'include_path' to add locations where we'll search for libphutil.
+ // We look in these places:
+ //
+ // - Next to 'arcanist/'.
+ // - Anywhere in the normal PHP 'include_path'.
+ // - Inside 'arcanist/externals/includes/'.
+ //
+ // When looking in these places, we expect to find a 'libphutil/' directory.
+
+ // The 'arcanist/' directory.
+ $arcanist_dir = dirname(dirname(dirname(__FILE__)));
+
+ // The parent directory of 'arcanist/'.
+ $parent_dir = dirname($arcanist_dir);
+
+ // The 'arcanist/externals/includes/' directory.
+ $include_dir = implode(
+ DIRECTORY_SEPARATOR,
+ array(
+ $arcanist_dir,
+ 'externals',
+ 'includes',
+ ));
+
+ $php_include_path = ini_get('include_path');
+ $php_include_path = implode(
+ PATH_SEPARATOR,
+ array(
+ $parent_dir,
+ $php_include_path,
+ $include_dir,
+ ));
+
+ ini_set('include_path', $php_include_path);
+
+ // Load libphutil.
+ @include_once 'libphutil/scripts/__init_script__.php';
+
+ if (!@constant('__LIBPHUTIL__')) {
+ return $this->fatalError(
+ 'Unable to load libphutil. Put "libphutil/" next to "arcanist/"; '.
+ 'or update your PHP "include_path" to include the parent directory '.
+ 'of "libphutil/"; or symlink "libphutil" into '.
+ '"arcanist/externals/includes/".');
+ }
+
+ // Load Arcanist.
+ phutil_load_library($arcanist_dir.'/src/');
+
+ return 0;
+ }
+
+ private function loadConfiguration(PhutilArgumentParser $args) {
+ $configuration_manager = new ArcanistConfigurationManager();
+
+ $cwd = getcwd();
+ $working_copy = ArcanistWorkingCopyIdentity::newFromPath($cwd);
+ $configuration_manager->setWorkingCopyIdentity($working_copy);
+
+ $configuration_manager->applyRuntimeArcConfig($args);
+
+ return $configuration_manager;
+ }
+
+ private function loadLibraries(
+ PhutilArgumentParser $args,
+ ArcanistConfigurationManager $config) {
+
+ $is_trace = $args->getArg('trace');
+
+ if ($is_trace) {
+ $libraries = array(
+ 'phutil',
+ 'arcanist',
+ );
+
+ foreach ($libraries as $library_name) {
+ $this->logTrace(
+ pht('LOAD'),
+ pht(
+ 'Loaded "%s" from "%s".',
+ $library_name,
+ phutil_get_library_root($library_name)));
+ }
+ }
+
+ $load = array();
+ $working_copy = $config->getWorkingCopyIdentity();
+
+ $cli_libraries = $args->getArg('load-phutil-library');
+ if ($cli_libraries) {
+ $load[] = array(
+ '--load-phutil-library',
+ $cli_libraries,
+ );
+ } else {
+ $system_config = $config->readSystemArcConfig();
+ $load[] = array(
+ $config->getSystemArcConfigLocation(),
+ idx($system_config, 'load', array()),
+ );
+
+ $global_config = $config->readUserArcConfig();
+ $load[] = array(
+ $config->getUserConfigurationFileLocation(),
+ idx($global_config, 'load', array()),
+ );
+
+ $load[] = array(
+ '.arcconfig',
+ $working_copy->getProjectConfig('load'),
+ );
+
+ $load[] = array(
+ // TODO: We could explain exactly where this is coming from more
+ // clearly.
+ './.../arc/config',
+ $working_copy->getLocalConfig('load'),
+ );
+
+ $load[] = array(
+ '--config load=...',
+ $config->getRuntimeConfig('load', array()),
+ );
+ }
+
+ foreach ($load as $spec) {
+ list($source, $libraries) = $spec;
+ if ($is_trace) {
+ $this->logTrace(
+ pht('LOAD'),
+ pht(
+ 'Loading libraries from "%s"...',
+ $source));
+ }
+
+ if (!$libraries) {
+ if ($is_trace) {
+ $this->logTrace(pht('NONE'), pht('Nothing to load.'));
+ }
+ continue;
+ }
+
+ if (!is_array($libraries)) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Libraries specified by "%s" are not formatted correctly. '.
+ 'Expected a list of paths. Check your configuration.',
+ $source));
+ }
+
+ foreach ($libraries as $library) {
+ $this->loadLibrary($source, $library, $working_copy, $is_trace);
+ }
+ }
+ }
+
+ private function loadLibrary(
+ $source,
+ $location,
+ ArcanistWorkingCopyIdentity $working_copy,
+ $is_trace) {
+
+ // Try to resolve the library location. We look in several places, in
+ // order:
+ //
+ // 1. Inside the working copy. This is for phutil libraries within the
+ // project. For instance "library/src" will resolve to
+ // "./library/src" if it exists.
+ // 2. In the same directory as the working copy. This allows you to
+ // check out a library alongside a working copy and reference it.
+ // If we haven't resolved yet, "library/src" will try to resolve to
+ // "../library/src" if it exists.
+ // 3. Using normal libphutil resolution rules. Generally, this means
+ // that it checks for libraries next to libphutil, then libraries
+ // in the PHP include_path.
+ //
+ // Note that absolute paths will just resolve absolutely through rule (1).
+
+ $resolved = false;
+
+ // Check inside the working copy. This also checks absolute paths, since
+ // they'll resolve absolute and just ignore the project root.
+ $resolved_location = Filesystem::resolvePath(
+ $location,
+ $working_copy->getProjectRoot());
+ if (Filesystem::pathExists($resolved_location)) {
+ $location = $resolved_location;
+ $resolved = true;
+ }
+
+ // If we didn't find anything, check alongside the working copy.
+ if (!$resolved) {
+ $resolved_location = Filesystem::resolvePath(
+ $location,
+ dirname($working_copy->getProjectRoot()));
+ if (Filesystem::pathExists($resolved_location)) {
+ $location = $resolved_location;
+ $resolved = true;
+ }
+ }
+
+ if ($is_trace) {
+ $this->logTrace(
+ pht('LOAD'),
+ pht('Loading phutil library from "%s"...', $location));
+ }
+
+ $error = null;
+ try {
+ phutil_load_library($location);
+ } catch (PhutilBootloaderException $ex) {
+ fwrite(
+ STDERR,
+ "%s",
+ tsprintf(
+ "**<bg:red> %s </bg>** %s\n",
+ pht(
+ 'Failed to load phutil library at location "%s". This library '.
+ 'is specified by "%s". Check that the setting is correct and '.
+ 'the library is located in the right place.',
+ $location,
+ $source)));
+
+ $prompt = pht('Continue without loading library?');
+ if (!phutil_console_confirm($prompt)) {
+ throw $ex;
+ }
+ } catch (PhutilLibraryConflictException $ex) {
+ if ($ex->getLibrary() != 'arcanist') {
+ throw $ex;
+ }
+
+ // NOTE: If you are running `arc` against itself, we ignore the library
+ // conflict created by loading the local `arc` library (in the current
+ // working directory) and continue without loading it.
+
+ // This means we only execute code in the `arcanist/` directory which is
+ // associated with the binary you are running, whereas we would normally
+ // execute local code.
+
+ // This can make `arc` development slightly confusing if your setup is
+ // especially bizarre, but it allows `arc` to be used in automation
+ // workflows more easily. For some context, see PHI13.
+
+ $executing_directory = dirname(dirname(__FILE__));
+ $working_directory = dirname($location);
+
+ fwrite(
+ STDERR,
+ tsprintf(
+ "**<bg:yellow> %s </bg>** %s\n",
+ pht('VERY META'),
+ pht(
+ 'You are running one copy of Arcanist (at path "%s") against '.
+ 'another copy of Arcanist (at path "%s"). Code in the current '.
+ 'working directory will not be loaded or executed.',
+ $executing_directory,
+ $working_directory)));
+ }
+ }
+
+ private function newToolset(array $argv) {
+ $binary = basename($argv[0]);
+
+ $toolsets = ArcanistToolset::newToolsetMap();
+ if (!isset($toolsets[$binary])) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Arcanist toolset "%s" is unknown. The Arcanist binary should '.
+ 'be executed so that "argv[0]" identifies a supported toolset. '.
+ 'Rename the binary or install the library that provides the '.
+ 'desired toolset. Current available toolsets: %s.',
+ $binary,
+ implode(', ', array_keys($toolsets))));
+ }
+
+ return $toolsets[$binary];
+ }
+
+ private function newWorkflows(ArcanistToolset $toolset) {
+ $workflows = id(new PhutilClassMapQuery())
+ ->setAncestorClass('ArcanistWorkflow')
+ ->execute();
+
+ foreach ($workflows as $key => $workflow) {
+ if (!$workflow->supportsToolset($toolset)) {
+ unset($workflows[$key]);
+ }
+ }
+
+ $map = array();
+ foreach ($workflows as $workflow) {
+ $key = $workflow->getWorkflowName();
+ if (isset($map[$key])) {
+ throw new Exception(
+ pht(
+ 'Two workflows ("%s" and "%s") both have the same name ("%s") '.
+ 'and both support the current toolset ("%s", "%s"). Each '.
+ 'workflow in a given toolset must have a unique name.',
+ get_class($workflow),
+ get_class($map[$key]),
+ get_class($toolset),
+ $toolset->getToolsetKey()));
+ }
+ $map[$key] = id(clone $workflow)
+ ->setToolset($toolset);
+ }
+
+ return $map;
+ }
+
+ private function resolveAliases(
+ array $workflows,
+ array $argv,
+ ArcanistConfigurationManager $config) {
+
+ $command = head($argv);
+
+ // If this is a match for a recognized workflow, just return the arguments
+ // unmodified. You aren't allowed to alias over real workflows.
+ if (isset($workflows[$command])) {
+ return $argv;
+ }
+
+ $aliases = ArcanistAliasWorkflow::getAliases($config);
+ list($new_command, $new_args) = ArcanistAliasWorkflow::resolveAliases(
+ $command,
+ $this,
+ array_slice($argv, 1),
+ $config);
+
+ // You can't alias something to itself, so if the new command isn't new,
+ // we're all done resolving aliases.
+ if ($new_command === $command) {
+ return $argv;
+ }
+
+ $full_alias = idx($aliases, $command, array());
+ $full_alias = implode(' ', $full_alias);
+
+ // Run shell command aliases.
+ if (ArcanistAliasWorkflow::isShellCommandAlias($new_command)) {
+ fwrite(
+ STDERR,
+ tsprintf(
+ '**<bg:green> %s </bg>** arc %s -> $ %s',
+ pht('ALIAS'),
+ $command,
+ $shell_cmd));
+
+ $shell_cmd = substr($full_alias, 1);
+
+ return phutil_passthru('%C %Ls', $shell_cmd, $args);
+ }
+
+ fwrite(
+ STDERR,
+ tsprintf(
+ '**<bg:green> %s </bg>** arc %s -> arc %s',
+ pht('ALIAS'),
+ $command,
+ $new_command));
+
+ $new_argv = array_merge(array($new_command), $new_args);
+
+ return $this->resolveAliases($workflows, $new_argv, $config);
+ }
+
+ private function logTrace($label, $message) {
+ echo tsprintf(
+ "**<bg:magenta> %s </bg>** %s\n",
+ $label,
+ $message);
+ }
+
+}
diff --git a/scripts/phage.php b/scripts/phage.php
deleted file mode 100755
--- a/scripts/phage.php
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env php
-<?php
-
-require_once dirname(__FILE__).'/__init_script__.php';
-ini_set('memory_limit', -1);
-
-$args = new PhutilArgumentParser($argv);
-$args->parseStandardArguments();
-
-$args->parsePartial(array());
-
-
-// TODO: This is pretty minimal and should be shared with "arc".
-$working_directory = getcwd();
-$working_copy = ArcanistWorkingCopyIdentity::newFromPath($working_directory);
-$config = id(new ArcanistConfigurationManager())
- ->setWorkingCopyIdentity($working_copy);
-
-foreach ($config->getProjectConfig('load') as $load) {
- $load = Filesystem::resolvePath($working_copy->getProjectRoot().'/'.$load);
- phutil_load_library($load);
-}
-
-
-$workflows = id(new PhutilClassMapQuery())
- ->setAncestorClass('PhageWorkflow')
- ->execute();
-$workflows[] = new PhutilHelpArgumentWorkflow();
-$args->parseWorkflows($workflows);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Oct 2, 1:13 AM (22 h, 8 m)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/vv/s3/6s4ytxmkloi2p4yr
Default Alt Text
D19677.diff (34 KB)
Attached To
Mode
D19677: [Wilds] Rewrite "arc" entrypoints for toolsets
Attached
Detach File
Event Timeline
Log In to Comment