make.drush.inc

  1. 8.0.x commands/make/make.drush.inc
  2. 6.x commands/make/make.drush.inc
  3. 7.x commands/make/make.drush.inc
  4. 5.x commands/make/make.drush.inc
  5. master commands/make/make.drush.inc

Drush Make commands.

Functions

Namesort descending Description
drush_make Drush callback; make based on the makefile.
drush_make_post_make Implements drush_hook_post_COMMAND() for the make command.
drush_make_process Drush callback: hidden file to process an individual project.
make_build_path The path where the final build will be placed.
make_drush_command Implements hook_drush_command().
make_drush_help Implements hook_drush_help().
make_libraries Process all libraries specified in the make file.
make_make_complete Command argument complete callback.
make_move_build Move the completed build into place.
make_prepare_request Create a request array for use with release_info_fetch().
make_projects Process all projects specified in the make file.
make_project_needs_release_info Determine if the release information is required for this project. When it is determined that it is, this potentially results in the use of pm-download to process the project.
_make_enable_cache Enables caching if not explicitly disabled.

Constants

Namesort descending Description
MAKE_API Make refuses to build makefiles whose api version is mismatched with make command.
MAKE_DEFAULT_L10N_SERVER Default localization server for downloading translations.

File

commands/make/make.drush.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Drush Make commands.
  5. */
  6. /**
  7. * Default localization server for downloading translations.
  8. */
  9. define('MAKE_DEFAULT_L10N_SERVER', 'http://ftp.drupal.org/files/translations/l10n_server.xml');
  10. /**
  11. * Make refuses to build makefiles whose api version is mismatched
  12. * with make command.
  13. */
  14. define('MAKE_API', 2);
  15. include_once 'make.utilities.inc';
  16. include_once 'make.download.inc';
  17. include_once 'make.project.inc';
  18. /**
  19. * Implements hook_drush_command().
  20. */
  21. function make_drush_command() {
  22. $items['make'] = array(
  23. 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
  24. 'description' => 'Turns a makefile into a working Drupal codebase.',
  25. 'arguments' => array(
  26. 'makefile' => 'Filename of the makefile to use for this build.',
  27. 'build path' => 'The path at which to build the makefile.',
  28. ),
  29. 'examples' => array(
  30. 'drush make example.make example' => 'Build the example.make makefile in the example directory.',
  31. 'drush make --no-core --contrib-destination=. installprofile.make' => 'Build an installation profile within an existing Drupal site',
  32. 'drush make http://example.com/example.make example' => 'Build the remote example.make makefile in the example directory.',
  33. ),
  34. 'options' => array(
  35. 'version' => 'Print the make API version and exit.',
  36. 'concurrency' => array(
  37. 'description' => 'Set the number of concurrent projects that will be processed at the same time. The default is 1.',
  38. 'example-value' => '1',
  39. ),
  40. 'contrib-destination' => 'Specify a path under which modules and themes should be placed. Defaults to sites/all for Drupal 6,7 and the corresponding directory in the Drupal root for Drupal 8 and above.',
  41. 'force-complete' => 'Force a complete build even if errors occur.',
  42. 'ignore-checksums' => 'Ignore md5 checksums for downloads.',
  43. 'md5' => array(
  44. 'description' => 'Output an md5 hash of the current build after completion. Use --md5=print to print to stdout.',
  45. 'example-value' => 'print',
  46. 'value' => 'optional',
  47. ),
  48. 'make-update-default-url' => 'The default location to load the XML update information from.',
  49. 'no-cache' => 'Do not use the pm-download caching (defaults to cache enabled).',
  50. 'no-clean' => 'Leave temporary build directories in place instead of cleaning up after completion.',
  51. 'no-core' => 'Do not require a Drupal core project to be specified.',
  52. 'no-patch-txt' => 'Do not write a PATCHES.txt file in the directory of each patched project.',
  53. 'no-gitinfofile' => 'Do not modify .info files when cloning from Git.',
  54. 'prepare-install' => 'Prepare the built site for installation. Generate a properly permissioned settings.php and files directory.',
  55. 'tar' => 'Generate a tar archive of the build. The output filename will be [build path].tar.gz.',
  56. 'test' => 'Run a temporary test build and clean up.',
  57. 'translations' => 'Retrieve translations for the specified comma-separated list of language(s) if available for all projects.',
  58. 'working-copy' => 'Preserves VCS directories, like .git, for projects downloaded using such methods.',
  59. 'download-mechanism' => 'How to download files. Should be autodetected, but this is an override if it doesn\'t work. Options are "curl" and "make" (a native download method).',
  60. 'projects' => array(
  61. 'description' => 'Restrict the make to this comma-separated list of projects. To specify all projects, pass *.',
  62. 'example' => 'views,ctools',
  63. ),
  64. 'libraries' => array(
  65. 'description' => 'Restrict the make to this comma-separated list of libraries. To specify all libraries, pass *.',
  66. 'example' => 'tinymce',
  67. ),
  68. 'allow-override' => array(
  69. 'description' => 'Restrict the make options to this comma-separated list of options.',
  70. 'example' => 'all or none or working-copy or no-core, working-copy',
  71. ),
  72. ),
  73. 'engines' => array('release_info'),
  74. 'topics' => array('docs-make', 'docs-make-example'),
  75. );
  76. $items['make-generate'] = array(
  77. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
  78. 'description' => 'Generate a makefile from the current Drupal site.',
  79. 'examples' => array(
  80. 'drush generate-makefile example.make' => 'Generate a makefile with ALL projects versioned (should a project have a known version number)',
  81. 'drush generate-makefile example.make --exclude-versions' => 'Generate a makefile with NO projects versioned',
  82. 'drush generate-makefile example.make --exclude-versions=drupal,views,cck' => 'Generate a makefile with ALL projects versioned EXCEPT core, Views and CCK',
  83. 'drush generate-makefile example.make --include-versions=admin_menu,og,ctools (--exclude-versions)' => 'Generate a makefile with NO projects versioned EXCEPT Admin Menu, OG and CTools.',
  84. ),
  85. 'options' => array(
  86. 'exclude-versions' => 'Exclude all version numbers (default is include all version numbers) or optionally specify a list of projects to exclude from versioning',
  87. 'include-versions' => 'Include a specific list of projects, while all other projects remain unversioned in the makefile (so implies --exclude-versions)',
  88. ),
  89. 'engines' => array('release_info'),
  90. 'aliases' => array('generate-makefile'),
  91. );
  92. // Hidden command to build a group of projects.
  93. $items['make-process'] = array(
  94. 'hidden' => TRUE,
  95. 'arguments' => array(
  96. 'directory' => 'The temporary working directory to use',
  97. ),
  98. 'options' => array(
  99. 'projects' => 'An array of projects generated by make_projects()',
  100. 'manifest' => 'An array of projects already being processed',
  101. ),
  102. 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
  103. 'engines' => array('release_info'),
  104. );
  105. // Add docs topic.
  106. $docs_dir = drush_get_context('DOC_PREFIX', DRUSH_BASE_PATH);
  107. $items['docs-make'] = array(
  108. 'description' => 'Drush Make overview with examples',
  109. 'hidden' => TRUE,
  110. 'topic' => TRUE,
  111. 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
  112. 'callback' => 'drush_print_file',
  113. 'callback arguments' => array($docs_dir . '/docs/make.txt'),
  114. );
  115. $items['docs-make-example'] = array(
  116. 'description' => 'Drush Make example makefile',
  117. 'hidden' => TRUE,
  118. 'topic' => TRUE,
  119. 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
  120. 'callback' => 'drush_print_file',
  121. 'callback arguments' => array($docs_dir . '/examples/example.make'),
  122. );
  123. return $items;
  124. }
  125. /**
  126. * Implements hook_drush_help().
  127. */
  128. function make_drush_help($section) {
  129. switch ($section) {
  130. case 'drush:make':
  131. return 'Turns a makefile into a Drupal codebase. For a full description of options and makefile syntax, see docs/make.txt and examples/example.make.';
  132. case 'drush:make-generate':
  133. return 'Generate a makefile from the current Drupal site, specifying project version numbers unless not known or otherwise specified. Unversioned projects will be interpreted later by drush make as "most recent stable release"';
  134. }
  135. }
  136. /**
  137. * Command argument complete callback.
  138. *
  139. * @return array
  140. * Strong glob of files to complete on.
  141. */
  142. function make_make_complete() {
  143. return array(
  144. 'files' => array(
  145. 'directories' => array(
  146. 'pattern' => '*',
  147. 'flags' => GLOB_ONLYDIR,
  148. ),
  149. 'make' => array(
  150. 'pattern' => '*.make',
  151. ),
  152. ),
  153. );
  154. }
  155. /**
  156. * Drush callback; make based on the makefile.
  157. */
  158. function drush_make($makefile = NULL, $build_path = NULL) {
  159. // Set the cache option based on our '--no-cache' option.
  160. _make_enable_cache();
  161. // If --version option is supplied, print it and bail.
  162. if (drush_get_option('version', FALSE)) {
  163. drush_print(dt('Drush make API version !version', array('!version' => MAKE_API)));
  164. drush_print_pipe(MAKE_API);
  165. return;
  166. }
  167. if (!($build_path = make_build_path($build_path))) {
  168. return FALSE;
  169. }
  170. $info = make_parse_info_file($makefile);
  171. // Support making just a portion of a make file.
  172. $include_only = array(
  173. 'projects' => array_filter(drush_get_option_list('projects')),
  174. 'libraries' => array_filter(drush_get_option_list('libraries')),
  175. );
  176. $info = make_prune_info_file($info, $include_only);
  177. if ($info === FALSE || ($info = make_validate_info_file($info)) === FALSE) {
  178. return FALSE;
  179. }
  180. drush_log(dt('Beginning to build !makefile.', array('!makefile' => $makefile)), 'ok');
  181. $make_dir = realpath(dirname($makefile));
  182. $core_version = str_replace('.x', '', $info['core'][0]);
  183. $sitewide = drush_drupal_sitewide_directory($core_version);
  184. if (make_projects(FALSE, drush_get_option('contrib-destination', $sitewide), $info, $build_path, $make_dir)) {
  185. make_libraries(drush_get_option('contrib-destination', $sitewide), $info, $build_path, $make_dir);
  186. if (drush_get_option('prepare-install')) {
  187. make_prepare_install($build_path);
  188. }
  189. }
  190. return $info;
  191. }
  192. /**
  193. * Drush callback: hidden file to process an individual project.
  194. */
  195. function drush_make_process($directory) {
  196. // Set the temporary directory.
  197. make_tmp(TRUE, $directory);
  198. $projects = drush_get_option('projects', array());
  199. $manifest = drush_get_option('manifest', array());
  200. foreach ($projects as $project) {
  201. if ($instance = DrushMakeProject::getInstance($project['type'], $project)) {
  202. $instance->setManifest($manifest);
  203. $instance->make();
  204. }
  205. else {
  206. make_error('PROJECT-TYPE', dt('Non-existent project type %type on project %project.', array('%type' => $project['type'], '%project' => $project['name'])));
  207. }
  208. }
  209. }
  210. /**
  211. * Implements drush_hook_post_COMMAND() for the make command.
  212. */
  213. function drush_make_post_make($makefile = NULL, $build_path = NULL) {
  214. if (drush_get_option('version')) {
  215. return;
  216. }
  217. if (!($build_path = make_build_path($build_path))) {
  218. return;
  219. }
  220. if ($option = drush_get_option('md5')) {
  221. $md5 = make_md5();
  222. if ($option === 'print') {
  223. drush_print($md5);
  224. }
  225. else {
  226. drush_log(dt('Build hash: %md5', array('%md5' => $md5)), 'ok');
  227. }
  228. }
  229. // Only take final build steps if not in testing mode.
  230. if (!drush_get_option('test')) {
  231. if (drush_get_option('tar')) {
  232. make_tar($build_path);
  233. }
  234. else {
  235. make_move_build($build_path);
  236. }
  237. }
  238. make_clean_tmp();
  239. }
  240. /**
  241. * Process all projects specified in the make file.
  242. */
  243. function make_projects($recursion, $contrib_destination, $info, $build_path, $make_dir) {
  244. $projects = array();
  245. if (empty($info['projects'])) {
  246. if (drush_get_option('no-core') || $recursion) {
  247. return TRUE;
  248. }
  249. else {
  250. drush_set_error('MAKE_NO_CORE', dt('No core project specified.'));
  251. return FALSE;
  252. }
  253. }
  254. $ignore_checksums = drush_get_option('ignore-checksums');
  255. $translations = array();
  256. if (isset($info['translations'])) {
  257. $translations = $info['translations'];
  258. }
  259. if ($arg_translations = drush_get_option('translations', FALSE)) {
  260. $translations = array_merge(explode(',', $arg_translations), $translations);
  261. }
  262. foreach ($info['projects'] as $key => $project) {
  263. $md5 = '';
  264. if (isset($project['md5'])) {
  265. $md5 = $project['md5'];
  266. }
  267. // Merge the known data onto the project info.
  268. $project += array(
  269. 'name' => $key,
  270. 'core' => $info['core'],
  271. 'translations' => $translations,
  272. 'build_path' => $build_path,
  273. 'contrib_destination' => $contrib_destination,
  274. 'version' => '',
  275. 'location' => drush_get_option('make-update-default-url', RELEASE_INFO_DEFAULT_URL),
  276. 'subdir' => '',
  277. 'directory_name' => '',
  278. 'make_directory' => $make_dir,
  279. 'options' => array(),
  280. );
  281. // If download components are specified, but not the download
  282. // type, default to git.
  283. if (isset($project['download']) && !isset($project['download']['type'])) {
  284. $project['download']['type'] = 'git';
  285. }
  286. if (!isset($project['l10n_url']) && ($project['location'] == RELEASE_INFO_DEFAULT_URL)) {
  287. $project['l10n_url'] = MAKE_DEFAULT_L10N_SERVER;
  288. }
  289. // For convenience: define $request to be compatible with release_info
  290. // engine.
  291. // TODO: refactor to enforce 'make' to internally work with release_info
  292. // keys.
  293. $request = make_prepare_request($project);
  294. if ($project['location'] != RELEASE_INFO_DEFAULT_URL && !isset($project['type'])) {
  295. $project_type = release_info_check_project($request, 'core');
  296. $project['download_type'] = ($project_type ? 'core' : 'contrib');
  297. }
  298. elseif (!empty($project['type'])) {
  299. $project['download_type'] = ($project['type'] == 'core' ? 'core' : 'contrib');
  300. }
  301. else {
  302. $project['download_type'] = ($project['name'] == 'drupal' ? 'core' : 'contrib');
  303. }
  304. $projects[$project['download_type']][$project['name']] = $project;
  305. }
  306. $cores = !empty($projects['core']) ? count($projects['core']) : 0;
  307. if (drush_get_option('no-core')) {
  308. unset($projects['core']);
  309. }
  310. elseif ($cores == 0 && !$recursion) {
  311. drush_set_error('MAKE_NO_CORE', dt('No core project specified.'));
  312. return FALSE;
  313. }
  314. elseif ($cores == 1 && $recursion) {
  315. unset($projects['core']);
  316. }
  317. elseif ($cores > 1) {
  318. drush_set_error('MAKE_MULTIPLE_CORES', dt('More than one core project specified.'));
  319. return FALSE;
  320. }
  321. foreach ($projects as $type => $type_projects) {
  322. foreach ($type_projects as $project) {
  323. if (make_project_needs_release_info($project)) {
  324. // For convenience: define $request to be compatible with release_info
  325. // engine.
  326. // TODO: refactor to enforce 'make' to internally work with release_info
  327. // keys.
  328. $request = make_prepare_request($project, $type);
  329. $release = release_info_fetch($request, '', 'ignore');
  330. if ($release === FALSE) {
  331. return FALSE;
  332. }
  333. if (!isset($project['type'])) {
  334. // Translate release_info key for project_type to drush make.
  335. $project['type'] = $request['project_type'];
  336. }
  337. if (!isset($project['download'])) {
  338. $project['download'] = array(
  339. 'type' => 'pm',
  340. 'full_version' => $release['version'],
  341. 'download_link' => $release['download_link'],
  342. 'status url' => $request['status url'],
  343. );
  344. }
  345. }
  346. if (!empty($md5)) {
  347. $project['download']['md5'] = $md5;
  348. }
  349. if ($ignore_checksums) {
  350. unset($project['download']['md5']);
  351. }
  352. $projects[($project['type'] == 'core' ? 'core' : 'contrib')][$project['name']] = $project;
  353. }
  354. }
  355. // Core is built in place, rather than using make-process.
  356. if (isset($projects['core'])) {
  357. foreach ($projects['core'] as $project) {
  358. if ($instance = DrushMakeProject::getInstance($project['type'], $project)) {
  359. $project = $instance;
  360. }
  361. else {
  362. make_error('PROJECT-TYPE', dt('Non-existent project type %type on project %project.', array('%type' => $project['type'], '%project' => $project['name'])));
  363. }
  364. $project->make();
  365. }
  366. }
  367. // Process all projects concurrently using make-process.
  368. if (isset($projects['contrib'])) {
  369. $concurrency = drush_get_option('concurrency', 1);
  370. // Generate $concurrency sub-processes to do the actual work.
  371. $invocations = array();
  372. $thread = 0;
  373. foreach ($projects['contrib'] as $project) {
  374. $thread = ++$thread % $concurrency;
  375. // Ensure that we've set this sub-process up.
  376. if (!isset($invocations[$thread])) {
  377. $invocations[$thread] = array(
  378. 'args' => array(
  379. make_tmp(),
  380. ),
  381. 'options' => array(
  382. 'projects' => array(),
  383. ),
  384. 'site' => array(),
  385. );
  386. }
  387. // Add the project to this sub-process.
  388. $invocations[$thread]['options']['projects'][] = $project;
  389. // Add the manifest so recursive downloads do not override projects.
  390. $invocations[$thread]['options']['manifest'] = array_keys($projects['contrib']);
  391. }
  392. if (!empty($invocations)) {
  393. // Backend options.
  394. $backend_options = array(
  395. 'concurrency' => $concurrency,
  396. 'method' => 'POST',
  397. );
  398. $common_options = drush_redispatch_get_options();
  399. // Merge in stdin options since we process makefiles recursively. See http://drupal.org/node/1510180.
  400. $common_options = array_merge($common_options, drush_get_context('stdin'));
  401. // Package handler should use 'wget'.
  402. $common_options['package-handler'] = 'wget';
  403. // Avoid any prompts from CLI.
  404. $common_options['yes'] = TRUE;
  405. // Use cache unless explicitly turned off.
  406. if (!drush_get_option('no-cache', FALSE)) {
  407. $common_options['cache'] = TRUE;
  408. }
  409. // Unless --verbose or --debug are passed, quiter backend output.
  410. if (empty($common_options['verbose']) && empty($common_options['debug'])) {
  411. $backend_options['#output-label'] = FALSE;
  412. $backend_options['integrate'] = TRUE;
  413. }
  414. drush_backend_invoke_concurrent($invocations, $common_options, $backend_options, 'make-process', '@none');
  415. }
  416. }
  417. return TRUE;
  418. }
  419. /**
  420. * Process all libraries specified in the make file.
  421. */
  422. function make_libraries($contrib_destination, $info, $build_path, $make_dir) {
  423. if (empty($info['libraries'])) {
  424. return;
  425. }
  426. $ignore_checksums = drush_get_option('ignore-checksums');
  427. foreach ($info['libraries'] as $key => $library) {
  428. if (!is_string($key) || !is_array($library)) {
  429. // TODO Print a prettier message.
  430. continue;
  431. }
  432. // Merge the known data onto the library info.
  433. $library += array(
  434. 'name' => $key,
  435. 'core' => $info['core'],
  436. 'build_path' => $build_path,
  437. 'contrib_destination' => $contrib_destination,
  438. 'subdir' => '',
  439. 'directory_name' => $key,
  440. 'make_directory' => $make_dir,
  441. );
  442. if ($ignore_checksums) {
  443. unset($library['download']['md5']);
  444. }
  445. $class = DrushMakeProject::getInstance('library', $library);
  446. $class->make();
  447. }
  448. }
  449. /**
  450. * The path where the final build will be placed.
  451. */
  452. function make_build_path($build_path) {
  453. static $saved_path;
  454. if (isset($saved_path)) {
  455. return $saved_path;
  456. }
  457. // Determine the base of the build.
  458. if (drush_get_option('tar')) {
  459. $build_path = dirname($build_path) . '/' . basename($build_path, '.tar.gz') . '.tar.gz';
  460. }
  461. elseif (isset($build_path) && (!empty($build_path) || $build_path == '.')) {
  462. $build_path = rtrim($build_path, '/');
  463. }
  464. // Allow tests to run without a specified base path.
  465. elseif (drush_get_option('test') || drush_confirm(dt("Make new site in the current directory?"))) {
  466. $build_path = '.';
  467. }
  468. else {
  469. return drush_user_abort(dt('Build aborted.'));
  470. }
  471. if ($build_path != '.' && file_exists($build_path) && !drush_get_option('no-core', FALSE)) {
  472. return drush_set_error('MAKE_PATH_EXISTS', dt('Base path %path already exists.', array('%path' => $build_path)));
  473. }
  474. $saved_path = $build_path;
  475. return $build_path;
  476. }
  477. /**
  478. * Move the completed build into place.
  479. */
  480. function make_move_build($build_path) {
  481. $tmp_path = make_tmp();
  482. $ret = TRUE;
  483. if ($build_path == '.' || (drush_get_option('no-core', FALSE) && file_exists($build_path))) {
  484. $info = drush_scan_directory($tmp_path . DIRECTORY_SEPARATOR . '__build__', '/./', array('.', '..'), 0, FALSE, 'filename', 0, TRUE);
  485. foreach ($info as $file) {
  486. $destination = $build_path . DIRECTORY_SEPARATOR . $file->basename;
  487. if (file_exists($destination)) {
  488. // To prevent the removal of top-level directories such as 'modules' or
  489. // 'themes', descend in a level if the file exists.
  490. // TODO: This only protects one level of directories from being removed.
  491. $files = drush_scan_directory($file->filename, '/./', array('.', '..'), 0, FALSE);
  492. foreach ($files as $file) {
  493. $ret = $ret && drush_copy_dir($file->filename, $destination . DIRECTORY_SEPARATOR . $file->basename, FILE_EXISTS_MERGE);
  494. }
  495. }
  496. else {
  497. $ret = $ret && drush_copy_dir($file->filename, $destination);
  498. }
  499. }
  500. }
  501. else {
  502. drush_mkdir(dirname($build_path));
  503. $ret = drush_move_dir($tmp_path . DIRECTORY_SEPARATOR . '__build__', $tmp_path . DIRECTORY_SEPARATOR . basename($build_path), TRUE);
  504. $ret = $ret && drush_copy_dir($tmp_path . DIRECTORY_SEPARATOR . basename($build_path), $build_path);
  505. }
  506. // Copying to final destination resets write permissions. Re-apply.
  507. if (drush_get_option('prepare-install')) {
  508. $default = $build_path . '/sites/default';
  509. chmod($default . '/settings.php', 0666);
  510. chmod($default . '/files', 0777);
  511. }
  512. if (!$ret) {
  513. drush_set_error('MAKE_CANNOT_MOVE_BUILD', dt("Cannot move build into place."));
  514. }
  515. return $ret;
  516. }
  517. /**
  518. * Create a request array for use with release_info_fetch().
  519. *
  520. * @param array $project
  521. * Project array.
  522. * @param string $type
  523. * 'contrib' or 'core'.
  524. */
  525. function make_prepare_request($project, $type = 'contrib') {
  526. $request = array(
  527. 'name' => $project['name'],
  528. 'drupal_version' => $project['core'],
  529. 'status url' => $project['location'],
  530. );
  531. if ($project['version'] != '') {
  532. $request['project_version'] = $project['version'];
  533. $request['version'] = $type == 'core' ? $project['version'] : $project['core'] . '-' . $project['version'];
  534. }
  535. return $request;
  536. }
  537. /**
  538. * Determine if the release information is required for this
  539. * project. When it is determined that it is, this potentially results
  540. * in the use of pm-download to process the project.
  541. *
  542. * If the location of the project is not customized (uses d.o), and
  543. * one of the following is true, then release information is required:
  544. *
  545. * - $project['type'] has not been specified
  546. * - $project['download'] has not been specified
  547. *
  548. * @see make_projects()
  549. */
  550. function make_project_needs_release_info($project) {
  551. return isset($project['location'])
  552. // Only fetch release info if the project type is unknown OR if
  553. // download attributes are unspecified.
  554. && (!isset($project['type']) || !isset($project['download']));
  555. }
  556. /**
  557. * Enables caching if not explicitly disabled.
  558. *
  559. * @return bool
  560. * The previous value of the 'cache' option.
  561. */
  562. function _make_enable_cache() {
  563. $cache_before = drush_get_option('cache');
  564. if (!drush_get_option('no-cache', FALSE)) {
  565. drush_set_option('cache', TRUE);
  566. }
  567. return $cache_before;
  568. }