Path: blob/master/scripts/symbols/import_repository_symbols.php
12241 views
#!/usr/bin/env php1<?php23$root = dirname(dirname(dirname(__FILE__)));4require_once $root.'/scripts/__init_script__.php';56$args = new PhutilArgumentParser($argv);7$args->setSynopsis(<<<EOSYNOPSIS8**import_repository_symbols.php** [__options__] __repository__ < symbols910Import repository symbols (symbols are read from stdin).11EOSYNOPSIS12);13$args->parseStandardArguments();14$args->parse(15array(16array(17'name' => 'no-purge',18'help' => pht(19'Do not clear all symbols for this repository before '.20'uploading new symbols. Useful for incremental updating.'),21),22array(23'name' => 'ignore-errors',24'help' => pht(25"If a line can't be parsed, ignore that line and ".26"continue instead of exiting."),27),28array(29'name' => 'max-transaction',30'param' => 'num-syms',31'default' => '100000',32'help' => pht(33'Maximum number of symbols that should '.34'be part of a single transaction.'),35),36array(37'name' => 'repository',38'wildcard' => true,39),40));4142$identifiers = $args->getArg('repository');43if (count($identifiers) !== 1) {44$args->printHelpAndExit();45}4647$identifier = head($identifiers);48$repository = id(new PhabricatorRepositoryQuery())49->setViewer(PhabricatorUser::getOmnipotentUser())50->withIdentifiers($identifiers)51->executeOne();5253if (!$repository) {54echo tsprintf(55"%s\n",56pht('Repository "%s" does not exist.', $identifier));57exit(1);58}5960if (!function_exists('posix_isatty') || posix_isatty(STDIN)) {61echo pht('Parsing input from stdin...'), "\n";62}6364$input = file_get_contents('php://stdin');65$input = trim($input);66$input = explode("\n", $input);676869function commit_symbols(70array $symbols,71PhabricatorRepository $repository,72$no_purge) {7374echo pht('Looking up path IDs...'), "\n";75$path_map =76PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths(77ipull($symbols, 'path'));7879$symbol = new PhabricatorRepositorySymbol();80$conn_w = $symbol->establishConnection('w');8182echo pht('Preparing queries...'), "\n";83$sql = array();84foreach ($symbols as $dict) {85$sql[] = qsprintf(86$conn_w,87'(%s, %s, %s, %s, %s, %d, %d)',88$repository->getPHID(),89$dict['ctxt'],90$dict['name'],91$dict['type'],92$dict['lang'],93$dict['line'],94$path_map[$dict['path']]);95}9697if (!$no_purge) {98echo pht('Purging old symbols...'), "\n";99queryfx(100$conn_w,101'DELETE FROM %T WHERE repositoryPHID = %s',102$symbol->getTableName(),103$repository->getPHID());104}105106echo pht('Loading %s symbols...', phutil_count($sql)), "\n";107foreach (array_chunk($sql, 128) as $chunk) {108queryfx(109$conn_w,110'INSERT INTO %T111(repositoryPHID, symbolContext, symbolName, symbolType,112symbolLanguage, lineNumber, pathID) VALUES %LQ',113$symbol->getTableName(),114$chunk);115}116}117118function check_string_value($value, $field_name, $line_no, $max_length) {119if (strlen($value) > $max_length) {120throw new Exception(121pht(122"%s '%s' defined on line #%d is too long, ".123"maximum %s length is %d characters.",124$field_name,125$value,126$line_no,127$field_name,128$max_length));129}130131if (!phutil_is_utf8_with_only_bmp_characters($value)) {132throw new Exception(133pht(134"%s '%s' defined on line #%d is not a valid ".135"UTF-8 string, it should contain only UTF-8 characters.",136$field_name,137$value,138$line_no));139}140}141142$no_purge = $args->getArg('no-purge');143$symbols = array();144foreach ($input as $key => $line) {145try {146$line_no = $key + 1;147$matches = null;148$ok = preg_match(149'/^((?P<context>[^ ]+)? )?(?P<name>[^ ]+) (?P<type>[^ ]+) '.150'(?P<lang>[^ ]+) (?P<line>\d+) (?P<path>.*)$/',151$line,152$matches);153if (!$ok) {154throw new Exception(155pht(156"Line #%d of input is invalid. Expected five or six space-delimited ".157"fields: maybe symbol context, symbol name, symbol type, symbol ".158"language, line number, path. For example:\n\n%s\n\n".159"Actual line was:\n\n%s",160$line_no,161'idx function php 13 /path/to/some/file.php',162$line));163}164if (empty($matches['context'])) {165$matches['context'] = '';166}167$context = $matches['context'];168$name = $matches['name'];169$type = $matches['type'];170$lang = $matches['lang'];171$line_number = $matches['line'];172$path = $matches['path'];173174check_string_value($context, pht('Symbol context'), $line_no, 128);175check_string_value($name, pht('Symbol name'), $line_no, 128);176check_string_value($type, pht('Symbol type'), $line_no, 12);177check_string_value($lang, pht('Symbol language'), $line_no, 32);178check_string_value($path, pht('Path'), $line_no, 512);179180if (!strlen($path) || $path[0] != '/') {181throw new Exception(182pht(183"Path '%s' defined on line #%d is invalid. Paths should begin with ".184"'%s' and specify a path from the root of the project, like '%s'.",185$path,186$line_no,187'/',188'/src/utils/utils.php'));189}190191$symbols[] = array(192'ctxt' => $context,193'name' => $name,194'type' => $type,195'lang' => $lang,196'line' => $line_number,197'path' => $path,198);199} catch (Exception $e) {200if ($args->getArg('ignore-errors')) {201continue;202} else {203throw $e;204}205}206207if (count($symbols) >= $args->getArg('max-transaction')) {208try {209echo pht(210"Committing %s symbols...\n",211new PhutilNumber($args->getArg('max-transaction')));212commit_symbols($symbols, $repository, $no_purge);213$no_purge = true;214unset($symbols);215$symbols = array();216} catch (Exception $e) {217if ($args->getArg('ignore-errors')) {218continue;219} else {220throw $e;221}222}223}224}225226if (count($symbols)) {227commit_symbols($symbols, $repository, $no_purge);228}229230echo pht('Done.')."\n";231232233