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_convert Command callback; convert ini makefile to YAML.
drush_make_pre_make Implements drush_hook_pre_COMMAND().
drush_make_process Drush callback: hidden file to process an individual project.
drush_make_validate Validation callback for make command.
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_libraries Gather additional data on all libraries specified in the make file.
make_prepare_projects Gather additional data on all projects specified in the make file.
make_prepare_request Create a request array suitable for release_info engine.
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.
_make_write_project_json Writes out project data to temporary files.

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. use Drush\Log\LogLevel;
  7. use Drush\UpdateService\ReleaseInfo;
  8. /**
  9. * Default localization server for downloading translations.
  10. */
  11. define('MAKE_DEFAULT_L10N_SERVER', 'http://ftp.drupal.org/files/translations/l10n_server.xml');
  12. /**
  13. * Make refuses to build makefiles whose api version is mismatched
  14. * with make command.
  15. */
  16. define('MAKE_API', 2);
  17. include_once 'make.utilities.inc';
  18. include_once 'make.download.inc';
  19. include_once 'make.project.inc';
  20. include_once 'generate.contents.make.inc';
  21. /**
  22. * Implements hook_drush_command().
  23. */
  24. function make_drush_command() {
  25. $projects = array(
  26. 'description' => 'Restrict the make to this comma-separated list of projects. To specify all projects, pass *.',
  27. 'example-value' => 'views,ctools',
  28. );
  29. $libraries = array(
  30. 'description' => 'Restrict the make to this comma-separated list of libraries. To specify all libraries, pass *.',
  31. 'example-value' => 'tinymce',
  32. );
  33. $items['make'] = array(
  34. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  35. 'description' => 'Turns a makefile into a working Drupal codebase.',
  36. 'arguments' => array(
  37. 'makefile' => 'Filename of the makefile to use for this build.',
  38. 'build path' => 'The path at which to build the makefile.',
  39. ),
  40. 'examples' => array(
  41. 'drush make example.make example' => 'Build the example.make makefile in the example directory.',
  42. 'drush make --no-core --contrib-destination=. installprofile.make' => 'Build an installation profile within an existing Drupal site',
  43. 'drush make http://example.com/example.make example' => 'Build the remote example.make makefile in the example directory.',
  44. 'drush make example.make --no-build --lock=example.lock' => 'Write a new makefile to example.lock. All project versions will be resolved.',
  45. ),
  46. 'options' => array(
  47. 'version' => 'Print the make API version and exit.',
  48. 'concurrency' => array(
  49. 'description' => 'Set the number of concurrent projects that will be processed at the same time. The default is 1.',
  50. 'example-value' => '1',
  51. ),
  52. '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.',
  53. 'force-complete' => 'Force a complete build even if errors occur.',
  54. 'ignore-checksums' => 'Ignore md5 checksums for downloads.',
  55. 'md5' => array(
  56. 'description' => 'Output an md5 hash of the current build after completion. Use --md5=print to print to stdout.',
  57. 'example-value' => 'print',
  58. 'value' => 'optional',
  59. ),
  60. 'make-update-default-url' => 'The default location to load the XML update information from.',
  61. 'no-build' => 'Do not build a codebase. Makes the `build path` argument optional.',
  62. 'no-cache' => 'Do not use the pm-download caching (defaults to cache enabled).',
  63. 'no-clean' => 'Leave temporary build directories in place instead of cleaning up after completion.',
  64. 'no-core' => 'Do not require a Drupal core project to be specified.',
  65. 'no-recursion' => 'Do not recurse into the makefiles of any downloaded projects; you can also set [do_recursion] = 0 on a per-project basis in the makefile.',
  66. 'no-patch-txt' => 'Do not write a PATCHES.txt file in the directory of each patched project.',
  67. 'no-gitinfofile' => 'Do not modify .info files when cloning from Git.',
  68. 'force-gitinfofile' => 'Force a modification of .info files when cloning from Git even if repository isn\'t hosted on Drupal.org.',
  69. 'no-gitprojectinfo' => 'Do not inject project info into .info files when cloning from Git.',
  70. 'overwrite' => 'Overwrite existing directories. Default is to merge.',
  71. 'prepare-install' => 'Prepare the built site for installation. Generate a properly permissioned settings.php and files directory.',
  72. 'tar' => 'Generate a tar archive of the build. The output filename will be [build path].tar.gz.',
  73. 'test' => 'Run a temporary test build and clean up.',
  74. 'translations' => 'Retrieve translations for the specified comma-separated list of language(s) if available for all projects.',
  75. 'working-copy' => 'Preserves VCS directories, like .git, for projects downloaded using such methods.',
  76. '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).',
  77. 'projects' => $projects,
  78. 'libraries' => $libraries,
  79. 'allow-override' => array(
  80. 'description' => 'Restrict the make options to a comma-separated list. Defaults to unrestricted.',
  81. ),
  82. 'lock' => array(
  83. 'description' => 'Generate a makefile, based on the one passed in, with all versions *resolved*. Defaults to printing to the terminal, but an output file may be provided.',
  84. 'example-value' => 'example.make.lock',
  85. ),
  86. 'shallow-clone' => array(
  87. 'description' => 'For makefile entries which use git for downloading, this option will utilize shallow clones where possible (ie. by using the git-clone\'s depth=1 option). If the "working-copy" option is not desired, this option will significantly speed up makes which involve modules stored in very large git repos. In fact, if "working-copy" option is enabled, this option cannot be used.',
  88. ),
  89. ),
  90. 'engines' => array('release_info'),
  91. 'topics' => array('docs-make', 'docs-make-example'),
  92. );
  93. $items['make-generate'] = array(
  94. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
  95. 'description' => 'Generate a makefile from the current Drupal site.',
  96. 'examples' => array(
  97. 'drush generate-makefile example.make' => 'Generate a makefile with ALL projects versioned (should a project have a known version number)',
  98. 'drush generate-makefile example.make --exclude-versions' => 'Generate a makefile with NO projects versioned',
  99. 'drush generate-makefile example.make --exclude-versions=drupal,views,cck' => 'Generate a makefile with ALL projects versioned EXCEPT core, Views and CCK',
  100. '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.',
  101. ),
  102. 'options' => array(
  103. 'exclude-versions' => 'Exclude all version numbers (default is include all version numbers) or optionally specify a list of projects to exclude from versioning',
  104. 'include-versions' => 'Include a specific list of projects, while all other projects remain unversioned in the makefile (so implies --exclude-versions)',
  105. ),
  106. 'engines' => array('release_info'),
  107. 'aliases' => array('generate-makefile'),
  108. );
  109. $items['make-convert'] = array(
  110. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  111. 'description' => 'Convert a legacy makefile into YAML format.',
  112. 'arguments' => array(
  113. 'makefile' => 'Filename of the makefile to convert.',
  114. ),
  115. 'options' => array(
  116. 'projects' => $projects,
  117. 'libraries' => $libraries,
  118. ),
  119. 'required-arguments' => TRUE,
  120. 'examples' => array(
  121. 'drush make-convert example.make' => 'Convert example.make to example.make.yml',
  122. ),
  123. );
  124. // Hidden command to build a group of projects.
  125. $items['make-process'] = array(
  126. 'hidden' => TRUE,
  127. 'arguments' => array(
  128. 'directory' => 'The temporary working directory to use',
  129. ),
  130. 'options' => array(
  131. 'projects-location' => 'Name of a temporary file containing json-encoded output of make_projects().',
  132. 'manifest' => 'An array of projects already being processed.',
  133. ),
  134. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  135. 'engines' => array('release_info'),
  136. );
  137. $items['make-update'] = array(
  138. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  139. 'description' => 'Process a makefile and outputs an equivalent makefile with projects version resolved to latest available.',
  140. 'arguments' => array(
  141. 'makefile' => 'Filename of the makefile to use for this build.',
  142. ),
  143. 'options' => array(
  144. 'result-file' => array(
  145. 'description' => 'Save to a file. If not provided, the updated makefile will be dump to stdout.',
  146. 'example-value' => 'updated.make',
  147. ),
  148. ),
  149. 'engines' => array('release_info', 'update_status'),
  150. );
  151. // Add docs topic.
  152. $docs_dir = drush_get_context('DOC_PREFIX', DRUSH_BASE_PATH);
  153. $items['docs-make'] = array(
  154. 'description' => 'Drush Make overview with examples',
  155. 'hidden' => TRUE,
  156. 'topic' => TRUE,
  157. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  158. 'callback' => 'drush_print_file',
  159. 'callback arguments' => array($docs_dir . '/docs/make.md'),
  160. );
  161. $items['docs-make-example'] = array(
  162. 'description' => 'Drush Make example makefile',
  163. 'hidden' => TRUE,
  164. 'topic' => TRUE,
  165. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  166. 'callback' => 'drush_print_file',
  167. 'callback arguments' => array($docs_dir . '/examples/example.make.yml'),
  168. );
  169. return $items;
  170. }
  171. /**
  172. * Implements hook_drush_help().
  173. */
  174. function make_drush_help($section) {
  175. switch ($section) {
  176. case 'drush:make':
  177. 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.';
  178. case 'drush:make-generate':
  179. 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"';
  180. }
  181. }
  182. /**
  183. * Command argument complete callback.
  184. *
  185. * @return array
  186. * Strong glob of files to complete on.
  187. */
  188. function make_make_complete() {
  189. return array(
  190. 'files' => array(
  191. 'directories' => array(
  192. 'pattern' => '*',
  193. 'flags' => GLOB_ONLYDIR,
  194. ),
  195. 'make' => array(
  196. 'pattern' => '*.make',
  197. ),
  198. ),
  199. );
  200. }
  201. /**
  202. * Validation callback for make command.
  203. */
  204. function drush_make_validate($makefile = NULL, $build_path = NULL) {
  205. // Don't validate if --version option is supplied.
  206. if (drush_get_option('version', FALSE)) {
  207. return;
  208. }
  209. if (drush_get_option('shallow-clone', FALSE) && drush_get_option('working-copy', FALSE)) {
  210. return drush_set_error('MAKE_SHALLOW_CLONE_WORKING_COPY_CONFLICT', dt('You cannot use "--shallow-clone" and "--working-copy" options together.'));
  211. }
  212. // Error out if the build path is not valid and --no-build was not supplied.
  213. if (!drush_get_option('no-build', FALSE) && !make_build_path($build_path)) {
  214. return FALSE;
  215. }
  216. }
  217. /**
  218. * Implements drush_hook_pre_COMMAND().
  219. *
  220. * If --version option is supplied, print it and prevent execution of the command.
  221. */
  222. function drush_make_pre_make($makefile = NULL, $build_path = NULL) {
  223. if (drush_get_option('version', FALSE)) {
  224. drush_print(dt('Drush make API version !version', array('!version' => MAKE_API)));
  225. drush_print_pipe(MAKE_API);
  226. // Prevent command execution.
  227. return FALSE;
  228. }
  229. }
  230. /**
  231. * Drush callback; make based on the makefile.
  232. */
  233. function drush_make($makefile = NULL, $build_path = NULL) {
  234. // Set the cache option based on our '--no-cache' option.
  235. _make_enable_cache();
  236. // Build.
  237. if (!drush_get_option('no-build', FALSE)) {
  238. $info = make_parse_info_file($makefile);
  239. drush_log(dt('Beginning to build !makefile.', array('!makefile' => $makefile)), LogLevel::OK);
  240. // Default contrib destination depends on Drupal core version.
  241. $core_version = str_replace('.x', '', $info['core'][0]);
  242. $sitewide = drush_drupal_sitewide_directory($core_version);
  243. $contrib_destination = drush_get_option('contrib-destination', $sitewide);
  244. $build_path = make_build_path($build_path);
  245. $make_dir = realpath(dirname($makefile));
  246. $success = make_projects(FALSE, $contrib_destination, $info, $build_path, $make_dir);
  247. if ($success) {
  248. make_libraries(FALSE, $contrib_destination, $info, $build_path, $make_dir);
  249. if (drush_get_option('prepare-install')) {
  250. make_prepare_install($build_path);
  251. }
  252. if ($option = drush_get_option('md5')) {
  253. $md5 = make_md5();
  254. if ($option === 'print') {
  255. drush_print($md5);
  256. }
  257. else {
  258. drush_log(dt('Build hash: %md5', array('%md5' => $md5)), LogLevel::OK);
  259. }
  260. }
  261. // Only take final build steps if not in testing mode.
  262. if (!drush_get_option('test')) {
  263. if (drush_get_option('tar')) {
  264. make_tar($build_path);
  265. }
  266. else {
  267. make_move_build($build_path);
  268. }
  269. }
  270. make_clean_tmp();
  271. }
  272. }
  273. // Process --lock.
  274. if (drush_get_option('lock', FALSE)) {
  275. return make_generate_from_makefile(drush_get_option('lock'), $makefile);
  276. }
  277. // Used by core-quick-drupal command.
  278. // @see drush_core_quick_drupal().
  279. return $info;
  280. }
  281. /**
  282. * Command callback; convert ini makefile to YAML.
  283. */
  284. function drush_make_convert($makefile) {
  285. drush_log(dt('Beginning to convert !makefile.', array('!makefile' => $makefile)), LogLevel::OK);
  286. $file = $makefile . '.yml';
  287. $info = make_parse_info_file($makefile);
  288. // Remove incorrect value.
  289. unset($info['format']);
  290. $dumper = drush_load_engine('outputformat', 'yaml');
  291. $yaml = $dumper->format($info, array());
  292. if (file_put_contents($file, $yaml)) {
  293. drush_log(dt("Wrote make file @file", array('@file' => $file)), LogLevel::OK);
  294. return $file;
  295. }
  296. else {
  297. make_error('FILE_ERROR', dt("Unable to write file !file", array('!file' => $file)));
  298. }
  299. }
  300. /**
  301. * Drush callback: hidden file to process an individual project.
  302. *
  303. * @param string $directory
  304. * Directory where the project is being built.
  305. */
  306. function drush_make_process($directory) {
  307. drush_get_engine('release_info');
  308. // Set the temporary directory.
  309. make_tmp(TRUE, $directory);
  310. if (!$projects_location = drush_get_option('projects-location')) {
  311. return drush_set_error('MAKE-PROCESS', dt('No projects passed to drush_make_process'));
  312. }
  313. $projects = json_decode(file_get_contents($projects_location), TRUE);
  314. $manifest = drush_get_option('manifest', array());
  315. foreach ($projects as $project) {
  316. if ($instance = DrushMakeProject::getInstance($project['type'], $project)) {
  317. $instance->setManifest($manifest);
  318. $instance->make();
  319. }
  320. else {
  321. make_error('PROJECT-TYPE', dt('Non-existent project type %type on project %project.', array('%type' => $project['type'], '%project' => $project['name'])));
  322. }
  323. }
  324. }
  325. /**
  326. * Gather additional data on all projects specified in the make file.
  327. */
  328. function make_prepare_projects($recursion, $info, $contrib_destination = '', $build_path = '', $make_dir = '') {
  329. $release_info = drush_get_engine('release_info');
  330. // Nothing to make if the project list is empty. Maybe complain about it.
  331. if (empty($info['projects'])) {
  332. if (drush_get_option('no-core') || $recursion) {
  333. return TRUE;
  334. }
  335. else {
  336. return drush_set_error('MAKE_NO_CORE', dt('No core project specified.'));
  337. }
  338. }
  339. // Obtain translations to download along with the projects.
  340. $translations = array();
  341. if (isset($info['translations'])) {
  342. $translations = $info['translations'];
  343. }
  344. if ($arg_translations = drush_get_option('translations', FALSE)) {
  345. $translations = array_merge(explode(',', $arg_translations), $translations);
  346. }
  347. // Normalize projects.
  348. $projects = array();
  349. $ignore_checksums = drush_get_option('ignore-checksums');
  350. foreach ($info['projects'] as $key => $project) {
  351. // Merge the known data onto the project info.
  352. $project += array(
  353. 'name' => $key,
  354. 'type' => 'module',
  355. 'core' => $info['core'],
  356. 'translations' => $translations,
  357. 'build_path' => $build_path,
  358. 'contrib_destination' => $contrib_destination,
  359. 'version' => '',
  360. 'location' => drush_get_option('make-update-default-url', ReleaseInfo::DEFAULT_URL),
  361. 'subdir' => '',
  362. 'directory_name' => '',
  363. 'make_directory' => $make_dir,
  364. 'options' => array(),
  365. );
  366. // MD5 Checksum.
  367. if ($ignore_checksums) {
  368. unset($project['download']['md5']);
  369. }
  370. elseif (!empty($project['md5'])) {
  371. $project['download']['md5'] = $project['md5'];
  372. }
  373. // If download components are specified, but not the download
  374. // type, default to git.
  375. if (isset($project['download']) && !isset($project['download']['type'])) {
  376. $project['download']['type'] = 'git';
  377. }
  378. // Localization server.
  379. if (!isset($project['l10n_url']) && ($project['location'] == ReleaseInfo::DEFAULT_URL)) {
  380. $project['l10n_url'] = MAKE_DEFAULT_L10N_SERVER;
  381. }
  382. // Classify projects in core or contrib.
  383. if ($project['type'] == 'core') {
  384. $project['download_type'] = 'core';
  385. }
  386. elseif ($project['location'] != ReleaseInfo::DEFAULT_URL || !isset($project['download'])) {
  387. $request = make_prepare_request($project);
  388. $is_core = $release_info->checkProject($request, 'core');
  389. $project['download_type'] = ($is_core ? 'core' : 'contrib');
  390. $project['type'] = $is_core ? 'core' : $project['type'];
  391. }
  392. else {
  393. $project['download_type'] = ($project['name'] == 'drupal' ? 'core' : 'contrib');
  394. }
  395. $projects[$project['download_type']][$project['name']] = $project;
  396. }
  397. // Verify there're enough cores, but not too many.
  398. $cores = !empty($projects['core']) ? count($projects['core']) : 0;
  399. if (drush_get_option('no-core')) {
  400. unset($projects['core']);
  401. }
  402. elseif ($cores == 0 && !$recursion) {
  403. return drush_set_error('MAKE_NO_CORE', dt('No core project specified.'));
  404. }
  405. elseif ($cores == 1 && $recursion) {
  406. unset($projects['core']);
  407. }
  408. elseif ($cores > 1) {
  409. return drush_set_error('MAKE_MULTIPLE_CORES', dt('More than one core project specified.'));
  410. }
  411. // Set download type = pm for suitable projects.
  412. foreach (array_keys($projects) as $project_type) {
  413. foreach ($projects[$project_type] as $project) {
  414. if (make_project_needs_release_info($project)) {
  415. $request = make_prepare_request($project, $project_type);
  416. $release = $release_info->selectReleaseBasedOnStrategy($request, '', 'ignore');
  417. if ($release === FALSE) {
  418. return FALSE;
  419. }
  420. // Override default project type with data from update service.
  421. if (!isset($info['projects'][$project['name']]['type'])) {
  422. $project['type'] = $release_info->get($request)->getType();
  423. }
  424. if (!isset($project['download'])) {
  425. $project['download'] = array(
  426. 'type' => 'pm',
  427. 'full_version' => $release['version'],
  428. 'download_link' => $release['download_link'],
  429. 'status url' => $request['status url'],
  430. );
  431. }
  432. }
  433. $projects[$project_type][$project['name']] = $project;
  434. }
  435. }
  436. if (!$recursion) {
  437. $projects += array(
  438. 'core' => array(),
  439. 'contrib' => array(),
  440. );
  441. drush_set_option('DRUSH_MAKE_PROJECTS', array_merge($projects['core'], $projects['contrib']));
  442. }
  443. return $projects;
  444. }
  445. /**
  446. * Process all projects specified in the make file.
  447. */
  448. function make_projects($recursion, $contrib_destination, $info, $build_path, $make_dir) {
  449. $projects = make_prepare_projects($recursion, $info, $contrib_destination, $build_path, $make_dir);
  450. // Abort if there was an error processing projects.
  451. if ($projects === FALSE) {
  452. return FALSE;
  453. }
  454. // Core is built in place, rather than using make-process.
  455. if (!empty($projects['core']) && count($projects['core'])) {
  456. $project = current($projects['core']);
  457. $project = DrushMakeProject::getInstance('core', $project);
  458. $project->make();
  459. }
  460. // Process all projects concurrently using make-process.
  461. if (isset($projects['contrib'])) {
  462. $concurrency = drush_get_option('concurrency', 1);
  463. // Generate $concurrency sub-processes to do the actual work.
  464. $invocations = array();
  465. $thread = 0;
  466. foreach ($projects['contrib'] as $project) {
  467. $thread = ++$thread % $concurrency;
  468. // Ensure that we've set this sub-process up.
  469. if (!isset($invocations[$thread])) {
  470. $invocations[$thread] = array(
  471. 'args' => array(
  472. make_tmp(),
  473. ),
  474. 'options' => array(
  475. 'projects' => array(),
  476. ),
  477. 'site' => array(),
  478. );
  479. }
  480. // Add the project to this sub-process.
  481. $invocations[$thread]['options']['projects'][] = $project;
  482. // Add the manifest so recursive downloads do not override projects.
  483. $invocations[$thread]['options']['manifest'] = array_keys($projects['contrib']);
  484. }
  485. if (!empty($invocations)) {
  486. // Backend options.
  487. $backend_options = array(
  488. 'concurrency' => $concurrency,
  489. 'method' => 'POST',
  490. );
  491. // Store projects in temporary files since passing this much data on the
  492. // pipe buffer can break on certain systems.
  493. _make_write_project_json($invocations);
  494. $common_options = drush_redispatch_get_options();
  495. // Merge in stdin options since we process makefiles recursively. See http://drupal.org/node/1510180.
  496. $common_options = array_merge($common_options, drush_get_context('stdin'));
  497. // Package handler should use 'wget'.
  498. $common_options['package-handler'] = 'wget';
  499. // Avoid any prompts from CLI.
  500. $common_options['yes'] = TRUE;
  501. // Use cache unless explicitly turned off.
  502. if (!drush_get_option('no-cache', FALSE)) {
  503. $common_options['cache'] = TRUE;
  504. }
  505. // Unless --verbose or --debug are passed, quiter backend output.
  506. if (empty($common_options['verbose']) && empty($common_options['debug'])) {
  507. $backend_options['#output-label'] = FALSE;
  508. $backend_options['integrate'] = TRUE;
  509. }
  510. drush_backend_invoke_concurrent($invocations, $common_options, $backend_options, 'make-process', '@none');
  511. }
  512. }
  513. return TRUE;
  514. }
  515. /**
  516. * Writes out project data to temporary files.
  517. *
  518. * @param array &$invocations
  519. * An array containing projects sorted by thread.
  520. */
  521. function _make_write_project_json(array &$invocations) {
  522. foreach ($invocations as $thread => $info) {
  523. $projects = $info['options']['projects'];
  524. unset($invocations[$thread]['options']['projects']);
  525. $temp_file = drush_tempnam('make_projects');
  526. file_put_contents($temp_file, json_encode($projects));
  527. $invocations[$thread]['options']['projects-location'] = $temp_file;
  528. }
  529. }
  530. /**
  531. * Gather additional data on all libraries specified in the make file.
  532. */
  533. function make_prepare_libraries($recursion, $info, $contrib_destination = '', $build_path = '', $make_dir = '') {
  534. // Nothing to make if the libraries list is empty.
  535. if (empty($info['libraries'])) {
  536. return;
  537. }
  538. $libraries = array();
  539. $ignore_checksums = drush_get_option('ignore-checksums');
  540. foreach ($info['libraries'] as $key => $library) {
  541. if (!is_string($key) || !is_array($library)) {
  542. // TODO Print a prettier message.
  543. continue;
  544. }
  545. // Merge the known data onto the library info.
  546. $library += array(
  547. 'name' => $key,
  548. 'core' => $info['core'],
  549. 'build_path' => $build_path,
  550. 'contrib_destination' => $contrib_destination,
  551. 'subdir' => '',
  552. 'directory_name' => $key,
  553. 'make_directory' => $make_dir,
  554. );
  555. if ($ignore_checksums) {
  556. unset($library['download']['md5']);
  557. }
  558. $libraries[$key] = $library;
  559. }
  560. if (!$recursion) {
  561. drush_set_option('DRUSH_MAKE_LIBRARIES', $info['libraries']);
  562. }
  563. return $libraries;
  564. }
  565. /**
  566. * Process all libraries specified in the make file.
  567. */
  568. function make_libraries($recursion, $contrib_destination, $info, $build_path, $make_dir) {
  569. $libraries = make_prepare_libraries($recursion, $info, $contrib_destination, $build_path, $make_dir);
  570. if (empty($libraries)) {
  571. return;
  572. }
  573. foreach ($libraries as $key => $library) {
  574. $class = DrushMakeProject::getInstance('library', $library);
  575. $class->make();
  576. }
  577. }
  578. /**
  579. * The path where the final build will be placed.
  580. */
  581. function make_build_path($build_path) {
  582. static $saved_path;
  583. if (isset($saved_path)) {
  584. return $saved_path;
  585. }
  586. // Determine the base of the build.
  587. if (drush_get_option('tar')) {
  588. $build_path = dirname($build_path) . '/' . basename($build_path, '.tar.gz') . '.tar.gz';
  589. }
  590. elseif (isset($build_path) && (!empty($build_path) || $build_path == '.')) {
  591. $build_path = rtrim($build_path, '/');
  592. }
  593. // Allow tests to run without a specified base path.
  594. elseif (drush_get_option('test') || drush_confirm(dt("Make new site in the current directory?"))) {
  595. $build_path = '.';
  596. }
  597. else {
  598. return drush_user_abort(dt('Build aborted.'));
  599. }
  600. if ($build_path != '.' && file_exists($build_path) && !drush_get_option('no-core', FALSE)) {
  601. return drush_set_error('MAKE_PATH_EXISTS', dt('Base path %path already exists.', array('%path' => $build_path)));
  602. }
  603. $saved_path = $build_path;
  604. return $build_path;
  605. }
  606. /**
  607. * Move the completed build into place.
  608. */
  609. function make_move_build($build_path) {
  610. $tmp_path = make_tmp();
  611. $ret = TRUE;
  612. if ($build_path == '.' || (drush_get_option('no-core', FALSE) && file_exists($build_path))) {
  613. $info = drush_scan_directory($tmp_path . DIRECTORY_SEPARATOR . '__build__', '/./', array('.', '..'), 0, FALSE, 'filename', 0, TRUE);
  614. foreach ($info as $file) {
  615. $destination = $build_path . DIRECTORY_SEPARATOR . $file->basename;
  616. if (file_exists($destination)) {
  617. // To prevent the removal of top-level directories such as 'modules' or
  618. // 'themes', descend in a level if the file exists.
  619. // TODO: This only protects one level of directories from being removed.
  620. $files = drush_scan_directory($file->filename, '/./', array('.', '..'), 0, FALSE);
  621. $overwrite = drush_get_option('overwrite', FALSE) ? FILE_EXISTS_OVERWRITE : FILE_EXISTS_MERGE;
  622. foreach ($files as $file) {
  623. $ret = $ret && drush_copy_dir($file->filename, $destination . DIRECTORY_SEPARATOR . $file->basename, $overwrite);
  624. }
  625. }
  626. else {
  627. $ret = $ret && drush_copy_dir($file->filename, $destination);
  628. }
  629. }
  630. }
  631. else {
  632. drush_mkdir(dirname($build_path));
  633. $ret = drush_move_dir($tmp_path . DIRECTORY_SEPARATOR . '__build__', $tmp_path . DIRECTORY_SEPARATOR . basename($build_path), TRUE);
  634. $ret = $ret && drush_copy_dir($tmp_path . DIRECTORY_SEPARATOR . basename($build_path), $build_path);
  635. }
  636. // Copying to final destination resets write permissions. Re-apply.
  637. if (drush_get_option('prepare-install')) {
  638. $default = $build_path . '/sites/default';
  639. chmod($default . '/settings.php', 0666);
  640. chmod($default . '/files', 0777);
  641. }
  642. if (!$ret) {
  643. return drush_set_error('MAKE_CANNOT_MOVE_BUILD', dt("Cannot move build into place."));
  644. }
  645. return $ret;
  646. }
  647. /**
  648. * Create a request array suitable for release_info engine.
  649. *
  650. * This is a convenience function to easily integrate drush_make
  651. * with drush release_info engine.
  652. *
  653. * @todo: refactor 'make' to internally work with release_info keys.
  654. *
  655. * @param array $project
  656. * Project array.
  657. * @param string $type
  658. * 'contrib' or 'core'.
  659. */
  660. function make_prepare_request($project, $type = 'contrib') {
  661. $request = array(
  662. 'name' => $project['name'],
  663. 'drupal_version' => $project['core'],
  664. 'status url' => $project['location'],
  665. );
  666. if ($project['version'] != '') {
  667. $request['project_version'] = $project['version'];
  668. $request['version'] = $type == 'core' ? $project['version'] : $project['core'] . '-' . $project['version'];
  669. }
  670. return $request;
  671. }
  672. /**
  673. * Determine if the release information is required for this
  674. * project. When it is determined that it is, this potentially results
  675. * in the use of pm-download to process the project.
  676. *
  677. * If the location of the project is not customized (uses d.o), and
  678. * one of the following is true, then release information is required:
  679. *
  680. * - $project['type'] has not been specified
  681. * - $project['download'] has not been specified
  682. *
  683. * @see make_projects()
  684. */
  685. function make_project_needs_release_info($project) {
  686. return isset($project['location'])
  687. // Only fetch release info if the project type is unknown OR if
  688. // download attributes are unspecified.
  689. && (!isset($project['type']) || !isset($project['download']));
  690. }
  691. /**
  692. * Enables caching if not explicitly disabled.
  693. *
  694. * @return bool
  695. * The previous value of the 'cache' option.
  696. */
  697. function _make_enable_cache() {
  698. $cache_before = drush_get_option('cache');
  699. if (!drush_get_option('no-cache', FALSE)) {
  700. drush_set_option('cache', TRUE);
  701. }
  702. return $cache_before;
  703. }