X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs-website;a=blobdiff_plain;f=vendor%2Fdrush%2Fdrush%2Fcommands%2Fmake%2Fmake.project.inc;fp=vendor%2Fdrush%2Fdrush%2Fcommands%2Fmake%2Fmake.project.inc;h=60e73257c73aa32394fcee1c117bdceb0abbddc1;hp=0000000000000000000000000000000000000000;hb=a2bd1bf0c2c1f1a17d188f4dc0726a45494cefae;hpb=57c063afa3f66b07c4bbddc2d6129a96d90f0aad diff --git a/vendor/drush/drush/commands/make/make.project.inc b/vendor/drush/drush/commands/make/make.project.inc new file mode 100644 index 000000000..60e73257c --- /dev/null +++ b/vendor/drush/drush/commands/make/make.project.inc @@ -0,0 +1,740 @@ + $value) { + $this->{$key} = $value; + } + if (!empty($this->options['working-copy'])) { + $this->download['working-copy'] = TRUE; + } + // Don't recurse when we're using a pre-built profile tarball. + if ($this->variant == 'projects') { + $this->do_recursion = FALSE; + } + } + + /** + * Get an instance for the type and project. + * + * @param string $type + * Type of project: core, library, module, profile, or translation. + * @param array $project + * Project information. + * + * @return mixed + * An instance for the project or FALSE if invalid type. + */ + public static function getInstance($type, $project) { + if (!isset(self::$self[$type][$project['name']])) { + $class = 'DrushMakeProject_' . $type; + self::$self[$type][$project['name']] = class_exists($class) ? new $class($project) : FALSE; + } + return self::$self[$type][$project['name']]; + } + + /** + * Set the manifest array. + * + * @param array $manifest + * An array of projects as generated by `make_projects`. + */ + public function setManifest($manifest) { + $this->manifest = $manifest; + } + + /** + * Download a project. + */ + function download() { + $this->downloaded = TRUE; + + // In some cases, make_download_factory() is going to need to know the + // full version string of the project we're trying to download. However, + // the version is a project-level attribute, not a download-level + // attribute. So, if we don't already have a full version string in the + // download array (e.g. if it was initialized via the release history XML + // for the PM case), we take the version info from the project-level + // attribute, convert it into a full version string, and stuff it into + // $this->download so that the download backend has access to it, too. + if (!empty($this->version) && empty($this->download['full_version'])) { + $full_version = ''; + $matches = array(); + // Core needs different conversion rules than contrib. + if (!empty($this->type) && $this->type == 'core') { + // Generally, the version for core is already set properly. + $full_version = $this->version; + // However, it might just be something like '7' or '7.x', in which + // case we need to turn that into '7.x-dev'; + if (preg_match('/^\d+(\.x)?$/', $this->version, $matches)) { + // If there's no '.x' already, append it. + if (empty($matches[1])) { + $full_version .= '.x'; + } + $full_version .= '-dev'; + } + } + // Contrib. + else { + // If the version doesn't already define a core version, prepend it. + if (!preg_match('/^\d+\.x-\d+.*$/', $this->version)) { + // Just find the major version from $this->core so we don't end up + // with version strings like '7.12-2.0'. + $core_parts = explode('.', $this->core); + $full_version = $core_parts[0] . '.x-'; + } + $full_version .= $this->version; + // If the project-level version attribute is just a number it's a major + // version. + if (preg_match('/^\d+(\.x)?$/', $this->version, $matches)) { + // If there's no '.x' already, append it. + if (empty($matches[1])) { + $full_version .= '.x'; + } + $full_version .= '-dev'; + } + } + $this->download['full_version'] = $full_version; + } + + $this->download['variant'] = $this->variant; + + if (make_download_factory($this->name, $this->type, $this->download, $this->download_location) === FALSE) { + $this->downloaded = FALSE; + } + return $this->downloaded; + } + + /** + * Build a project. + */ + function make() { + if ($this->made) { + drush_log(dt('Attempt to build project @project more then once prevented.', array('@project' => $this->name))); + return TRUE; + } + $this->made = TRUE; + + if (!isset($this->download_location)) { + $this->download_location = $this->findDownloadLocation(); + } + if ($this->download() === FALSE) { + return FALSE; + } + if (!$this->addLockfile($this->download_location)) { + return FALSE; + } + if (!$this->applyPatches($this->download_location)) { + return FALSE; + } + if (!$this->getTranslations($this->download_location)) { + return FALSE; + } + // Handle .info file re-writing (if so desired). + if (!drush_get_option('no-gitinfofile', FALSE) && isset($this->download['type']) && $this->download['type'] == 'git') { + $this->processGitInfoFiles(); + } + // Clean-up .git directories. + if (!_get_working_copy_option($this->download)) { + $this->removeGitDirectory(); + } + if (!$this->recurse($this->download_location)) { + return FALSE; + } + return TRUE; + } + + /** + * Determine the location to download project to. + */ + function findDownloadLocation() { + $this->path = $this->generatePath(); + $this->project_directory = !empty($this->directory_name) ? $this->directory_name : $this->name; + $this->download_location = $this->path . '/' . $this->project_directory; + // This directory shouldn't exist yet -- if it does, stop, + // unless overwrite has been set to TRUE. + if (is_dir($this->download_location) && !$this->overwrite) { + return drush_set_error('MAKE_DIRECTORY_EXISTS', dt('Directory not empty: !directory', array('!directory' => $this->download_location))); + } + elseif ($this->download['type'] === 'pm') { + // pm-download will create the final contrib directory. + drush_mkdir(dirname($this->download_location)); + } + else { + drush_mkdir($this->download_location); + } + return $this->download_location; + } + + /** + * Rewrite relative URLs and file:/// URLs + * + * relative path -> absolute path using the make_directory + * local file:/// urls -> local paths + * + * @param mixed &$info + * Either an array or a simple url string. The `$info` variable will be + * transformed into an array. + */ + protected function preprocessLocalFileUrl(&$info) { + if (is_string($info)) { + $info = array('url' => $info, 'local' => FALSE); + } + + if (!_drush_is_url($info['url']) && !drush_is_absolute_path($info['url'])) { + $info['url'] = $this->make_directory . '/' . $info['url']; + $info['local'] = TRUE; + } elseif (substr($info['url'], 0, 8) == 'file:///') { + $info['url'] = substr($info['url'], 7); + $info['local'] = TRUE; + } + } + + /** + * Retrieve and apply any patches specified by the makefile to this project. + */ + function applyPatches($project_directory) { + if (empty($this->patch)) { + return TRUE; + } + + $patches_txt = ''; + $local_patches = array(); + $ignore_checksums = drush_get_option('ignore-checksums'); + foreach ($this->patch as $info) { + $this->preprocessLocalFileUrl($info); + + // Download the patch. + if ($filename = _make_download_file($info['url'])) { + $patched = FALSE; + $output = ''; + // Test each patch style; -p1 is the default with git. See + // http://drupal.org/node/1054616 + $patch_levels = array('-p1', '-p0'); + foreach ($patch_levels as $patch_level) { + $checked = drush_shell_cd_and_exec($project_directory, 'git --git-dir=. apply --check %s %s --verbose', $patch_level, $filename); + if ($checked) { + // Apply the first successful style. + $patched = drush_shell_cd_and_exec($project_directory, 'git --git-dir=. apply %s %s --verbose', $patch_level, $filename); + break; + } + } + + // In some rare cases, git will fail to apply a patch, fallback to using + // the 'patch' command. + if (!$patched) { + foreach ($patch_levels as $patch_level) { + // --no-backup-if-mismatch here is a hack that fixes some + // differences between how patch works on windows and unix. + if ($patched = drush_shell_exec("patch %s --no-backup-if-mismatch -d %s < %s", $patch_level, $project_directory, $filename)) { + break; + } + } + } + + if ($output = drush_shell_exec_output()) { + // Log any command output, visible only in --verbose or --debug mode. + drush_log(implode("\n", $output)); + } + + // Set up string placeholders to pass to dt(). + $dt_args = array( + '@name' => $this->name, + '@filename' => basename($filename), + ); + + if ($patched) { + if (!$ignore_checksums && !_make_verify_checksums($info, $filename)) { + return FALSE; + } + $patch_url = $info['url']; + + // If this is a local patch, copy that into place as well. + if ($info['local']) { + $local_patches[] = $info['url']; + // Use a local path for the PATCHES.txt file. + $pathinfo = pathinfo($patch_url); + $patch_url = $pathinfo['basename']; + } + $patches_txt .= '- ' . $patch_url . "\n"; + + drush_log(dt('@name patched with @filename.', $dt_args), LogLevel::OK); + } + else { + make_error('PATCH_ERROR', dt("Unable to patch @name with @filename.", $dt_args)); + } + drush_op('unlink', $filename); + } + else { + make_error('DOWNLOAD_ERROR', 'Unable to download ' . $info['url'] . '.'); + return FALSE; + } + } + if (!empty($patches_txt) && !drush_get_option('no-patch-txt') && !file_exists($project_directory . '/PATCHES.txt')) { + $patches_txt = "The following patches have been applied to this project:\n" . + $patches_txt . + "\nThis file was automatically generated by Drush Make (http://drupal.org/project/drush).\n"; + file_put_contents($project_directory . '/PATCHES.txt', $patches_txt); + drush_log('Generated PATCHES.txt file for ' . $this->name, LogLevel::OK); + + // Copy local patches into place. + foreach ($local_patches as $url) { + $pathinfo = pathinfo($url); + drush_copy_dir($url, $project_directory . '/' . $pathinfo['basename']); + } + } + return TRUE; + } + + /** + * Process info files when downloading things from git. + */ + function processGitInfoFiles() { + // Bail out if this isn't hosted on Drupal.org (unless --force-gitinfofile option was specified). + if (!drush_get_option('force-gitinfofile', FALSE) && isset($this->download['url']) && strpos($this->download['url'], 'drupal.org') === FALSE) { + return; + } + + // Figure out the proper version string to use based on the .make file. + // Best case is the .make file author told us directly. + if (!empty($this->download['full_version'])) { + $full_version = $this->download['full_version']; + } + // Next best is if we have a tag, since those are identical to versions. + elseif (!empty($this->download['tag'])) { + $full_version = $this->download['tag']; + } + // If we have a branch, append '-dev'. + elseif (!empty($this->download['branch'])) { + $full_version = $this->download['branch'] . '-dev'; + } + // Ugh. Not sure what else we can do in this case. + elseif (!empty($this->download['revision'])) { + $full_version = $this->download['revision']; + } + // Probably can never reach this case. + else { + $full_version = 'unknown'; + } + + // If the version string ends in '.x-dev' do the Git magic to figure out + // the appropriate 'rebuild version' string, e.g. '7.x-1.2+7-dev'. + $matches = array(); + if (preg_match('/^(.+).x-dev$/', $full_version, $matches)) { + require_once dirname(__FILE__) . '/../pm/package_handler/git_drupalorg.inc'; + $rebuild_version = drush_pm_git_drupalorg_compute_rebuild_version($this->download_location, $matches[1]); + if ($rebuild_version) { + $full_version = $rebuild_version; + } + } + require_once dirname(__FILE__) . '/../pm/pm.drush.inc'; + if (drush_shell_cd_and_exec($this->download_location, 'git log -1 --pretty=format:%ct')) { + $output = drush_shell_exec_output(); + $datestamp = $output[0]; + } + else { + $datestamp = time(); + } + drush_pm_inject_info_file_metadata($this->download_location, $this->name, $full_version, $datestamp); + } + + /** + * Remove the .git directory from a project. + */ + function removeGitDirectory() { + if (isset($this->download['type']) && $this->download['type'] == 'git' && file_exists($this->download_location . '/.git')) { + drush_delete_dir($this->download_location . '/.git', TRUE); + } + } + + /** + * Add a lock file. + */ + function addLockfile($project_directory) { + if (!empty($this->lock)) { + file_put_contents($project_directory . '/.drush-lock-update', $this->lock); + } + return TRUE; + } + + /** + * Retrieve translations for this project. + */ + function getTranslations($project_directory) { + static $cache = array(); + $langcodes = $this->translations; + if ($langcodes && in_array($this->type, array('core', 'module', 'profile', 'theme'), TRUE)) { + // Support the l10n_path, l10n_url keys from l10n_update. Note that the + // l10n_server key is not supported. + if (isset($this->l10n_path)) { + $update_url = $this->l10n_path; + } + else { + if (isset($this->l10n_url)) { + $l10n_server = $this->l10n_url; + } + else { + $l10n_server = FALSE; + } + if ($l10n_server) { + if (!isset($cache[$l10n_server])) { + $this->preprocessLocalFileUrl($l10n_server); + $l10n_server = $l10n_server['url']; + if ($filename = _make_download_file($l10n_server)) { + $server_info = simplexml_load_string(file_get_contents($filename)); + $cache[$l10n_server] = !empty($server_info->update_url) ? $server_info->update_url : FALSE; + } + } + if ($cache[$l10n_server]) { + $update_url = $cache[$l10n_server]; + } + else { + make_error('XML_ERROR', dt("Could not retrieve l10n update url for !project.", array('!project' => $this->name))); + return FALSE; + } + } + } + if ($update_url) { + $failed = array(); + foreach ($langcodes as $langcode) { + $variables = array( + '%project' => $this->name, + '%release' => $this->download['full_version'], + '%core' => $this->core, + '%language' => $langcode, + '%filename' => '%filename', + ); + $url = strtr($update_url, $variables); + + // Download the translation file. Since its contents are volatile, + // cache for only 4 hours. + if ($filename = _make_download_file($url, 3600 * 4)) { + // If this is the core project type, download the translation file + // and place it in every profile and an additional copy in + // modules/system/translations where it can be detected for import + // by other non-default install profiles. + if ($this->type === 'core') { + $profiles = drush_scan_directory($project_directory . '/profiles', '/.*/', array(), 0, FALSE, 'filename', 0, TRUE); + foreach ($profiles as $profile) { + if (is_dir($project_directory . '/profiles/' . $profile->basename)) { + drush_mkdir($project_directory . '/profiles/' . $profile->basename . '/translations'); + drush_copy_dir($filename, $project_directory . '/profiles/' . $profile->basename . '/translations/' . $langcode . '.po'); + } + } + drush_mkdir($project_directory . '/modules/system/translations'); + drush_copy_dir($filename, $project_directory . '/modules/system/translations/' . $langcode . '.po'); + } + else { + drush_mkdir($project_directory . '/translations'); + drush_copy_dir($filename, $project_directory . '/translations/' . $langcode . '.po', FILE_EXISTS_OVERWRITE); + } + } + else { + $failed[] = $langcode; + } + } + if (empty($failed)) { + drush_log('All translations downloaded for ' . $this->name, LogLevel::OK); + } + else { + drush_log('Unable to download translations for ' . $this->name . ': ' . implode(', ', $failed), LogLevel::WARNING); + } + } + } + return TRUE; + } + + /** + * Generate the proper path for this project type. + * + * @param boolean $base + * Whether include the base part (tmp dir). Defaults to TRUE. + */ + protected function generatePath($base = TRUE) { + $path = array(); + if ($base) { + $path[] = make_tmp(); + $path[] = '__build__'; + } + if (!empty($this->contrib_destination)) { + $path[] = $this->contrib_destination; + } + if (!empty($this->subdir)) { + $path[] = $this->subdir; + } + return implode('/', $path); + } + + /** + * Return the proper path for dependencies to be placed in. + * + * @return string + * The path that dependencies will be placed in. + */ + protected function buildPath($directory) { + return $this->base_contrib_destination; + } + + /** + * Recurse to process additional makefiles that may be found during + * processing. + */ + function recurse($path) { + if (!$this->do_recursion || drush_get_option('no-recursion')) { + drush_log(dt("Preventing recursive makefile parsing for !project", + array("!project" => $this->name)), LogLevel::NOTICE); + return TRUE; + } + $candidates = array( + $this->name . '.make.yml', + $this->name . '.make', + 'drupal-org.make.yml', + 'drupal-org.make', + ); + $makefile = FALSE; + foreach ($candidates as $filename) { + if (file_exists($this->download_location . '/' . $filename)) { + $makefile = $this->download_location . '/' . $filename; + break; + } + } + + if (!$makefile) { + return TRUE; + } + + drush_log(dt("Found makefile: !makefile", array("!makefile" => basename($makefile))), LogLevel::OK); + + // Save the original state of the 'custom' context. + $custom_context = &drush_get_context('custom'); + $original_custom_context_values = $custom_context; + + $info = make_parse_info_file($makefile, TRUE, $this->options); + if (!($info = make_validate_info_file($info))) { + $result = FALSE; + } + else { + // Inherit the translations specified in the extender makefile. + if (!empty($this->translations)) { + $info['translations'] = $this->translations; + } + // Strip out any modules that have already been processed before this. + foreach ($this->manifest as $name) { + unset($info['projects'][$name]); + } + $build_path = $this->buildPath($this->name); + make_projects(TRUE, trim($build_path, '/'), $info, $this->build_path, $this->download_location); + make_libraries(TRUE, trim($build_path, '/'), $info, $this->build_path, $this->download_location); + $result = TRUE; + } + // Restore original 'custom' context so that any + // settings changes made are used. + $custom_context = $original_custom_context_values; + + return $result; + } +} + +/** + * For processing Drupal core projects. + */ +class DrushMakeProject_Core extends DrushMakeProject { + /** + * Override constructor for core to adjust project info. + */ + protected function __construct(&$project) { + parent::__construct($project); + // subdir and contrib_destination are not allowed for core. + $this->subdir = ''; + $this->contrib_destination = ''; + } + + /** + * Determine the location to download project to. + */ + function findDownloadLocation() { + $this->path = $this->download_location = $this->generatePath(); + $this->project_directory = ''; + if (is_dir($this->download_location)) { + return drush_set_error('MAKE_DIRECTORY_EXISTS', dt('Directory not empty: !directory', array('!directory' => $this->download_location))); + } + elseif ($this->download['type'] === 'pm') { + // pm-download will create the final __build__ directory, so nothing to do + // here. + } + else { + drush_mkdir($this->download_location); + } + return $this->download_location; + } +} + +/** + * For processing libraries. + */ +class DrushMakeProject_Library extends DrushMakeProject { + /** + * Override constructor for libraries to properly set contrib destination. + */ + protected function __construct(&$project) { + parent::__construct($project); + // Allow libraries to specify where they should live in the build path. + if (isset($project['destination'])) { + $project_path = $project['destination']; + } + else { + $project_path = 'libraries'; + } + + $this->contrib_destination = ($this->base_contrib_destination != '.' ? $this->base_contrib_destination . '/' : '') . $project_path; + } + + /** + * No recursion for libraries, sorry :-( + */ + function recurse($path) { + // Return TRUE so that processing continues in the make() method. + return TRUE; + } + + /** + * No translations for libraries. + */ + function getTranslations($download_location) { + // Return TRUE so that processing continues in the make() method. + return TRUE; + } +} + +/** + * For processing modules. + */ +class DrushMakeProject_Module extends DrushMakeProject { + /** + * Override constructor for modules to properly set contrib destination. + */ + protected function __construct(&$project) { + parent::__construct($project); + $this->contrib_destination = ($this->base_contrib_destination != '.' ? $this->base_contrib_destination . '/' : '') . 'modules'; + } +} + +/** + * For processing installation profiles. + */ +class DrushMakeProject_Profile extends DrushMakeProject { + /** + * Override contructor for installation profiles to properly set contrib + * destination. + */ + protected function __construct(&$project) { + parent::__construct($project); + $this->contrib_destination = (!empty($this->destination) ? $this->destination : 'profiles'); + } + + /** + * Find the build path. + */ + protected function buildPath($directory) { + return $this->generatePath(FALSE) . '/' . $directory; + } +} + +/** + * For processing themes. + */ +class DrushMakeProject_Theme extends DrushMakeProject { + /** + * Override contructor for themes to properly set contrib destination. + */ + protected function __construct(&$project) { + parent::__construct($project); + $this->contrib_destination = ($this->base_contrib_destination != '.' ? $this->base_contrib_destination . '/' : '') . 'themes'; + } +} + +/** + * For processing translations. + */ +class DrushMakeProject_Translation extends DrushMakeProject { + /** + * Override constructor for translations to properly set contrib destination. + */ + protected function __construct(&$project) { + parent::__construct($project); + switch ($project['core']) { + case '5.x': + // Don't think there's an automatic place we can put 5.x translations, + // so we'll toss them in a translations directory in the Drupal root. + $this->contrib_destination = ($this->base_contrib_destination != '.' ? $this->base_contrib_destination . '/' : '') . 'translations'; + break; + + default: + $this->contrib_destination = ''; + break; + } + } +}